Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_job.py @ 8a3c9e8a

History | View | Annotate | Download (14.5 kB)

1 a09b9e3d Michael Hanselmann
#
2 7a1ecaed Iustin Pop
#
3 7a1ecaed Iustin Pop
4 76b62028 Iustin Pop
# Copyright (C) 2006, 2007, 2012 Google Inc.
5 7a1ecaed Iustin Pop
#
6 7a1ecaed Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 7a1ecaed Iustin Pop
# it under the terms of the GNU General Public License as published by
8 7a1ecaed Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 7a1ecaed Iustin Pop
# (at your option) any later version.
10 7a1ecaed Iustin Pop
#
11 7a1ecaed Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 7a1ecaed Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 7a1ecaed Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 7a1ecaed Iustin Pop
# General Public License for more details.
15 7a1ecaed Iustin Pop
#
16 7a1ecaed Iustin Pop
# You should have received a copy of the GNU General Public License
17 7a1ecaed Iustin Pop
# along with this program; if not, write to the Free Software
18 7a1ecaed Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 7a1ecaed Iustin Pop
# 02110-1301, USA.
20 7a1ecaed Iustin Pop
21 7260cfbe Iustin Pop
"""Job related commands"""
22 7a1ecaed Iustin Pop
23 b459a848 Andrea Spadaccini
# pylint: disable=W0401,W0613,W0614,C0103
24 2f79bd34 Iustin Pop
# W0401: Wildcard import ganeti.cli
25 2d54e29c Iustin Pop
# W0613: Unused argument, since all functions follow the same API
26 2f79bd34 Iustin Pop
# W0614: Unused import %s from wildcard import (since we need cli)
27 7260cfbe Iustin Pop
# C0103: Invalid name gnt-job
28 2f79bd34 Iustin Pop
29 7a1ecaed Iustin Pop
from ganeti.cli import *
30 7a1ecaed Iustin Pop
from ganeti import constants
31 7a1ecaed Iustin Pop
from ganeti import errors
32 26f15862 Iustin Pop
from ganeti import utils
33 e7d6946c Michael Hanselmann
from ganeti import cli
34 3086220e Michael Hanselmann
from ganeti import qlang
35 7a1ecaed Iustin Pop
36 7a1ecaed Iustin Pop
37 917b4e56 Iustin Pop
#: default list of fields for L{ListJobs}
38 60dd1473 Iustin Pop
_LIST_DEF_FIELDS = ["id", "status", "summary"]
39 7a5d3bbd Iustin Pop
40 917b4e56 Iustin Pop
#: map converting the job status contants to user-visible
41 917b4e56 Iustin Pop
#: names
42 af30b2fd Michael Hanselmann
_USER_JOB_STATUS = {
43 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_QUEUED: "queued",
44 47099cd1 Michael Hanselmann
  constants.JOB_STATUS_WAITING: "waiting",
45 fbf0262f Michael Hanselmann
  constants.JOB_STATUS_CANCELING: "canceling",
46 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_RUNNING: "running",
47 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_CANCELED: "canceled",
48 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_SUCCESS: "success",
49 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_ERROR: "error",
50 af30b2fd Michael Hanselmann
  }
51 af30b2fd Michael Hanselmann
52 0ad64cf8 Michael Hanselmann
53 3086220e Michael Hanselmann
def _FormatStatus(value):
54 3086220e Michael Hanselmann
  """Formats a job status.
55 3086220e Michael Hanselmann

56 3086220e Michael Hanselmann
  """
57 3086220e Michael Hanselmann
  try:
58 3086220e Michael Hanselmann
    return _USER_JOB_STATUS[value]
59 3086220e Michael Hanselmann
  except KeyError:
60 3086220e Michael Hanselmann
    raise errors.ProgrammerError("Unknown job status code '%s'" % value)
61 3086220e Michael Hanselmann
62 3086220e Michael Hanselmann
63 e1c701e7 Michael Hanselmann
_JOB_LIST_FORMAT = {
64 e1c701e7 Michael Hanselmann
  "status": (_FormatStatus, False),
65 e1c701e7 Michael Hanselmann
  "summary": (lambda value: ",".join(str(item) for item in value), False),
66 e1c701e7 Michael Hanselmann
  }
