gnt-job 8.09 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/python
#

# Copyright (C) 2006, 2007 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


import sys
import os
import itertools
25
import time
Iustin Pop's avatar
Iustin Pop committed
26
27
28
29
30
31
32
33
34
35
36
from optparse import make_option
from cStringIO import StringIO

from ganeti.cli import *
from ganeti import opcodes
from ganeti import logger
from ganeti import constants
from ganeti import utils
from ganeti import errors


37
_LIST_DEF_FIELDS = ["id", "status", "summary"]
38

39
40
41
42
43
44
45
46
_USER_JOB_STATUS = {
  constants.JOB_STATUS_QUEUED: "queued",
  constants.JOB_STATUS_RUNNING: "running",
  constants.JOB_STATUS_CANCELED: "canceled",
  constants.JOB_STATUS_SUCCESS: "success",
  constants.JOB_STATUS_ERROR: "error",
  }

47

Iustin Pop's avatar
Iustin Pop committed
48
49
50
51
52
def ListJobs(opts, args):
  """List the jobs

  """
  if opts.output is None:
53
54
55
    selected_fields = _LIST_DEF_FIELDS
  elif opts.output.startswith("+"):
    selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
Iustin Pop's avatar
Iustin Pop committed
56
57
58
  else:
    selected_fields = opts.output.split(",")

59
  output = GetClient().QueryJobs(None, selected_fields)
Iustin Pop's avatar
Iustin Pop committed
60
  if not opts.no_headers:
61
    # TODO: Implement more fields
Iustin Pop's avatar
Iustin Pop committed
62
63
64
    headers = {
      "id": "ID",
      "status": "Status",
65
66
67
      "ops": "OpCodes",
      "opresult": "OpCode_result",
      "opstatus": "OpCode_status",
68
      "oplog": "OpCode_log",
69
      "summary": "Summary",
70
71
72
73
74
      "opstart": "OpCode_start",
      "opend": "OpCode_end",
      "start_ts": "Start",
      "end_ts": "End",
      "received_ts": "Received",
Iustin Pop's avatar
Iustin Pop committed
75
76
77
78
79
80
      }
  else:
    headers = None

  # we don't have yet unitfields here
  unitfields = None
81
  numfields = None
Iustin Pop's avatar
Iustin Pop committed
82
83
84
85
86
87

  # change raw values to nicer strings
  for row in output:
    for idx, field in enumerate(selected_fields):
      val = row[idx]
      if field == "status":
88
89
        if val in _USER_JOB_STATUS:
          val = _USER_JOB_STATUS[val]
Iustin Pop's avatar
Iustin Pop committed
90
91
        else:
          raise errors.ProgrammerError("Unknown job status code '%s'" % val)
92
93
      elif field == "summary":
        val = ",".join(val)
94
95
96
97
      elif field in ("start_ts", "end_ts", "received_ts"):
        val = FormatTimestamp(val)
      elif field in ("opstart", "opend"):
        val = [FormatTimestamp(entry) for entry in val]
Iustin Pop's avatar
Iustin Pop committed
98
99
100
101
102
103
104
105
106
107
108
109

      row[idx] = str(val)

  data = GenerateTable(separator=opts.separator, headers=headers,
                       fields=selected_fields, unitfields=unitfields,
                       numfields=numfields, data=output)
  for line in data:
    print line

  return 0


110
111
112
113
114
115
116
117
118
def ArchiveJobs(opts, args):
  client = GetClient()

  for job_id in args:
    client.ArchiveJob(job_id)

  return 0


119
120
121
122
123
124
125
126
127
def CancelJobs(opts, args):
  client = GetClient()

  for job_id in args:
    client.CancelJob(job_id)

  return 0


Iustin Pop's avatar
Iustin Pop committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def ShowJobs(opts, args):
  """List the jobs

  """
  def format(level, text):
    """Display the text indented."""
    print "%s%s" % ("  " * level, text)

  def result_helper(value):
    """Format a result field in a nice way."""
    if isinstance(value, (tuple, list)):
      return "[%s]" % (", ".join(str(elem) for elem in value))
    else:
      return str(value)

143
144
145
146
  selected_fields = [
    "id", "status", "ops", "opresult", "opstatus", "oplog",
    "opstart", "opend", "received_ts", "start_ts", "end_ts",
    ]
Iustin Pop's avatar
Iustin Pop committed
147
148
149
150
151

  result = GetClient().QueryJobs(args, selected_fields)

  first = True

152
  for idx, entry in enumerate(result):
Iustin Pop's avatar
Iustin Pop committed
153
154
155
156
    if not first:
      format(0, "")
    else:
      first = False
157
158

    if entry is None:
159
160
161
162
163
164
      if idx <= len(args):
        format(0, "Job ID %s not found" % args[idx])
      else:
        # this should not happen, when we don't pass args it will be a
        # valid job returned
        format(0, "Job ID requested as argument %s not found" % (idx + 1))
