Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_job.py @ 178ad717

History | View | Annotate | Download (16.6 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 04bcb621 Helga Velroyen
def _FormatSummary(value):
64 04bcb621 Helga Velroyen
  """Formats a job's summary. Takes possible non-ascii encoding into account.
65 04bcb621 Helga Velroyen

66 04bcb621 Helga Velroyen
  """
67 04bcb621 Helga Velroyen
  return ','.encode('utf-8').join(item.encode('utf-8') for item in value)
68 04bcb621 Helga Velroyen
69 04bcb621 Helga Velroyen
70 e1c701e7 Michael Hanselmann
_JOB_LIST_FORMAT = {
71 e1c701e7 Michael Hanselmann
  "status": (_FormatStatus, False),
72 04bcb621 Helga Velroyen
  "summary": (_FormatSummary, False),
73 e1c701e7 Michael Hanselmann
  }
74 e1c701e7 Michael Hanselmann
_JOB_LIST_FORMAT.update(dict.fromkeys(["opstart", "opexec", "opend"],
75 e1c701e7 Michael Hanselmann
                                      (lambda value: map(FormatTimestamp,
76 e1c701e7 Michael Hanselmann
                                                         value),
77 e1c701e7 Michael Hanselmann
                                       None)))
78 e1c701e7 Michael Hanselmann
79 e1c701e7 Michael Hanselmann
80 76b62028 Iustin Pop
def _ParseJobIds(args):
81 76b62028 Iustin Pop
  """Parses a list of string job IDs into integers.
82 76b62028 Iustin Pop

83 76b62028 Iustin Pop
  @param args: list of strings
84 76b62028 Iustin Pop
  @return: list of integers
85 76b62028 Iustin Pop
  @raise OpPrereqError: in case of invalid values
86 76b62028 Iustin Pop

87 76b62028 Iustin Pop
  """
88 76b62028 Iustin Pop
  try:
89 76b62028 Iustin Pop
    return [int(a) for a in args]
90 76b62028 Iustin Pop
  except (ValueError, TypeError), err:
91 76b62028 Iustin Pop
    raise errors.OpPrereqError("Invalid job ID passed: %s" % err,
92 76b62028 Iustin Pop
                               errors.ECODE_INVAL)
93 76b62028 Iustin Pop
94 76b62028 Iustin Pop
95 7a1ecaed Iustin Pop
def ListJobs(opts, args):
96 7a1ecaed Iustin Pop
  """List the jobs
97 7a1ecaed Iustin Pop

98 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
99 917b4e56 Iustin Pop
  @type args: list
100 917b4e56 Iustin Pop
  @param args: should be an empty list
101 917b4e56 Iustin Pop
  @rtype: int
102 917b4e56 Iustin Pop
  @return: the desired exit code
103 917b4e56 Iustin Pop

104 7a1ecaed Iustin Pop
  """
105 a4ebd726 Michael Hanselmann
  selected_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
106 7a1ecaed Iustin Pop
107 43d51ef2 Michael Hanselmann
  if opts.archived and "archived" not in selected_fields:
108 43d51ef2 Michael Hanselmann
    selected_fields.append("archived")
109 43d51ef2 Michael Hanselmann
110 f037e9d7 Michael Hanselmann
  qfilter = qlang.MakeSimpleFilter("status", opts.status_filter)
111 f037e9d7 Michael Hanselmann
112 d45a824b Iustin Pop
  cl = GetClient(query=True)
113 d45a824b Iustin Pop
114 3086220e Michael Hanselmann
  return GenericList(constants.QR_JOB, selected_fields, args, None,
115 3086220e Michael Hanselmann
                     opts.separator, not opts.no_headers,
116 e1c701e7 Michael Hanselmann
                     format_override=_JOB_LIST_FORMAT, verbose=opts.verbose,
117 f037e9d7 Michael Hanselmann
                     force_filter=opts.force_filter, namefield="id",
118 d45a824b Iustin Pop
                     qfilter=qfilter, isnumeric=True, cl=cl)
119 b8802cc4 Michael Hanselmann
120 dcbd6288 Guido Trotter
121 3086220e Michael Hanselmann
def ListJobFields(opts, args):
122 3086220e Michael Hanselmann
  """List job fields.
123 7a1ecaed Iustin Pop

124 3086220e Michael Hanselmann
  @param opts: the command line options selected by the user
125 3086220e Michael Hanselmann
  @type args: list
126 3086220e Michael Hanselmann
  @param args: fields to list, or empty for all
127 3086220e Michael Hanselmann
  @rtype: int
128 3086220e Michael Hanselmann
  @return: the desired exit code
129 3086220e Michael Hanselmann

130 3086220e Michael Hanselmann
  """
131 d45a824b Iustin Pop
  cl = GetClient(query=True)
132 d45a824b Iustin Pop
133 3086220e Michael Hanselmann
  return GenericListFields(constants.QR_JOB, args, opts.separator,
134 d45a824b Iustin Pop
                           not opts.no_headers, cl=cl)
135 7a1ecaed Iustin Pop
136 7a1ecaed Iustin Pop
137 0ad64cf8 Michael Hanselmann
def ArchiveJobs(opts, args):
138 917b4e56 Iustin Pop
  """Archive jobs.
139 917b4e56 Iustin Pop

140 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
141 917b4e56 Iustin Pop
  @type args: list
142 917b4e56 Iustin Pop
  @param args: should contain the job IDs to be archived
143 917b4e56 Iustin Pop
  @rtype: int
144 917b4e56 Iustin Pop
  @return: the desired exit code
145 917b4e56 Iustin Pop

146 917b4e56 Iustin Pop
  """
147 0ad64cf8 Michael Hanselmann
  client = GetClient()
148 0ad64cf8 Michael Hanselmann
149 aa9f8167 Iustin Pop
  rcode = 0
150 0ad64cf8 Michael Hanselmann
  for job_id in args:
151 aa9f8167 Iustin Pop
    if not client.ArchiveJob(job_id):
152 aa9f8167 Iustin Pop
      ToStderr("Failed to archive job with ID '%s'", job_id)
153 aa9f8167 Iustin Pop
      rcode = 1
154 0ad64cf8 Michael Hanselmann
155 aa9f8167 Iustin Pop
  return rcode
156 0ad64cf8 Michael Hanselmann
157 0ad64cf8 Michael Hanselmann
158 07cd723a Iustin Pop
def AutoArchiveJobs(opts, args):
159 917b4e56 Iustin Pop
  """Archive jobs based on age.
160 917b4e56 Iustin Pop

161 917b4e56 Iustin Pop
  This will archive jobs based on their age, or all jobs if a 'all' is
162 917b4e56 Iustin Pop
  passed.
163 917b4e56 Iustin Pop

164 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
165 917b4e56 Iustin Pop
  @type args: list
166 917b4e56 Iustin Pop
  @param args: should contain only one element, the age as a time spec
167 c41eea6e Iustin Pop
      that can be parsed by L{ganeti.cli.ParseTimespec} or the
168 c41eea6e Iustin Pop
      keyword I{all}, which will cause all jobs to be archived
169 917b4e56 Iustin Pop
  @rtype: int
170 917b4e56 Iustin Pop
  @return: the desired exit code
171 917b4e56 Iustin Pop

172 917b4e56 Iustin Pop
  """
173 07cd723a Iustin Pop
  client = GetClient()
174 07cd723a Iustin Pop
175 07cd723a Iustin Pop
  age = args[0]
176 07cd723a Iustin Pop
177 d0c8c01d Iustin Pop
  if age == "all":
178 07cd723a Iustin Pop
    age = -1
179 07cd723a Iustin Pop
  else:
180 07cd723a Iustin Pop
    age = ParseTimespec(age)
181 07cd723a Iustin Pop
182 f8ad5591 Michael Hanselmann
  (archived_count, jobs_left) = client.AutoArchiveJobs(age)
183 f8ad5591 Michael Hanselmann
  ToStdout("Archived %s jobs, %s unchecked left", archived_count, jobs_left)
184 f8ad5591 Michael Hanselmann
185 07cd723a Iustin Pop
  return 0
186 07cd723a Iustin Pop
187 07cd723a Iustin Pop
188 feec4cc5 Michael Hanselmann
def _MultiJobAction(opts, args, cl, stdout_fn, ask_fn, question, action_fn):
189 feec4cc5 Michael Hanselmann
  """Applies a function to multipe jobs.
190 917b4e56 Iustin Pop

191 feec4cc5 Michael Hanselmann
  @param opts: Command line options
192 917b4e56 Iustin Pop
  @type args: list
193 feec4cc5 Michael Hanselmann
  @param args: Job IDs
194 917b4e56 Iustin Pop
  @rtype: int
195 feec4cc5 Michael Hanselmann
  @return: Exit code
196 917b4e56 Iustin Pop

197 917b4e56 Iustin Pop
  """
198 e1c701e7 Michael Hanselmann
  if cl is None:
199 e1c701e7 Michael Hanselmann
    cl = GetClient()
200 e1c701e7 Michael Hanselmann
201 feec4cc5 Michael Hanselmann
  if stdout_fn is None:
202 feec4cc5 Michael Hanselmann
    stdout_fn = ToStdout
203 feec4cc5 Michael Hanselmann
204 feec4cc5 Michael Hanselmann
  if ask_fn is None:
205 feec4cc5 Michael Hanselmann
    ask_fn = AskUser
206 feec4cc5 Michael Hanselmann
207 06fef5e0 Michael Hanselmann
  result = constants.EXIT_SUCCESS
208 d2b92ffc Michael Hanselmann
209 e1c701e7 Michael Hanselmann
  if bool(args) ^ (opts.status_filter is None):
210 e1c701e7 Michael Hanselmann
    raise errors.OpPrereqError("Either a status filter or job ID(s) must be"
211 e1c701e7 Michael Hanselmann
                               " specified and never both", errors.ECODE_INVAL)
212 e1c701e7 Michael Hanselmann
213 e1c701e7 Michael Hanselmann
  if opts.status_filter is not None:
214 e1c701e7 Michael Hanselmann
    response = cl.Query(constants.QR_JOB, ["id", "status", "summary"],
215 e1c701e7 Michael Hanselmann
                        qlang.MakeSimpleFilter("status", opts.status_filter))
216 e1c701e7 Michael Hanselmann
217 e1c701e7 Michael Hanselmann
    jobs = [i for ((_, i), _, _) in response.data]
218 e1c701e7 Michael Hanselmann
    if not jobs:
219 e1c701e7 Michael Hanselmann
      raise errors.OpPrereqError("No jobs with the requested status have been"
220 e1c701e7 Michael Hanselmann
                                 " found", errors.ECODE_STATE)
221 e1c701e7 Michael Hanselmann
222 e1c701e7 Michael Hanselmann
    if not opts.force:
223 e1c701e7 Michael Hanselmann
      (_, table) = FormatQueryResult(response, header=True,
224 e1c701e7 Michael Hanselmann
                                     format_override=_JOB_LIST_FORMAT)
225 e1c701e7 Michael Hanselmann
      for line in table:
226 feec4cc5 Michael Hanselmann
        stdout_fn(line)
227 e1c701e7 Michael Hanselmann
228 feec4cc5 Michael Hanselmann
      if not ask_fn(question):
229 e1c701e7 Michael Hanselmann
        return constants.EXIT_CONFIRMATION
230 e1c701e7 Michael Hanselmann
  else:
231 e1c701e7 Michael Hanselmann
    jobs = args
232 e1c701e7 Michael Hanselmann
233 e1c701e7 Michael Hanselmann
  for job_id in jobs:
234 feec4cc5 Michael Hanselmann
    (success, msg) = action_fn(cl, job_id)
235 06fef5e0 Michael Hanselmann
236 06fef5e0 Michael Hanselmann
    if not success:
237 06fef5e0 Michael Hanselmann
      result = constants.EXIT_FAILURE
238 06fef5e0 Michael Hanselmann
239 feec4cc5 Michael Hanselmann
    stdout_fn(msg)
240 d2b92ffc Michael Hanselmann
241 06fef5e0 Michael Hanselmann
  return result
242 d2b92ffc Michael Hanselmann
243 d2b92ffc Michael Hanselmann
244 feec4cc5 Michael Hanselmann
def CancelJobs(opts, args, cl=None, _stdout_fn=ToStdout, _ask_fn=AskUser):
245 feec4cc5 Michael Hanselmann
  """Cancel not-yet-started jobs.
246 feec4cc5 Michael Hanselmann

247 feec4cc5 Michael Hanselmann
  @param opts: the command line options selected by the user
248 feec4cc5 Michael Hanselmann
  @type args: list
249 feec4cc5 Michael Hanselmann
  @param args: should contain the job IDs to be cancelled
250 feec4cc5 Michael Hanselmann
  @rtype: int
251 feec4cc5 Michael Hanselmann
  @return: the desired exit code
252 feec4cc5 Michael Hanselmann

253 feec4cc5 Michael Hanselmann
  """
254 feec4cc5 Michael Hanselmann
  return _MultiJobAction(opts, args, cl, _stdout_fn, _ask_fn,
255 feec4cc5 Michael Hanselmann
                         "Cancel job(s) listed above?",
256 feec4cc5 Michael Hanselmann
                         lambda cl, job_id: cl.CancelJob(job_id))
257 feec4cc5 Michael Hanselmann
258 feec4cc5 Michael Hanselmann
259 e9e07c9c Michael Hanselmann
def ChangePriority(opts, args):
260 e9e07c9c Michael Hanselmann
  """Change priority of jobs.
261 e9e07c9c Michael Hanselmann

262 e9e07c9c Michael Hanselmann
  @param opts: Command line options
263 e9e07c9c Michael Hanselmann
  @type args: list
264 e9e07c9c Michael Hanselmann
  @param args: Job IDs
265 e9e07c9c Michael Hanselmann
  @rtype: int
266 e9e07c9c Michael Hanselmann
  @return: Exit code
267 e9e07c9c Michael Hanselmann

268 e9e07c9c Michael Hanselmann
  """
269 e9e07c9c Michael Hanselmann
  if opts.priority is None:
270 e9e07c9c Michael Hanselmann
    ToStderr("--priority option must be given.")
271 e9e07c9c Michael Hanselmann
    return constants.EXIT_FAILURE
272 e9e07c9c Michael Hanselmann
273 e9e07c9c Michael Hanselmann
  return _MultiJobAction(opts, args, None, None, None,
274 e9e07c9c Michael Hanselmann
                         "Change priority of job(s) listed above?",
275 e9e07c9c Michael Hanselmann
                         lambda cl, job_id:
276 e9e07c9c Michael Hanselmann
                           cl.ChangeJobPriority(job_id, opts.priority))
277 e9e07c9c Michael Hanselmann
278 e9e07c9c Michael Hanselmann
279 191712c0 Iustin Pop
def ShowJobs(opts, args):
280 917b4e56 Iustin Pop
  """Show detailed information about jobs.
281 917b4e56 Iustin Pop

282 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
283 917b4e56 Iustin Pop
  @type args: list
284 917b4e56 Iustin Pop
  @param args: should contain the job IDs to be queried
285 917b4e56 Iustin Pop
  @rtype: int
286 917b4e56 Iustin Pop
  @return: the desired exit code
287 191712c0 Iustin Pop

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

420 e7d6946c Michael Hanselmann
  @param opts: the command line options selected by the user
421 e7d6946c Michael Hanselmann
  @type args: list
422 e7d6946c Michael Hanselmann
  @param args: Contains the job ID
423 e7d6946c Michael Hanselmann
  @rtype: int
424 e7d6946c Michael Hanselmann
  @return: the desired exit code
425 e7d6946c Michael Hanselmann

426 e7d6946c Michael Hanselmann
  """
427 e7d6946c Michael Hanselmann
  job_id = args[0]
428 e7d6946c Michael Hanselmann
429 e7d6946c Michael Hanselmann
  msg = ("Output from job %s follows" % job_id)
430 e7d6946c Michael Hanselmann
  ToStdout(msg)
431 e7d6946c Michael Hanselmann
  ToStdout("-" * len(msg))
432 e7d6946c Michael Hanselmann
433 e7d6946c Michael Hanselmann
  retcode = 0
434 e7d6946c Michael Hanselmann
  try:
435 e7d6946c Michael Hanselmann
    cli.PollJob(job_id)
436 e7d6946c Michael Hanselmann
  except errors.GenericError, err:
437 e7d6946c Michael Hanselmann
    (retcode, job_result) = cli.FormatError(err)
438 e7d6946c Michael Hanselmann
    ToStderr("Job %s failed: %s", job_id, job_result)
439 e7d6946c Michael Hanselmann
440 e7d6946c Michael Hanselmann
  return retcode
441 e7d6946c Michael Hanselmann
442 e7d6946c Michael Hanselmann
443 f5d13a77 Klaus Aehlig
def WaitJob(opts, args):
444 f5d13a77 Klaus Aehlig
  """Wait for a job to finish, not producing any output.
445 f5d13a77 Klaus Aehlig

446 f5d13a77 Klaus Aehlig
  @param opts: the command line options selected by the user
447 f5d13a77 Klaus Aehlig
  @type args: list
448 f5d13a77 Klaus Aehlig
  @param args: Contains the job ID
449 f5d13a77 Klaus Aehlig
  @rtype: int
450 f5d13a77 Klaus Aehlig
  @return: the desired exit code
451 f5d13a77 Klaus Aehlig

452 f5d13a77 Klaus Aehlig
  """
453 f5d13a77 Klaus Aehlig
  job_id = args[0]
454 f5d13a77 Klaus Aehlig
455 f5d13a77 Klaus Aehlig
  retcode = 0
456 f5d13a77 Klaus Aehlig
  try:
457 f5d13a77 Klaus Aehlig
    cli.PollJob(job_id, feedback_fn=lambda _: None)
458 f5d13a77 Klaus Aehlig
  except errors.GenericError, err:
459 f5d13a77 Klaus Aehlig
    (retcode, job_result) = cli.FormatError(err)
460 f5d13a77 Klaus Aehlig
    ToStderr("Job %s failed: %s", job_id, job_result)
461 f5d13a77 Klaus Aehlig
462 f5d13a77 Klaus Aehlig
  return retcode
463 f5d13a77 Klaus Aehlig
464 f5d13a77 Klaus Aehlig
465 f037e9d7 Michael Hanselmann
_PENDING_OPT = \
466 f037e9d7 Michael Hanselmann
  cli_option("--pending", default=None,
467 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
468 e1c701e7 Michael Hanselmann
             const=constants.JOBS_PENDING,
469 e1c701e7 Michael Hanselmann
             help="Select jobs pending execution or being cancelled")
470 f037e9d7 Michael Hanselmann
471 f037e9d7 Michael Hanselmann
_RUNNING_OPT = \
472 f037e9d7 Michael Hanselmann
  cli_option("--running", default=None,
473 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
474 f037e9d7 Michael Hanselmann
             const=frozenset([
475 f037e9d7 Michael Hanselmann
               constants.JOB_STATUS_RUNNING,
476 f037e9d7 Michael Hanselmann
               ]),
477 f037e9d7 Michael Hanselmann
             help="Show jobs currently running only")
478 f037e9d7 Michael Hanselmann
479 f037e9d7 Michael Hanselmann
_ERROR_OPT = \
480 f037e9d7 Michael Hanselmann
  cli_option("--error", default=None,
481 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
482 f037e9d7 Michael Hanselmann
             const=frozenset([
483 f037e9d7 Michael Hanselmann
               constants.JOB_STATUS_ERROR,
484 f037e9d7 Michael Hanselmann
               ]),
485 f037e9d7 Michael Hanselmann
             help="Show failed jobs only")
486 f037e9d7 Michael Hanselmann
487 f037e9d7 Michael Hanselmann
_FINISHED_OPT = \
488 f037e9d7 Michael Hanselmann
  cli_option("--finished", default=None,
489 f037e9d7 Michael Hanselmann
             action="store_const", dest="status_filter",
490 f037e9d7 Michael Hanselmann
             const=constants.JOBS_FINALIZED,
491 f037e9d7 Michael Hanselmann
             help="Show finished jobs only")
492 f037e9d7 Michael Hanselmann
493 43d51ef2 Michael Hanselmann
_ARCHIVED_OPT = \
494 43d51ef2 Michael Hanselmann
  cli_option("--archived", default=False,
495 43d51ef2 Michael Hanselmann
             action="store_true", dest="archived",
496 43d51ef2 Michael Hanselmann
             help="Include archived jobs in list (slow and expensive)")
497 43d51ef2 Michael Hanselmann
498 e1c701e7 Michael Hanselmann
_QUEUED_OPT = \
499 e1c701e7 Michael Hanselmann
  cli_option("--queued", default=None,
500 e1c701e7 Michael Hanselmann
             action="store_const", dest="status_filter",
501 e1c701e7 Michael Hanselmann
             const=frozenset([
502 e1c701e7 Michael Hanselmann
               constants.JOB_STATUS_QUEUED,
503 e1c701e7 Michael Hanselmann
               ]),
504 e1c701e7 Michael Hanselmann
             help="Select queued jobs only")
505 e1c701e7 Michael Hanselmann
506 e1c701e7 Michael Hanselmann
_WAITING_OPT = \
507 e1c701e7 Michael Hanselmann
  cli_option("--waiting", default=None,
508 e1c701e7 Michael Hanselmann
             action="store_const", dest="status_filter",
509 e1c701e7 Michael Hanselmann
             const=frozenset([
510 e1c701e7 Michael Hanselmann
               constants.JOB_STATUS_WAITING,
511 e1c701e7 Michael Hanselmann
               ]),
512 e1c701e7 Michael Hanselmann
             help="Select waiting jobs only")
513 e1c701e7 Michael Hanselmann
514 f037e9d7 Michael Hanselmann
515 7a1ecaed Iustin Pop
commands = {
516 d0c8c01d Iustin Pop
  "list": (
517 6ea815cf Iustin Pop
    ListJobs, [ArgJobId()],
518 f037e9d7 Michael Hanselmann
    [NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT, FORCE_FILTER_OPT,
519 43d51ef2 Michael Hanselmann
     _PENDING_OPT, _RUNNING_OPT, _ERROR_OPT, _FINISHED_OPT, _ARCHIVED_OPT],
520 6ea815cf Iustin Pop
    "[job_id ...]",
521 3086220e Michael Hanselmann
    "Lists the jobs and their status. The available fields can be shown"
522 3086220e Michael Hanselmann
    " using the \"list-fields\" command (see the man page for details)."
523 3086220e Michael Hanselmann
    " The default field list is (in order): %s." %
524 3086220e Michael Hanselmann
    utils.CommaJoin(_LIST_DEF_FIELDS)),
525 3086220e Michael Hanselmann
  "list-fields": (
526 3086220e Michael Hanselmann
    ListJobFields, [ArgUnknown()],
527 3086220e Michael Hanselmann
    [NOHDR_OPT, SEP_OPT],
528 3086220e Michael Hanselmann
    "[fields...]",
529 3086220e Michael Hanselmann
    "Lists all available fields for jobs"),
530 d0c8c01d Iustin Pop
  "archive": (
531 064c21f8 Iustin Pop
    ArchiveJobs, [ArgJobId(min=1)], [],
532 6ea815cf Iustin Pop
    "<job-id> [<job-id> ...]", "Archive specified jobs"),
533 d0c8c01d Iustin Pop
  "autoarchive": (
534 6ea815cf Iustin Pop
    AutoArchiveJobs,
535 94182b63 Iustin Pop
    [ArgSuggest(min=1, max=1, choices=["1d", "1w", "4w", "all"])],
536 064c21f8 Iustin Pop
    [],
537 6ea815cf Iustin Pop
    "<age>", "Auto archive jobs older than the given age"),
538 d0c8c01d Iustin Pop
  "cancel": (
539 e1c701e7 Michael Hanselmann
    CancelJobs, [ArgJobId()],
540 e1c701e7 Michael Hanselmann
    [FORCE_OPT, _PENDING_OPT, _QUEUED_OPT, _WAITING_OPT],
541 e1c701e7 Michael Hanselmann
    "{[--force] {--pending | --queued | --waiting} |"
542 e1c701e7 Michael Hanselmann
    " <job-id> [<job-id> ...]}",
543 e1c701e7 Michael Hanselmann
    "Cancel jobs"),
544 d0c8c01d Iustin Pop
  "info": (
545 064c21f8 Iustin Pop
    ShowJobs, [ArgJobId(min=1)], [],
546 6ea815cf Iustin Pop
    "<job-id> [<job-id> ...]",
547 6ea815cf Iustin Pop
    "Show detailed information about the specified jobs"),
548 f5d13a77 Klaus Aehlig
  "wait": (
549 f5d13a77 Klaus Aehlig
    WaitJob, [ArgJobId(min=1, max=1)], [],
550 f5d13a77 Klaus Aehlig
    "<job-id>", "Wait for a job to finish"),
551 d0c8c01d Iustin Pop
  "watch": (
552 064c21f8 Iustin Pop
    WatchJob, [ArgJobId(min=1, max=1)], [],
553 6ea815cf Iustin Pop
    "<job-id>", "Follows a job and prints its output as it arrives"),
554 e9e07c9c Michael Hanselmann
  "change-priority": (
555 e9e07c9c Michael Hanselmann
    ChangePriority, [ArgJobId()],
556 e9e07c9c Michael Hanselmann
    [PRIORITY_OPT, FORCE_OPT, _PENDING_OPT, _QUEUED_OPT, _WAITING_OPT],
557 e9e07c9c Michael Hanselmann
    "--priority <priority> {[--force] {--pending | --queued | --waiting} |"
558 e9e07c9c Michael Hanselmann
    " <job-id> [<job-id> ...]}",
559 e9e07c9c Michael Hanselmann
    "Change the priority of jobs"),
560 7a1ecaed Iustin Pop
  }
561 7a1ecaed Iustin Pop
562 7a1ecaed Iustin Pop
563 029fe503 Guido Trotter
#: dictionary with aliases for commands
564 029fe503 Guido Trotter
aliases = {
565 029fe503 Guido Trotter
  "show": "info",
566 029fe503 Guido Trotter
  }
567 029fe503 Guido Trotter
568 029fe503 Guido Trotter
569 a09b9e3d Michael Hanselmann
def Main():
570 029fe503 Guido Trotter
  return GenericMain(commands, aliases=aliases)