67 e1c701e7 Michael Hanselmann
_JOB_LIST_FORMAT.update(dict.fromkeys(["opstart", "opexec", "opend"],
68 e1c701e7 Michael Hanselmann
                                      (lambda value: map(FormatTimestamp,
69 e1c701e7 Michael Hanselmann
                                                         value),
70 e1c701e7 Michael Hanselmann
                                       None)))
71 e1c701e7 Michael Hanselmann
72 e1c701e7 Michael Hanselmann
73 76b62028 Iustin Pop
def _ParseJobIds(args):
74 76b62028 Iustin Pop
  """Parses a list of string job IDs into integers.
75 76b62028 Iustin Pop

76 76b62028 Iustin Pop
  @param args: list of strings
77 76b62028 Iustin Pop
  @return: list of integers
78 76b62028 Iustin Pop
  @raise OpPrereqError: in case of invalid values
79 76b62028 Iustin Pop

80 76b62028 Iustin Pop
  """
81 76b62028 Iustin Pop
  try:
82 76b62028 Iustin Pop
    return [int(a) for a in args]
83 76b62028 Iustin Pop
  except (ValueError, TypeError), err:
84 76b62028 Iustin Pop
    raise errors.OpPrereqError("Invalid job ID passed: %s" % err,
85 76b62028 Iustin Pop
                               errors.ECODE_INVAL)
86 76b62028 Iustin Pop
87 76b62028 Iustin Pop
88 7a1ecaed Iustin Pop
def ListJobs(opts, args):
89 7a1ecaed Iustin Pop
  """List the jobs
90 7a1ecaed Iustin Pop

91 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
92 917b4e56 Iustin Pop
  @type args: list
93 917b4e56 Iustin Pop
  @param args: should be an empty list
94 917b4e56 Iustin Pop
  @rtype: int
95 917b4e56 Iustin Pop
  @return: the desired exit code
96 917b4e56 Iustin Pop

97 7a1ecaed Iustin Pop
  """
98 a4ebd726 Michael Hanselmann
  selected_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
99 7a1ecaed Iustin Pop
100 43d51ef2 Michael Hanselmann
  if opts.archived and "archived" not in selected_fields:
101 43d51ef2 Michael Hanselmann
    selected_fields.append("archived")
102 43d51ef2 Michael Hanselmann
103 f037e9d7 Michael Hanselmann
  qfilter = qlang.MakeSimpleFilter("status", opts.status_filter)
104 f037e9d7 Michael Hanselmann
105 3086220e Michael Hanselmann
  return GenericList(constants.QR_JOB, selected_fields, args, None,
106 3086220e Michael Hanselmann
                     opts.separator, not opts.no_headers,
107 e1c701e7 Michael Hanselmann
                     format_override=_JOB_LIST_FORMAT, verbose=opts.verbose,
108 f037e9d7 Michael Hanselmann
                     force_filter=opts.force_filter, namefield="id",
109 76b62028 Iustin Pop
                     qfilter=qfilter, isnumeric=True)
110 b8802cc4 Michael Hanselmann
111 dcbd6288 Guido Trotter
112 3086220e Michael Hanselmann
def ListJobFields(opts, args):
113 3086220e Michael Hanselmann
  """List job fields.
114 7a1ecaed Iustin Pop

115 3086220e Michael Hanselmann
  @param opts: the command line options selected by the user
116 3086220e Michael Hanselmann
  @type args: list
117 3086220e Michael Hanselmann
  @param args: fields to list, or empty for all
118 3086220e Michael Hanselmann
  @rtype: int
119 3086220e Michael Hanselmann
  @return: the desired exit code
120 3086220e Michael Hanselmann

121 3086220e Michael Hanselmann
  """
122 3086220e Michael Hanselmann
  return GenericListFields(constants.QR_JOB, args, opts.separator,
123 3086220e Michael Hanselmann
                           not opts.no_headers)
124 7a1ecaed Iustin Pop
125 7a1ecaed Iustin Pop
126 0ad64cf8 Michael Hanselmann
def ArchiveJobs(opts, args):
127 917b4e56 Iustin Pop
  """Archive jobs.
128 917b4e56 Iustin Pop

129 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
130 917b4e56 Iustin Pop
  @type args: list
131 917b4e56 Iustin Pop
  @param args: should contain the job IDs to be archived
132 917b4e56 Iustin Pop
  @rtype: int
133 917b4e56 Iustin Pop
  @return: the desired exit code
134 917b4e56 Iustin Pop

135 917b4e56 Iustin Pop
  """