165
166
167
168
      continue

    (job_id, status, ops, opresult, opstatus, oplog,
     opstart, opend, recv_ts, start_ts, end_ts) = entry
Iustin Pop's avatar
Iustin Pop committed
169
170
171
172
173
174
175
    format(0, "Job ID: %s" % job_id)
    if status in _USER_JOB_STATUS:
      status = _USER_JOB_STATUS[status]
    else:
      raise errors.ProgrammerError("Unknown job status code '%s'" % val)

    format(1, "Status: %s" % status)
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

    if recv_ts is not None:
      format(1, "Received:         %s" % FormatTimestamp(recv_ts))
    else:
      format(1, "Missing received timestamp (%s)" % str(recv_ts))

    if start_ts is not None:
      if recv_ts is not None:
        d1 = start_ts[0] - recv_ts[0] + (start_ts[1] - recv_ts[1]) / 1000000.0
        delta = " (delta %.6fs)" % d1
      else:
        delta = ""
      format(1, "Processing start: %s%s" % (FormatTimestamp(start_ts), delta))
    else:
      format(1, "Processing start: unknown (%s)" % str(start_ts))

    if end_ts is not None:
      if start_ts is not None:
        d2 = end_ts[0] - start_ts[0] + (end_ts[1] - start_ts[1]) / 1000000.0
        delta = " (delta %.6fs)" % d2
      else:
        delta = ""
      format(1, "Processing end:   %s%s" % (FormatTimestamp(end_ts), delta))
    else:
      format(1, "Processing end:   unknown (%s)" % str(end_ts))

    if end_ts is not None and recv_ts is not None:
      d3 = end_ts[0] - recv_ts[0] + (end_ts[1] - recv_ts[1]) / 1000000.0
      format(1, "Total processing time: %.6f seconds" % d3)
    else:
      format(1, "Total processing time: N/A")
Iustin Pop's avatar
Iustin Pop committed
207
    format(1, "Opcodes:")
208
209
    for (opcode, result, status, log, s_ts, e_ts) in \
            zip(ops, opresult, opstatus, oplog, opstart, opend):
Iustin Pop's avatar
Iustin Pop committed
210
211
      format(2, "%s" % opcode["OP_ID"])
      format(3, "Status: %s" % status)
212
213
214
215
216
217
218
219
      if isinstance(s_ts, (tuple, list)):
        format(3, "Processing start: %s" % FormatTimestamp(s_ts))
      else:
        format(3, "No processing start time")
      if isinstance(e_ts, (tuple, list)):
        format(3, "Processing end:   %s" % FormatTimestamp(e_ts))
      else:
        format(3, "No processing end time")
Iustin Pop's avatar
Iustin Pop committed
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
      format(3, "Input fields:")
      for key, val in opcode.iteritems():
        if key == "OP_ID":
          continue
        if isinstance(val, (tuple, list)):
          val = ",".join(val)
        format(4, "%s: %s" % (key, val))
      if result is None:
        format(3, "No output data")
      elif isinstance(result, (tuple, list)):
        if not result:
          format(3, "Result: empty sequence")
        else:
          format(3, "Result:")
          for elem in result:
            format(4, result_helper(elem))
      elif isinstance(result, dict):
        if not result:
          format(3, "Result: empty dictionary")
        else:
          for key, val in result.iteritems():
            format(4, "%s: %s" % (key, result_helper(val)))
      else:
        format(3, "Result: %s" % result)
244
      format(3, "Execution log:")
245
246
      for serial, log_ts, log_type, log_msg in log:
        time_txt = FormatTimestamp(log_ts)
247
248
        encoded = str(log_msg).encode('string_escape')
        format(4, "%s:%s:%s %s" % (serial, time_txt, log_type, encoded))
Iustin Pop's avatar
Iustin Pop committed
249
250
251
  return 0


Iustin Pop's avatar
Iustin Pop committed
252
253
commands = {
  'list': (ListJobs, ARGS_NONE,
254
255
            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
            "", "List the jobs and their status. The available fields are"
256
257
           " (see the man page for details): id, status, op_list,"
           " op_status, op_result."
Iustin Pop's avatar
Iustin Pop committed
258
           " The default field"
259
260
261
262
263
           " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS)),
  'archive': (ArchiveJobs, ARGS_ANY,
              [DEBUG_OPT],
              "<job-id> [<job-id> ...]",
              "Archive specified jobs"),
264
265
266
267
  'cancel': (CancelJobs, ARGS_ANY,
             [DEBUG_OPT],
             "<job-id> [<job-id> ...]",
             "Cancel specified jobs"),
Iustin Pop's avatar
Iustin Pop committed
268
269
270
  'info': (ShowJobs, ARGS_ANY, [DEBUG_OPT],
           "<job-id> [<job-id> ...]",
           "Show detailed information about the specified jobs"),
Iustin Pop's avatar
Iustin Pop committed
271
272
273
274
275
  }


if __name__ == '__main__':
  sys.exit(GenericMain(commands))