Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_job.py @ 1fa2c40b

History | View | Annotate | Download (15.8 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 feec4cc5 Michael Hanselmann
def _MultiJobAction(opts, args, cl, stdout_fn, ask_fn, question, action_fn):
178 feec4cc5 Michael Hanselmann
  """Applies a function to multipe jobs.
179 917b4e56 Iustin Pop

180 feec4cc5 Michael Hanselmann
  @param opts: Command line options
181 917b4e56 Iustin Pop
  @type args: list
182 feec4cc5 Michael Hanselmann
  @param args: Job IDs
183 917b4e56 Iustin Pop
  @rtype: int
184 feec4cc5 Michael Hanselmann
  @return: 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 feec4cc5 Michael Hanselmann
  if stdout_fn is None:
191 feec4cc5 Michael Hanselmann
    stdout_fn = ToStdout
192 feec4cc5 Michael Hanselmann
193 feec4cc5 Michael Hanselmann
  if ask_fn is None:
194 feec4cc5 Michael Hanselmann
    ask_fn = AskUser
195 feec4cc5 Michael Hanselmann
196 06fef5e0 Michael Hanselmann
  result = constants.EXIT_SUCCESS
197 d2b92ffc Michael Hanselmann
198 e1c701e7 Michael Hanselmann
  if bool(args) ^ (opts.status_filter is None):
199 e1c701e7 Michael Hanselmann
    raise errors.OpPrereqError("Either a status filter or job ID(s) must be"
200 e1c701e7 Michael Hanselmann
                               " specified and never both", errors.ECODE_INVAL)
201 e1c701e7 Michael Hanselmann
202 e1c701e7 Michael Hanselmann
  if opts.status_filter is not None:
203 e1c701e7 Michael Hanselmann
    response = cl.Query(constants.QR_JOB, ["id", "status", "summary"],
204 e1c701e7 Michael Hanselmann
                        qlang.MakeSimpleFilter("status", opts.status_filter))
205 e1c701e7 Michael Hanselmann
206 e1c701e7 Michael Hanselmann
    jobs = [i for ((_, i), _, _) in response.data]
207 e1c701e7 Michael Hanselmann
    if not jobs:
208 e1c701e7 Michael Hanselmann
      raise errors.OpPrereqError("No jobs with the requested status have been"
209 e1c701e7 Michael Hanselmann
                                 " found", errors.ECODE_STATE)
210 e1c701e7 Michael Hanselmann
211 e1c701e7 Michael Hanselmann
    if not opts.force:
212 e1c701e7 Michael Hanselmann
      (_, table) = FormatQueryResult(response, header=True,
213 e1c701e7 Michael Hanselmann
                                     format_override=_JOB_LIST_FORMAT)
214 e1c701e7 Michael Hanselmann
      for line in table:
215 feec4cc5 Michael Hanselmann
        stdout_fn(line)
216 e1c701e7 Michael Hanselmann
217 feec4cc5 Michael Hanselmann
      if not ask_fn(question):
218 e1c701e7 Michael Hanselmann
        return constants.EXIT_CONFIRMATION
219 e1c701e7 Michael Hanselmann
  else:
220 e1c701e7 Michael Hanselmann
    jobs = args
221 e1c701e7 Michael Hanselmann
222 e1c701e7 Michael Hanselmann
  for job_id in jobs:
223 feec4cc5 Michael Hanselmann
    (success, msg) = action_fn(cl, job_id)
224 06fef5e0 Michael Hanselmann
225 06fef5e0 Michael Hanselmann
    if not success:
226 06fef5e0 Michael Hanselmann
      result = constants.EXIT_FAILURE
227 06fef5e0 Michael Hanselmann
228 feec4cc5 Michael Hanselmann
    stdout_fn(msg)
229 d2b92ffc Michael Hanselmann
230 06fef5e0 Michael Hanselmann
  return result
231 d2b92ffc Michael Hanselmann
232 d2b92ffc Michael Hanselmann
233 feec4cc5 Michael Hanselmann
def CancelJobs(opts, args, cl=None, _stdout_fn=ToStdout, _ask_fn=AskUser):
234 feec4cc5 Michael Hanselmann
  """Cancel not-yet-started jobs.
235 feec4cc5 Michael Hanselmann

236 feec4cc5 Michael Hanselmann
  @param opts: the command line options selected by the user
237 feec4cc5 Michael Hanselmann
  @type args: list
238 feec4cc5 Michael Hanselmann
  @param args: should contain the job IDs to be cancelled
239 feec4cc5 Michael Hanselmann
  @rtype: int
240 feec4cc5 Michael Hanselmann
  @return: the desired exit code
241 feec4cc5 Michael Hanselmann

242 feec4cc5 Michael Hanselmann
  """
243 feec4cc5 Michael Hanselmann
  return _MultiJobAction(opts, args, cl, _stdout_fn, _ask_fn,
244 feec4cc5 Michael Hanselmann
                         "Cancel job(s) listed above?",
245 feec4cc5 Michael Hanselmann
                         lambda cl, job_id: cl.CancelJob(job_id))
246 feec4cc5 Michael Hanselmann
247 feec4cc5 Michael Hanselmann
248 e9e07c9c Michael Hanselmann
def ChangePriority(opts, args):
249 e9e07c9c Michael Hanselmann
  """Change priority of jobs.
250 e9e07c9c Michael Hanselmann

251 e9e07c9c Michael Hanselmann
  @param opts: Command line options
252 e9e07c9c Michael Hanselmann
  @type args: list
253 e9e07c9c Michael Hanselmann
  @param args: Job IDs
254 e9e07c9c Michael Hanselmann
  @rtype: int
255 e9e07c9c Michael Hanselmann
  @return: Exit code
256 e9e07c9c Michael Hanselmann

257 e9e07c9c Michael Hanselmann
  """
258 e9e07c9c Michael Hanselmann
  if opts.priority is None:
259 e9e07c9c Michael Hanselmann
    ToStderr("--priority option must be given.")
260 e9e07c9c Michael Hanselmann
    return constants.EXIT_FAILURE
261 e9e07c9c Michael Hanselmann
262 e9e07c9c Michael Hanselmann
  return _MultiJobAction(opts, args, None, None, None,
263 e9e07c9c Michael Hanselmann
                         "Change priority of job(s) listed above?",
264 e9e07c9c Michael Hanselmann
                         lambda cl, job_id:
265 e9e07c9c Michael Hanselmann
                           cl.ChangeJobPriority(job_id, opts.priority))
266 e9e07c9c Michael Hanselmann
267 e9e07c9c Michael Hanselmann
268 191712c0 Iustin Pop
def ShowJobs(opts, args):
269 917b4e56 Iustin Pop
  """Show detailed information about jobs.
270 917b4e56 Iustin Pop

271 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
272 917b4e56 Iustin Pop
  @type args: list
273 917b4e56 Iustin Pop
  @param args: should contain the job IDs to be queried
274 917b4e56 Iustin Pop
  @rtype: int
275 917b4e56 Iustin Pop
  @return: the desired exit code
276 191712c0 Iustin Pop

277 191712c0 Iustin Pop
  """
278 c04bc777 Iustin Pop
  def format_msg(level, text):
279 191712c0 Iustin Pop
    """Display the text indented."""
280 3a24c527 Iustin Pop
    ToStdout("%s%s", "  " * level, text)
281 191712c0 Iustin Pop
282 191712c0 Iustin Pop
  def result_helper(value):
283 191712c0 Iustin Pop
    """Format a result field in a nice way."""
284 191712c0 Iustin Pop
    if isinstance(value, (tuple, list)):
285 1f864b60 Iustin Pop
      return "[%s]" % utils.CommaJoin(value)
286 191712c0 Iustin Pop
    else:
287 191712c0 Iustin Pop
      return str(value)
288 191712c0 Iustin Pop
289 aad81f98 Iustin Pop
  selected_fields = [
290 aad81f98 Iustin Pop
    "id", "status", "ops", "opresult", "opstatus", "oplog",
291 b9b5abcb Iustin Pop
    "opstart", "opexec", "opend", "received_ts", "start_ts", "end_ts",
292 aad81f98 Iustin Pop
    ]
293 191712c0 Iustin Pop
294 76b62028 Iustin Pop
  qfilter = qlang.MakeSimpleFilter("id", _ParseJobIds(args))
295 76b62028 Iustin Pop
  result = GetClient().Query(constants.QR_JOB, selected_fields, qfilter).data
296 191712c0 Iustin Pop
297 191712c0 Iustin Pop
  first = True
298 191712c0 Iustin Pop
299 eba1aaad Michael Hanselmann
  for entry in result:
300 191712c0 Iustin Pop
    if not first:
301 c04bc777 Iustin Pop
      format_msg(0, "")
302 191712c0 Iustin Pop
    else:
303 191712c0 Iustin Pop
      first = False
304 aad81f98 Iustin Pop
305 eba1aaad Michael Hanselmann
    ((_, job_id), (rs_status, status), (_, ops), (_, opresult), (_, opstatus),
306 eba1aaad Michael Hanselmann
     (_, oplog), (_, opstart), (_, opexec), (_, opend), (_, recv_ts),
307 eba1aaad Michael Hanselmann
     (_, start_ts), (_, end_ts)) = entry
308 eba1aaad Michael Hanselmann
309 eba1aaad Michael Hanselmann
    # Detect non-normal results
310 eba1aaad Michael Hanselmann
    if rs_status != constants.RS_NORMAL:
311 eba1aaad Michael Hanselmann
      format_msg(0, "Job ID %s not found" % job_id)
312 aad81f98 Iustin Pop
      continue
313 aad81f98 Iustin Pop
314 c04bc777 Iustin Pop
    format_msg(0, "Job ID: %s" % job_id)
315 191712c0 Iustin Pop
    if status in _USER_JOB_STATUS:
316 191712c0 Iustin Pop
      status = _USER_JOB_STATUS[status]
317 191712c0 Iustin Pop
    else:
318 2f79bd34 Iustin Pop
      raise errors.ProgrammerError("Unknown job status code '%s'" % status)
319 191712c0 Iustin Pop
320 c04bc777 Iustin Pop
    format_msg(1, "Status: %s" % status)
321 aad81f98 Iustin Pop
322 aad81f98 Iustin Pop
    if recv_ts is not None:
323 c04bc777 Iustin Pop
      format_msg(1, "Received:         %s" % FormatTimestamp(recv_ts))
324 aad81f98 Iustin Pop
    else:
325 c04bc777 Iustin Pop
      format_msg(1, "Missing received timestamp (%s)" % str(recv_ts))
326 aad81f98 Iustin Pop
327 aad81f98 Iustin Pop
    if start_ts is not None:
328 aad81f98 Iustin Pop
      if recv_ts is not None:
329 aad81f98 Iustin Pop
        d1 = start_ts[0] - recv_ts[0] + (start_ts[1] - recv_ts[1]) / 1000000.0
330 aad81f98 Iustin Pop
        delta = " (delta %.6fs)" % d1
331 aad81f98 Iustin Pop
      else:
332 aad81f98 Iustin Pop
        delta = ""
333 c04bc777 Iustin Pop
      format_msg(1, "Processing start: %s%s" %
334 c04bc777 Iustin Pop
                 (FormatTimestamp(start_ts), delta))
335 aad81f98 Iustin Pop
    else:
336 c04bc777 Iustin Pop
      format_msg(1, "Processing start: unknown (%s)" % str(start_ts))
337 aad81f98 Iustin Pop
338 aad81f98 Iustin Pop
    if end_ts is not None:
339 aad81f98 Iustin Pop
      if start_ts is not None:
340 aad81f98 Iustin Pop
        d2 = end_ts[0] - start_ts[0] + (end_ts[1] - start_ts[1]) / 1000000.0
341 aad81f98 Iustin Pop
        delta = " (delta %.6fs)" % d2
342 aad81f98 Iustin Pop
      else:
343 aad81f98 Iustin Pop
        delta = ""
344 c04bc777 Iustin Pop
      format_msg(1, "Processing end:   %s%s" %
345 c04bc777 Iustin Pop
                 (FormatTimestamp(end_ts), delta))
346 aad81f98 Iustin Pop
    else:
347 c04bc777 Iustin Pop
      format_msg(1, "Processing end:   unknown (%s)" % str(end_ts))
348 aad81f98 Iustin Pop
349 aad81f98 Iustin Pop
    if end_ts is not None and recv_ts is not None:
350 aad81f98 Iustin Pop
      d3 = end_ts[0] - recv_ts[0] + (end_ts[1] - recv_ts[1]) / 1000000.0
351 c04bc777 Iustin Pop
      format_msg(1, "Total processing time: %.6f seconds" % d3)
352 aad81f98 Iustin Pop
    else:
353 c04bc777 Iustin Pop
      format_msg(1, "Total processing time: N/A")
354 c04bc777 Iustin Pop
    format_msg(1, "Opcodes:")
355 b9b5abcb Iustin Pop
    for (opcode, result, status, log, s_ts, x_ts, e_ts) in \
356 b9b5abcb Iustin Pop
            zip(ops, opresult, opstatus, oplog, opstart, opexec, opend):
357 c04bc777 Iustin Pop
      format_msg(2, "%s" % opcode["OP_ID"])
358 c04bc777 Iustin Pop
      format_msg(3, "Status: %s" % status)
359 aad81f98 Iustin Pop
      if isinstance(s_ts, (tuple, list)):
360 c04bc777 Iustin Pop
        format_msg(3, "Processing start: %s" % FormatTimestamp(s_ts))
361 aad81f98 Iustin Pop
      else:
362 c04bc777 Iustin Pop
        format_msg(3, "No processing start time")
363 b9b5abcb Iustin Pop
      if isinstance(x_ts, (tuple, list)):
364 c04bc777 Iustin Pop
        format_msg(3, "Execution start:  %s" % FormatTimestamp(x_ts))
365 b9b5abcb Iustin Pop
      else:
366 c04bc777 Iustin Pop
        format_msg(3, "No execution start time")
367 aad81f98 Iustin Pop
      if isinstance(e_ts, (tuple, list)):
368 c04bc777 Iustin Pop
        format_msg(3, "Processing end:   %s" % FormatTimestamp(e_ts))
369 aad81f98 Iustin Pop
      else:
370 c04bc777 Iustin Pop
        format_msg(3, "No processing end time")
371 c04bc777 Iustin Pop
      format_msg(3, "Input fields:")
372 598b5255 Michael Hanselmann
      for key in utils.NiceSort(opcode.keys()):
373 191712c0 Iustin Pop
        if key == "OP_ID":
374 191712c0 Iustin Pop
          continue
375 598b5255 Michael Hanselmann
        val = opcode[key]
376 191712c0 Iustin Pop
        if isinstance(val, (tuple, list)):
377 08db7c5c Iustin Pop
          val = ",".join([str(item) for item in val])
378 c04bc777 Iustin Pop
        format_msg(4, "%s: %s" % (key, val))
379 191712c0 Iustin Pop
      if result is None:
380 c04bc777 Iustin Pop
        format_msg(3, "No output data")
381 191712c0 Iustin Pop
      elif isinstance(result, (tuple, list)):
382 191712c0 Iustin Pop
        if not result:
383 c04bc777 Iustin Pop
          format_msg(3, "Result: empty sequence")
384 191712c0 Iustin Pop
        else:
385 c04bc777 Iustin Pop
          format_msg(3, "Result:")
386 191712c0 Iustin Pop
          for elem in result:
387 c04bc777 Iustin Pop
            format_msg(4, result_helper(elem))
388 191712c0 Iustin Pop
      elif isinstance(result, dict):
389 191712c0 Iustin Pop
        if not result:
390 c04bc777 Iustin Pop
          format_msg(3, "Result: empty dictionary")
391 191712c0 Iustin Pop
        else:
392 d1b47b16 Michael Hanselmann
          format_msg(3, "Result:")
393 191712c0 Iustin Pop
          for key, val in result.iteritems():
394 c04bc777 Iustin Pop
            format_msg(4, "%s: %s" % (key, result_helper(val)))
395 191712c0 Iustin Pop
      else:
396 c04bc777 Iustin Pop
        format_msg(3, "Result: %s" % result)
397 c04bc777 Iustin Pop
      format_msg(3, "Execution log:")
398 3386e7a9 Iustin Pop
      for serial, log_ts, log_type, log_msg in log:
399 3386e7a9 Iustin Pop
        time_txt = FormatTimestamp(log_ts)
400 8a7f1c61 Michael Hanselmann
        encoded = FormatLogMessage(log_type, log_msg)
401 c04bc777 Iustin Pop
        format_msg(4, "%s:%s:%s %s" % (serial, time_txt, log_type, encoded))
402 191712c0 Iustin Pop
  return 0
403 191712c0 Iustin Pop
404 191712c0 Iustin Pop
405 e7d6946c Michael Hanselmann
def WatchJob(opts, args):
406 e7d6946c Michael Hanselmann
  """Follow a job and print its output as it arrives.
407 e7d6946c Michael Hanselmann

408 e7d6946c Michael Hanselmann
  @param opts: the command line options selected by the user
409 e7d6946c Michael Hanselmann
  @type args: list
410 e7d6946c Michael Hanselmann
  @param args: Contains the job ID
411 e7d6946c Michael Hanselmann
  @rtype: int
412 e7d6946c Michael Hanselmann
  @return: the desired exit code
413 e7d6946c Michael Hanselmann

414 e7d6946c Michael Hanselmann
  """
415 e7d6946c Michael Hanselmann
  job_id = args[0]
416 e7d6946c Michael Hanselmann
417 e7d6946c Michael Hanselmann
  msg = ("Output from job %s follows" % job_id)
418 e7d6946c Michael Hanselmann
  ToStdout(msg)
419 e7d6946c Michael Hanselmann
  ToStdout("-" * len(msg))
420 e7d6946c Michael Hanselmann
421 e7d6946c Michael Hanselmann
  retcode = 0
422 e7d6946c Michael Hanselmann
  try:
423 e7d6946c Michael Hanselmann
    cli.PollJob(job_id)
424 e7d6946c Michael Hanselmann
  except errors.GenericError, err:
425 e7d6946c Michael Hanselmann
    (retcode, job_result) = cli.FormatError(err)
426 e7d6946c Michael Hanselmann
    ToStderr("Job %s failed: %s", job_id, job_result)
427 e7d6946c Michael Hanselmann
428 e7d6946c Michael Hanselmann
  return retcode
429 e7d6946c Michael Hanselmann
430 e7d6946c Michael Hanselmann
431 f037e9d7 Michael Hanselmann
_PENDING_OPT = \
432 f037e9d7 Michael Hanselmann
  cli_option("--pending", default=None,
433 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
434 e1c701e7 Michael Hanselmann
             const=constants.JOBS_PENDING,
435 e1c701e7 Michael Hanselmann
             help="Select jobs pending execution or being cancelled")
436 f037e9d7 Michael Hanselmann
437 f037e9d7 Michael Hanselmann
_RUNNING_OPT = \
438 f037e9d7 Michael Hanselmann
  cli_option("--running", default=None,
439 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
440 f037e9d7 Michael Hanselmann
             const=frozenset([
441 f037e9d7 Michael Hanselmann
               constants.JOB_STATUS_RUNNING,
442 f037e9d7 Michael Hanselmann
               ]),
443 f037e9d7 Michael Hanselmann
             help="Show jobs currently running only")
444 f037e9d7 Michael Hanselmann
445 f037e9d7 Michael Hanselmann
_ERROR_OPT = \
446 f037e9d7 Michael Hanselmann
  cli_option("--error", default=None,
447 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
448 f037e9d7 Michael Hanselmann
             const=frozenset([
449 f037e9d7 Michael Hanselmann
               constants.JOB_STATUS_ERROR,
450 f037e9d7 Michael Hanselmann
               ]),
451 f037e9d7 Michael Hanselmann
             help="Show failed jobs only")
452 f037e9d7 Michael Hanselmann
453 f037e9d7 Michael Hanselmann
_FINISHED_OPT = \
454 f037e9d7 Michael Hanselmann
  cli_option("--finished", default=None,
455 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
456 f037e9d7 Michael Hanselmann
             const=constants.JOBS_FINALIZED,
457 f037e9d7 Michael Hanselmann
             help="Show finished jobs only")
458 f037e9d7 Michael Hanselmann
459 43d51ef2 Michael Hanselmann
_ARCHIVED_OPT = \
460 43d51ef2 Michael Hanselmann
  cli_option("--archived", default=False,
461 43d51ef2 Michael Hanselmann
             action="store_true", dest="archived",
462 43d51ef2 Michael Hanselmann
             help="Include archived jobs in list (slow and expensive)")
463 43d51ef2 Michael Hanselmann
464 e1c701e7 Michael Hanselmann
_QUEUED_OPT = \
465 e1c701e7 Michael Hanselmann
  cli_option("--queued", default=None,
466 e1c701e7 Michael Hanselmann
             action="store_const", dest="status_filter",
467 e1c701e7 Michael Hanselmann
             const=frozenset([
468 e1c701e7 Michael Hanselmann
               constants.JOB_STATUS_QUEUED,
469 e1c701e7 Michael Hanselmann
               ]),
470 e1c701e7 Michael Hanselmann
             help="Select queued jobs only")
471 e1c701e7 Michael Hanselmann
472 e1c701e7 Michael Hanselmann
_WAITING_OPT = \
473 e1c701e7 Michael Hanselmann
  cli_option("--waiting", default=None,
474 e1c701e7 Michael Hanselmann
             action="store_const", dest="status_filter",
475 e1c701e7 Michael Hanselmann
             const=frozenset([
476 e1c701e7 Michael Hanselmann
               constants.JOB_STATUS_WAITING,
477 e1c701e7 Michael Hanselmann
               ]),
478 e1c701e7 Michael Hanselmann
             help="Select waiting jobs only")
479 e1c701e7 Michael Hanselmann
480 f037e9d7 Michael Hanselmann
481 7a1ecaed Iustin Pop
commands = {
482 d0c8c01d Iustin Pop
  "list": (
483 6ea815cf Iustin Pop
    ListJobs, [ArgJobId()],
484 f037e9d7 Michael Hanselmann
    [NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT, FORCE_FILTER_OPT,
485 43d51ef2 Michael Hanselmann
     _PENDING_OPT, _RUNNING_OPT, _ERROR_OPT, _FINISHED_OPT, _ARCHIVED_OPT],
486 6ea815cf Iustin Pop
    "[job_id ...]",
487 3086220e Michael Hanselmann
    "Lists the jobs and their status. The available fields can be shown"
488 3086220e Michael Hanselmann
    " using the \"list-fields\" command (see the man page for details)."
489 3086220e Michael Hanselmann
    " The default field list is (in order): %s." %
490 3086220e Michael Hanselmann
    utils.CommaJoin(_LIST_DEF_FIELDS)),
491 3086220e Michael Hanselmann
  "list-fields": (
492 3086220e Michael Hanselmann
    ListJobFields, [ArgUnknown()],
493 3086220e Michael Hanselmann
    [NOHDR_OPT, SEP_OPT],
494 3086220e Michael Hanselmann
    "[fields...]",
495 3086220e Michael Hanselmann
    "Lists all available fields for jobs"),
496 d0c8c01d Iustin Pop
  "archive": (
497 064c21f8 Iustin Pop
    ArchiveJobs, [ArgJobId(min=1)], [],
498 6ea815cf Iustin Pop
    "<job-id> [<job-id> ...]", "Archive specified jobs"),
499 d0c8c01d Iustin Pop
  "autoarchive": (
500 6ea815cf Iustin Pop
    AutoArchiveJobs,
501 94182b63 Iustin Pop
    [ArgSuggest(min=1, max=1, choices=["1d", "1w", "4w", "all"])],
502 064c21f8 Iustin Pop
    [],
503 6ea815cf Iustin Pop
    "<age>", "Auto archive jobs older than the given age"),
504 d0c8c01d Iustin Pop
  "cancel": (
505 e1c701e7 Michael Hanselmann
    CancelJobs, [ArgJobId()],
506 e1c701e7 Michael Hanselmann
    [FORCE_OPT, _PENDING_OPT, _QUEUED_OPT, _WAITING_OPT],
507 e1c701e7 Michael Hanselmann
    "{[--force] {--pending | --queued | --waiting} |"
508 e1c701e7 Michael Hanselmann
    " <job-id> [<job-id> ...]}",
509 e1c701e7 Michael Hanselmann
    "Cancel jobs"),
510 d0c8c01d Iustin Pop
  "info": (
511 064c21f8 Iustin Pop
    ShowJobs, [ArgJobId(min=1)], [],
512 6ea815cf Iustin Pop
    "<job-id> [<job-id> ...]",
513 6ea815cf Iustin Pop
    "Show detailed information about the specified jobs"),
514 d0c8c01d Iustin Pop
  "watch": (
515 064c21f8 Iustin Pop
    WatchJob, [ArgJobId(min=1, max=1)], [],
516 6ea815cf Iustin Pop
    "<job-id>", "Follows a job and prints its output as it arrives"),
517 e9e07c9c Michael Hanselmann
  "change-priority": (
518 e9e07c9c Michael Hanselmann
    ChangePriority, [ArgJobId()],
519 e9e07c9c Michael Hanselmann
    [PRIORITY_OPT, FORCE_OPT, _PENDING_OPT, _QUEUED_OPT, _WAITING_OPT],
520 e9e07c9c Michael Hanselmann
    "--priority <priority> {[--force] {--pending | --queued | --waiting} |"
521 e9e07c9c Michael Hanselmann
    " <job-id> [<job-id> ...]}",
522 e9e07c9c Michael Hanselmann
    "Change the priority of jobs"),
523 7a1ecaed Iustin Pop
  }
524 7a1ecaed Iustin Pop
525 7a1ecaed Iustin Pop
526 029fe503 Guido Trotter
#: dictionary with aliases for commands
527 029fe503 Guido Trotter
aliases = {
528 029fe503 Guido Trotter
  "show": "info",
529 029fe503 Guido Trotter
  }
530 029fe503 Guido Trotter
531 029fe503 Guido Trotter
532 a09b9e3d Michael Hanselmann
def Main():
533 029fe503 Guido Trotter
  return GenericMain(commands, aliases=aliases)