136 0ad64cf8 Michael Hanselmann
  client = GetClient()
137 0ad64cf8 Michael Hanselmann
138 aa9f8167 Iustin Pop
  rcode = 0
139 0ad64cf8 Michael Hanselmann
  for job_id in args:
140 aa9f8167 Iustin Pop
    if not client.ArchiveJob(job_id):
141 aa9f8167 Iustin Pop
      ToStderr("Failed to archive job with ID '%s'", job_id)
142 aa9f8167 Iustin Pop
      rcode = 1
143 0ad64cf8 Michael Hanselmann
144 aa9f8167 Iustin Pop
  return rcode
145 0ad64cf8 Michael Hanselmann
146 0ad64cf8 Michael Hanselmann
147 07cd723a Iustin Pop
def AutoArchiveJobs(opts, args):
148 917b4e56 Iustin Pop
  """Archive jobs based on age.
149 917b4e56 Iustin Pop

150 917b4e56 Iustin Pop
  This will archive jobs based on their age, or all jobs if a 'all' is
151 917b4e56 Iustin Pop
  passed.
152 917b4e56 Iustin Pop

153 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
154 917b4e56 Iustin Pop
  @type args: list
155 917b4e56 Iustin Pop
  @param args: should contain only one element, the age as a time spec
156 c41eea6e Iustin Pop
      that can be parsed by L{ganeti.cli.ParseTimespec} or the
157 c41eea6e Iustin Pop
      keyword I{all}, which will cause all jobs to be archived
158 917b4e56 Iustin Pop
  @rtype: int
159 917b4e56 Iustin Pop
  @return: the desired exit code
160 917b4e56 Iustin Pop

161 917b4e56 Iustin Pop
  """
162 07cd723a Iustin Pop
  client = GetClient()
163 07cd723a Iustin Pop
164 07cd723a Iustin Pop
  age = args[0]
165 07cd723a Iustin Pop
166 d0c8c01d Iustin Pop
  if age == "all":
167 07cd723a Iustin Pop
    age = -1
168 07cd723a Iustin Pop
  else:
169 07cd723a Iustin Pop
    age = ParseTimespec(age)
170 07cd723a Iustin Pop
171 f8ad5591 Michael Hanselmann
  (archived_count, jobs_left) = client.AutoArchiveJobs(age)
172 f8ad5591 Michael Hanselmann
  ToStdout("Archived %s jobs, %s unchecked left", archived_count, jobs_left)
173 f8ad5591 Michael Hanselmann
174 07cd723a Iustin Pop
  return 0
175 07cd723a Iustin Pop
176 07cd723a Iustin Pop
177 e1c701e7 Michael Hanselmann
def CancelJobs(opts, args, cl=None, _stdout_fn=ToStdout, _ask_fn=AskUser):
178 917b4e56 Iustin Pop
  """Cancel not-yet-started jobs.
179 917b4e56 Iustin Pop

180 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
181 917b4e56 Iustin Pop
  @type args: list
182 917b4e56 Iustin Pop
  @param args: should contain the job IDs to be cancelled
183 917b4e56 Iustin Pop
  @rtype: int
184 917b4e56 Iustin Pop
  @return: the desired exit code
185 917b4e56 Iustin Pop

186 917b4e56 Iustin Pop
  """
187 e1c701e7 Michael Hanselmann
  if cl is None:
188 e1c701e7 Michael Hanselmann
    cl = GetClient()
189 e1c701e7 Michael Hanselmann
190 06fef5e0 Michael Hanselmann
  result = constants.EXIT_SUCCESS
191 d2b92ffc Michael Hanselmann
192 e1c701e7 Michael Hanselmann
  if bool(args) ^ (opts.status_filter is None):
193 e1c701e7 Michael Hanselmann
    raise errors.OpPrereqError("Either a status filter or job ID(s) must be"
194 e1c701e7 Michael Hanselmann
                               " specified and never both", errors.ECODE_INVAL)
195 e1c701e7 Michael Hanselmann
196 e1c701e7 Michael Hanselmann
  if opts.status_filter is not None:
197 e1c701e7 Michael Hanselmann
    response = cl.Query(constants.QR_JOB, ["id", "status", "summary"],
198 e1c701e7 Michael Hanselmann
                        qlang.MakeSimpleFilter("status", opts.status_filter))
