gnt-job 8.47 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
_USER_JOB_STATUS = {
  constants.JOB_STATUS_QUEUED: "queued",
Iustin Pop's avatar
Iustin Pop committed
41
  constants.JOB_STATUS_WAITLOCK: "waiting",
42
43
44
45
46
47
  constants.JOB_STATUS_RUNNING: "running",
  constants.JOB_STATUS_CANCELED: "canceled",
  constants.JOB_STATUS_SUCCESS: "success",
  constants.JOB_STATUS_ERROR: "error",
  }

48

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

  """
  if opts.output is None:
54
55
56
    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
57
58
59
  else:
    selected_fields = opts.output.split(",")

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

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

  # change raw values to nicer strings
  for row in output:
    for idx, field in enumerate(selected_fields):
      val = row[idx]
      if field == "status":
89
90
        if val in _USER_JOB_STATUS:
          val = _USER_JOB_STATUS[val]
Iustin Pop's avatar
Iustin Pop committed
91
92
        else:
          raise errors.ProgrammerError("Unknown job status code '%s'" % val)
93
94
      elif field == "summary":
        val = ",".join(val)
95
96
97
98
      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
99
100
101
102
103
104
105
106
107
108
109
110

      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


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

  for job_id in args:
    client.ArchiveJob(job_id)

  return 0


Iustin Pop's avatar
Iustin Pop committed
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def AutoArchiveJobs(opts, args):
  client = GetClient()

  age = args[0]

  if age == 'all':
    age = -1
  else:
    age = ParseTimespec(age)

  client.AutoArchiveJobs(age)
  return 0


134
135
136
137
138
139
140
141
142
def CancelJobs(opts, args):
  client = GetClient()

  for job_id in args:
    client.CancelJob(job_id)

  return 0


Iustin Pop's avatar
Iustin Pop committed
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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)

158
159
160
161
  selected_fields = [
    "id", "status", "ops", "opresult", "opstatus", "oplog",
    "opstart", "opend", "received_ts", "start_ts", "end_ts",
    ]
Iustin Pop's avatar
Iustin Pop committed
162
163
164
165
166

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

  first = True

167
  for idx, entry in enumerate(result):
Iustin Pop's avatar
Iustin Pop committed
168
169
170
171
    if not first:
      format(0, "")
    else:
      first = False
172
173

    if entry is None:
174
175
176
177
178
179
      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))
180
181
182
183
      continue

    (job_id, status, ops, opresult, opstatus, oplog,
     opstart, opend, recv_ts, start_ts, end_ts) = entry
Iustin Pop's avatar
Iustin Pop committed
184
185
186
187
188
189
190
    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)
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

    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
222
    format(1, "Opcodes:")
223
224
    for (opcode, result, status, log, s_ts, e_ts) in \
            zip(ops, opresult, opstatus, oplog, opstart, opend):
Iustin Pop's avatar
Iustin Pop committed
225
226
      format(2, "%s" % opcode["OP_ID"])
      format(3, "Status: %s" % status)
227
228
229
230
231
232
233
234
      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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
      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)
259
      format(3, "Execution log:")
260
261
      for serial, log_ts, log_type, log_msg in log:
        time_txt = FormatTimestamp(log_ts)
262
263
        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
264
265
266
  return 0


Iustin Pop's avatar
Iustin Pop committed
267
268
commands = {
  'list': (ListJobs, ARGS_NONE,
269
270
            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
            "", "List the jobs and their status. The available fields are"
271
272
           " (see the man page for details): id, status, op_list,"
           " op_status, op_result."
Iustin Pop's avatar
Iustin Pop committed
273
           " The default field"
274
275
276
277
278
           " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS)),
  'archive': (ArchiveJobs, ARGS_ANY,
              [DEBUG_OPT],
              "<job-id> [<job-id> ...]",
              "Archive specified jobs"),
Iustin Pop's avatar
Iustin Pop committed
279
280
281
282
  'autoarchive': (AutoArchiveJobs, ARGS_ONE,
              [DEBUG_OPT],
              "<age>",
              "Auto archive jobs older than the given age"),
283
284
285
286
  'cancel': (CancelJobs, ARGS_ANY,
             [DEBUG_OPT],
             "<job-id> [<job-id> ...]",
             "Cancel specified jobs"),
Iustin Pop's avatar
Iustin Pop committed
287
288
289
  'info': (ShowJobs, ARGS_ANY, [DEBUG_OPT],
           "<job-id> [<job-id> ...]",
           "Show detailed information about the specified jobs"),
Iustin Pop's avatar
Iustin Pop committed
290
291
292
293
294
  }


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