199 e1c701e7 Michael Hanselmann
200 e1c701e7 Michael Hanselmann
    jobs = [i for ((_, i), _, _) in response.data]
201 e1c701e7 Michael Hanselmann
    if not jobs:
202 e1c701e7 Michael Hanselmann
      raise errors.OpPrereqError("No jobs with the requested status have been"
203 e1c701e7 Michael Hanselmann
                                 " found", errors.ECODE_STATE)
204 e1c701e7 Michael Hanselmann
205 e1c701e7 Michael Hanselmann
    if not opts.force:
206 e1c701e7 Michael Hanselmann
      (_, table) = FormatQueryResult(response, header=True,
207 e1c701e7 Michael Hanselmann
                                     format_override=_JOB_LIST_FORMAT)
208 e1c701e7 Michael Hanselmann
      for line in table:
209 e1c701e7 Michael Hanselmann
        _stdout_fn(line)
210 e1c701e7 Michael Hanselmann
211 e1c701e7 Michael Hanselmann
      if not _ask_fn("Cancel job(s) listed above?"):
212 e1c701e7 Michael Hanselmann
        return constants.EXIT_CONFIRMATION
213 e1c701e7 Michael Hanselmann
  else:
214 e1c701e7 Michael Hanselmann
    jobs = args
215 e1c701e7 Michael Hanselmann
216 e1c701e7 Michael Hanselmann
  for job_id in jobs:
217 e1c701e7 Michael Hanselmann
    (success, msg) = cl.CancelJob(job_id)
218 06fef5e0 Michael Hanselmann
219 06fef5e0 Michael Hanselmann
    if not success:
220 06fef5e0 Michael Hanselmann
      result = constants.EXIT_FAILURE
221 06fef5e0 Michael Hanselmann
222 e1c701e7 Michael Hanselmann
    _stdout_fn(msg)
223 d2b92ffc Michael Hanselmann
224 06fef5e0 Michael Hanselmann
  return result
225 d2b92ffc Michael Hanselmann
226 d2b92ffc Michael Hanselmann
227 191712c0 Iustin Pop
def ShowJobs(opts, args):
228 917b4e56 Iustin Pop
  """Show detailed information about jobs.
229 917b4e56 Iustin Pop

230 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
231 917b4e56 Iustin Pop
  @type args: list
232 917b4e56 Iustin Pop
  @param args: should contain the job IDs to be queried
233 917b4e56 Iustin Pop
  @rtype: int
234 917b4e56 Iustin Pop
  @return: the desired exit code
235 191712c0 Iustin Pop

236 191712c0 Iustin Pop
  """
237 c04bc777 Iustin Pop
  def format_msg(level, text):
238 191712c0 Iustin Pop
    """Display the text indented."""
239 3a24c527 Iustin Pop
    ToStdout("%s%s", "  " * level, text)
240 191712c0 Iustin Pop
241 191712c0 Iustin Pop
  def result_helper(value):
242 191712c0 Iustin Pop
    """Format a result field in a nice way."""
243 191712c0 Iustin Pop
    if isinstance(value, (tuple, list)):
244 1f864b60 Iustin Pop
      return "[%s]" % utils.CommaJoin(value)
245 191712c0 Iustin Pop
    else:
246 191712c0 Iustin Pop
      return str(value)
247 191712c0 Iustin Pop
248 aad81f98 Iustin Pop
  selected_fields = [
249 aad81f98 Iustin Pop
    "id", "status", "ops", "opresult", "opstatus", "oplog",
250 b9b5abcb Iustin Pop
    "opstart", "opexec", "opend", "received_ts", "start_ts", "end_ts",
251 aad81f98 Iustin Pop
    ]
252 191712c0 Iustin Pop
253 76b62028 Iustin Pop
  qfilter = qlang.MakeSimpleFilter("id", _ParseJobIds(args))
254 76b62028 Iustin Pop
  result = GetClient().Query(constants.QR_JOB, selected_fields, qfilter).data
255 191712c0 Iustin Pop
256 191712c0 Iustin Pop
  first = True
257 191712c0 Iustin Pop
258 eba1aaad Michael Hanselmann
  for entry in result:
259 191712c0 Iustin Pop
    if not first:
260 c04bc777 Iustin Pop
      format_msg(0, "")
261 191712c0 Iustin Pop
    else:
262 191712c0 Iustin Pop
      first = False
263 aad81f98 Iustin Pop
264 eba1aaad Michael Hanselmann
    ((_, job_id), (rs_status, status), (_, ops), (_, opresult), (_, opstatus),
265 eba1aaad Michael Hanselmann
     (_, oplog), (_, opstart), (_, opexec), (_, opend), (_, recv_ts),
266 eba1aaad Michael Hanselmann
     (_, start_ts), (_, end_ts)) = entry
267 eba1aaad Michael Hanselmann
268 eba1aaad Michael Hanselmann
    # Detect non-normal results
269 eba1aaad Michael Hanselmann
    if rs_status != constants.RS_NORMAL:
270 eba1aaad Michael Hanselmann
      format_msg(0, "Job ID %s not found" % job_id)
271 aad81f98 Iustin Pop
      continue
272 aad81f98 Iustin Pop
273 c04bc777 Iustin Pop
    format_msg(0, "Job ID: %s" % job_id)
274 191712c0 Iustin Pop
    if status in _USER_JOB_STATUS:
275 191712c0 Iustin Pop
      status = _USER_JOB_STATUS[status]
276 191712c0 Iustin Pop
    else:
277 2f79bd34 Iustin Pop
      raise errors.ProgrammerError("Unknown job status code '%s'" % status)
278 191712c0 Iustin Pop
279 c04bc777 Iustin Pop
    format_msg(1, "Status: %s" % status)
280 aad81f98 Iustin Pop
281 aad81f98 Iustin Pop
    if recv_ts is not None:
282 c04bc777 Iustin Pop
      format_msg(1, "Received:         %s" % FormatTimestamp(recv_ts))
283 aad81f98 Iustin Pop
    else:
284 c04bc777 Iustin Pop
      format_msg(1, "Missing received timestamp (%s)" % str(recv_ts))
285 aad81f98 Iustin Pop
286 aad81f98 Iustin Pop
    if start_ts is not None:
287 aad81f98 Iustin Pop
      if recv_ts is not None:
288 aad81f98 Iustin Pop
        d1 = start_ts[0] - recv_ts[0] + (start_ts[1] - recv_ts[1]) / 1000000.0
289 aad81f98 Iustin Pop
        delta = " (delta %.6fs)" % d1
290 aad81f98 Iustin Pop
      else:
291 aad81f98 Iustin Pop
        delta = ""
292 c04bc777 Iustin Pop
      format_msg(1, "Processing start: %s%s" %
293 c04bc777 Iustin Pop
                 (FormatTimestamp(start_ts), delta))
294 aad81f98 Iustin Pop
    else:
295 c04bc777 Iustin Pop
      format_msg(1, "Processing start: unknown (%s)" % str(start_ts))
296 aad81f98 Iustin Pop
297 aad81f98 Iustin Pop
    if end_ts is not None:
298 aad81f98 Iustin Pop
      if start_ts is not None:
299 aad81f98 Iustin Pop
        d2 = end_ts[0] - start_ts[0] + (end_ts[1] - start_ts[1]) / 1000000.0
300 aad81f98 Iustin Pop
        delta = " (delta %.6fs)" % d2
301 aad81f98 Iustin Pop
      else:
302 aad81f98 Iustin Pop
        delta = ""
303 c04bc777 Iustin Pop
      format_msg(1, "Processing end:   %s%s" %
304 c04bc777 Iustin Pop
                 (FormatTimestamp(end_ts), delta))
305 aad81f98 Iustin Pop
    else:
306 c04bc777 Iustin Pop
      format_msg(1, "Processing end:   unknown (%s)" % str(end_ts))
307 aad81f98 Iustin Pop
308 aad81f98 Iustin Pop
    if end_ts is not None and recv_ts is not None:
309 aad81f98 Iustin Pop
      d3 = end_ts[0] - recv_ts[0] + (end_ts[1] - recv_ts[1]) / 1000000.0
310 c04bc777 Iustin Pop
      format_msg(1, "Total processing time: %.6f seconds" % d3)
311 aad81f98 Iustin Pop
    else:
312 c04bc777 Iustin Pop
      format_msg(1, "Total processing time: N/A")
313 c04bc777 Iustin Pop
    format_msg(1, "Opcodes:")
314 b9b5abcb Iustin Pop
    for (opcode, result, status, log, s_ts, x_ts, e_ts) in \
315 b9b5abcb Iustin Pop
            zip(ops, opresult, opstatus, oplog, opstart, opexec, opend):
316 c04bc777 Iustin Pop
      format_msg(2, "%s" % opcode["OP_ID"])
317 c04bc777 Iustin Pop
      format_msg(3, "Status: %s" % status)
318 aad81f98 Iustin Pop
      if isinstance(s_ts, (tuple, list)):
319 c04bc777 Iustin Pop
        format_msg(3, "Processing start: %s" % FormatTimestamp(s_ts))
320 aad81f98 Iustin Pop
      else:
321 c04bc777 Iustin Pop
        format_msg(3, "No processing start time")
322 b9b5abcb Iustin Pop
      if isinstance(x_ts, (tuple, list)):
323 c04bc777 Iustin Pop
        format_msg(3, "Execution start:  %s" % FormatTimestamp(x_ts))
324 b9b5abcb Iustin Pop
      else:
325 c04bc777 Iustin Pop
        format_msg(3, "No execution start time")
326 aad81f98 Iustin Pop
      if isinstance(e_ts, (tuple, list)):
327 c04bc777 Iustin Pop
        format_msg(3, "Processing end:   %s" % FormatTimestamp(e_ts))
328 aad81f98 Iustin Pop
      else:
329 c04bc777 Iustin Pop
        format_msg(3, "No processing end time")
330 c04bc777 Iustin Pop
      format_msg(3, "Input fields:")
331 598b5255 Michael Hanselmann
      for key in utils.NiceSort(opcode.keys()):
332 191712c0 Iustin Pop
        if key == "OP_ID":
333 191712c0 Iustin Pop
          continue
334 598b5255 Michael Hanselmann
        val = opcode[key]
335 191712c0 Iustin Pop
        if isinstance(val, (tuple, list)):
336 08db7c5c Iustin Pop
          val = ",".join([str(item) for item in val])
337 c04bc777 Iustin Pop
        format_msg(4, "%s: %s" % (key, val))
338 191712c0 Iustin Pop
      if result is None:
339 c04bc777 Iustin Pop
        format_msg(3, "No output data")
340 191712c0 Iustin Pop
      elif isinstance(result, (tuple, list)):
341 191712c0 Iustin Pop
        if not result:
342 c04bc777 Iustin Pop
          format_msg(3, "Result: empty sequence")
343 191712c0 Iustin Pop
        else:
344 c04bc777 Iustin Pop
          format_msg(3, "Result:")
345 191712c0 Iustin Pop
          for elem in result:
346 c04bc777 Iustin Pop
            format_msg(4, result_helper(elem))
347 191712c0 Iustin Pop
      elif isinstance(result, dict):
348 191712c0 Iustin Pop
        if not result:
349 c04bc777 Iustin Pop
          format_msg(3, "Result: empty dictionary")
350 191712c0 Iustin Pop
        else:
351 d1b47b16 Michael Hanselmann
          format_msg(3, "Result:")
352 191712c0 Iustin Pop
          for key, val in result.iteritems():
353 c04bc777 Iustin Pop
            format_msg(4, "%s: %s" % (key, result_helper(val)))
354 191712c0 Iustin Pop
      else:
355 c04bc777 Iustin Pop
        format_msg(3, "Result: %s" % result)
356 c04bc777 Iustin Pop
      format_msg(3, "Execution log:")
357 3386e7a9 Iustin Pop
      for serial, log_ts, log_type, log_msg in log:
358 3386e7a9 Iustin Pop
        time_txt = FormatTimestamp(log_ts)
359 8a7f1c61 Michael Hanselmann
        encoded = FormatLogMessage(log_type, log_msg)
360 c04bc777 Iustin Pop
        format_msg(4, "%s:%s:%s %s" % (serial, time_txt, log_type, encoded))
361 191712c0 Iustin Pop
  return 0
362 191712c0 Iustin Pop
363 191712c0 Iustin Pop
364 e7d6946c Michael Hanselmann
def WatchJob(opts, args):
365 e7d6946c Michael Hanselmann
  """Follow a job and print its output as it arrives.
366 e7d6946c Michael Hanselmann

367 e7d6946c Michael Hanselmann
  @param opts: the command line options selected by the user
368 e7d6946c Michael Hanselmann
  @type args: list
369 e7d6946c Michael Hanselmann
  @param args: Contains the job ID
370 e7d6946c Michael Hanselmann
  @rtype: int
371 e7d6946c Michael Hanselmann
  @return: the desired exit code
372 e7d6946c Michael Hanselmann

373 e7d6946c Michael Hanselmann
  """
374 e7d6946c Michael Hanselmann
  job_id = args[0]
375 e7d6946c Michael Hanselmann
376 e7d6946c Michael Hanselmann
  msg = ("Output from job %s follows" % job_id)
377 e7d6946c Michael Hanselmann
  ToStdout(msg)
378 e7d6946c Michael Hanselmann
  ToStdout("-" * len(msg))
379 e7d6946c Michael Hanselmann
380 e7d6946c Michael Hanselmann
  retcode = 0
381 e7d6946c Michael Hanselmann
  try:
382 e7d6946c Michael Hanselmann
    cli.PollJob(job_id)
383 e7d6946c Michael Hanselmann
  except errors.GenericError, err:
384 e7d6946c Michael Hanselmann
    (retcode, job_result) = cli.FormatError(err)
385 e7d6946c Michael Hanselmann
    ToStderr("Job %s failed: %s", job_id, job_result)
386 e7d6946c Michael Hanselmann
387 e7d6946c Michael Hanselmann
  return retcode
388 e7d6946c Michael Hanselmann
389 e7d6946c Michael Hanselmann
390 f037e9d7 Michael Hanselmann
_PENDING_OPT = \
391 f037e9d7 Michael Hanselmann
  cli_option("--pending", default=None,
392 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
393 e1c701e7 Michael Hanselmann
             const=constants.JOBS_PENDING,
394 e1c701e7 Michael Hanselmann
             help="Select jobs pending execution or being cancelled")
395 f037e9d7 Michael Hanselmann
396 f037e9d7 Michael Hanselmann
_RUNNING_OPT = \
397 f037e9d7 Michael Hanselmann
  cli_option("--running", default=None,
398 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
399 f037e9d7 Michael Hanselmann
             const=frozenset([
400 f037e9d7 Michael Hanselmann
               constants.JOB_STATUS_RUNNING,
401 f037e9d7 Michael Hanselmann
               ]),
402 f037e9d7 Michael Hanselmann
             help="Show jobs currently running only")
403 f037e9d7 Michael Hanselmann
404 f037e9d7 Michael Hanselmann
_ERROR_OPT = \
405 f037e9d7 Michael Hanselmann
  cli_option("--error", default=None,
406 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
407 f037e9d7 Michael Hanselmann
             const=frozenset([
408 f037e9d7 Michael Hanselmann
               constants.JOB_STATUS_ERROR,
409 f037e9d7 Michael Hanselmann
               ]),
410 f037e9d7 Michael Hanselmann
             help="Show failed jobs only")
411 f037e9d7 Michael Hanselmann
412 f037e9d7 Michael Hanselmann
_FINISHED_OPT = \
413 f037e9d7 Michael Hanselmann
  cli_option("--finished", default=None,
414 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
415 f037e9d7 Michael Hanselmann
             const=constants.JOBS_FINALIZED,
416 f037e9d7 Michael Hanselmann
             help="Show finished jobs only")
417 f037e9d7 Michael Hanselmann
418 43d51ef2 Michael Hanselmann
_ARCHIVED_OPT = \
419 43d51ef2 Michael Hanselmann
  cli_option("--archived", default=False,
420 43d51ef2 Michael Hanselmann
             action="store_true", dest="archived",
421 43d51ef2 Michael Hanselmann
             help="Include archived jobs in list (slow and expensive)")
422 43d51ef2 Michael Hanselmann
423 e1c701e7 Michael Hanselmann
_QUEUED_OPT = \
424 e1c701e7 Michael Hanselmann
  cli_option("--queued", default=None,
425 e1c701e7 Michael Hanselmann
             action="store_const", dest="status_filter",
426 e1c701e7 Michael Hanselmann
             const=frozenset([
427 e1c701e7 Michael Hanselmann
               constants.JOB_STATUS_QUEUED,
428 e1c701e7 Michael Hanselmann
               ]),
429 e1c701e7 Michael Hanselmann
             help="Select queued jobs only")
430 e1c701e7 Michael Hanselmann
431 e1c701e7 Michael Hanselmann
_WAITING_OPT = \
432 e1c701e7 Michael Hanselmann
  cli_option("--waiting", default=None,
433 e1c701e7 Michael Hanselmann
             action="store_const", dest="status_filter",
434 e1c701e7 Michael Hanselmann
             const=frozenset([
435 e1c701e7 Michael Hanselmann
               constants.JOB_STATUS_WAITING,
436 e1c701e7 Michael Hanselmann
               ]),
437 e1c701e7 Michael Hanselmann
             help="Select waiting jobs only")
438 e1c701e7 Michael Hanselmann
439 f037e9d7 Michael Hanselmann
440 7a1ecaed Iustin Pop
commands = {
441 d0c8c01d Iustin Pop
  "list": (
442 6ea815cf Iustin Pop
    ListJobs, [ArgJobId()],
443 f037e9d7 Michael Hanselmann
    [NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT, FORCE_FILTER_OPT,
444 43d51ef2 Michael Hanselmann
     _PENDING_OPT, _RUNNING_OPT, _ERROR_OPT, _FINISHED_OPT, _ARCHIVED_OPT],
445 6ea815cf Iustin Pop
    "[job_id ...]",
446 3086220e Michael Hanselmann
    "Lists the jobs and their status. The available fields can be shown"
447 3086220e Michael Hanselmann
    " using the \"list-fields\" command (see the man page for details)."
448 3086220e Michael Hanselmann
    " The default field list is (in order): %s." %
449 3086220e Michael Hanselmann
    utils.CommaJoin(_LIST_DEF_FIELDS)),
450 3086220e Michael Hanselmann
  "list-fields": (
451 3086220e Michael Hanselmann
    ListJobFields, [ArgUnknown()],
452 3086220e Michael Hanselmann
    [NOHDR_OPT, SEP_OPT],
453 3086220e Michael Hanselmann
    "[fields...]",
454 3086220e Michael Hanselmann
    "Lists all available fields for jobs"),
455 d0c8c01d Iustin Pop
  "archive": (
456 064c21f8 Iustin Pop
    ArchiveJobs, [ArgJobId(min=1)], [],
457 6ea815cf Iustin Pop
    "<job-id> [<job-id> ...]", "Archive specified jobs"),
458 d0c8c01d Iustin Pop
  "autoarchive": (
459 6ea815cf Iustin Pop
    AutoArchiveJobs,
460 94182b63 Iustin Pop
    [ArgSuggest(min=1, max=1, choices=["1d", "1w", "4w", "all"])],
461 064c21f8 Iustin Pop
    [],
462 6ea815cf Iustin Pop
    "<age>", "Auto archive jobs older than the given age"),
463 d0c8c01d Iustin Pop
  "cancel": (
464 e1c701e7 Michael Hanselmann
    CancelJobs, [ArgJobId()],
465 e1c701e7 Michael Hanselmann
    [FORCE_OPT, _PENDING_OPT, _QUEUED_OPT, _WAITING_OPT],
466 e1c701e7 Michael Hanselmann
    "{[--force] {--pending | --queued | --waiting} |"
467 e1c701e7 Michael Hanselmann
    " <job-id> [<job-id> ...]}",
468 e1c701e7 Michael Hanselmann
    "Cancel jobs"),
469 d0c8c01d Iustin Pop
  "info": (
470 064c21f8 Iustin Pop
    ShowJobs, [ArgJobId(min=1)], [],
471 6ea815cf Iustin Pop
    "<job-id> [<job-id> ...]",
472 6ea815cf Iustin Pop
    "Show detailed information about the specified jobs"),
473 d0c8c01d Iustin Pop
  "watch": (
474 064c21f8 Iustin Pop
    WatchJob, [ArgJobId(min=1, max=1)], [],
475 6ea815cf Iustin Pop
    "<job-id>", "Follows a job and prints its output as it arrives"),
476 7a1ecaed Iustin Pop
  }
477 7a1ecaed Iustin Pop
478 7a1ecaed Iustin Pop
479 029fe503 Guido Trotter
#: dictionary with aliases for commands
480 029fe503 Guido Trotter
aliases = {
481 029fe503 Guido Trotter
  "show": "info",
482 029fe503 Guido Trotter
  }
483 029fe503 Guido Trotter
484 029fe503 Guido Trotter
485 a09b9e3d Michael Hanselmann
def Main():
486 029fe503 Guido Trotter
  return GenericMain(commands, aliases=aliases)