Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-job @ af1a81d1

History | View | Annotate | Download (11.1 kB)

1 7a1ecaed Iustin Pop
#!/usr/bin/python
2 7a1ecaed Iustin Pop
#
3 7a1ecaed Iustin Pop
4 7a1ecaed Iustin Pop
# Copyright (C) 2006, 2007 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 2d54e29c Iustin Pop
# pylint: disable-msg=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
import sys
30 7a1ecaed Iustin Pop
31 7a1ecaed Iustin Pop
from ganeti.cli import *
32 7a1ecaed Iustin Pop
from ganeti import constants
33 7a1ecaed Iustin Pop
from ganeti import errors
34 26f15862 Iustin Pop
from ganeti import utils
35 e7d6946c Michael Hanselmann
from ganeti import cli
36 7a1ecaed Iustin Pop
37 7a1ecaed Iustin Pop
38 917b4e56 Iustin Pop
#: default list of fields for L{ListJobs}
39 60dd1473 Iustin Pop
_LIST_DEF_FIELDS = ["id", "status", "summary"]
40 7a5d3bbd Iustin Pop
41 917b4e56 Iustin Pop
#: map converting the job status contants to user-visible
42 917b4e56 Iustin Pop
#: names
43 af30b2fd Michael Hanselmann
_USER_JOB_STATUS = {
44 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_QUEUED: "queued",
45 e92376d7 Iustin Pop
  constants.JOB_STATUS_WAITLOCK: "waiting",
46 fbf0262f Michael Hanselmann
  constants.JOB_STATUS_CANCELING: "canceling",
47 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_RUNNING: "running",
48 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_CANCELED: "canceled",
49 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_SUCCESS: "success",
50 af30b2fd Michael Hanselmann
  constants.JOB_STATUS_ERROR: "error",
51 af30b2fd Michael Hanselmann
  }
52 af30b2fd Michael Hanselmann
53 0ad64cf8 Michael Hanselmann
54 7a1ecaed Iustin Pop
def ListJobs(opts, args):
55 7a1ecaed Iustin Pop
  """List the jobs
56 7a1ecaed Iustin Pop
57 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
58 917b4e56 Iustin Pop
  @type args: list
59 917b4e56 Iustin Pop
  @param args: should be an empty list
60 917b4e56 Iustin Pop
  @rtype: int
61 917b4e56 Iustin Pop
  @return: the desired exit code
62 917b4e56 Iustin Pop
63 7a1ecaed Iustin Pop
  """
64 a4ebd726 Michael Hanselmann
  selected_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
65 7a1ecaed Iustin Pop
66 f1de3563 Iustin Pop
  output = GetClient().QueryJobs(args, selected_fields)
67 7a1ecaed Iustin Pop
  if not opts.no_headers:
68 af30b2fd Michael Hanselmann
    # TODO: Implement more fields
69 7a1ecaed Iustin Pop
    headers = {
70 7a1ecaed Iustin Pop
      "id": "ID",
71 7a1ecaed Iustin Pop
      "status": "Status",
72 af30b2fd Michael Hanselmann
      "ops": "OpCodes",
73 af30b2fd Michael Hanselmann
      "opresult": "OpCode_result",
74 af30b2fd Michael Hanselmann
      "opstatus": "OpCode_status",
75 5b23c34c Iustin Pop
      "oplog": "OpCode_log",
76 60dd1473 Iustin Pop
      "summary": "Summary",
77 aad81f98 Iustin Pop
      "opstart": "OpCode_start",
78 b9b5abcb Iustin Pop
      "opexec": "OpCode_exec",
79 aad81f98 Iustin Pop
      "opend": "OpCode_end",
80 aad81f98 Iustin Pop
      "start_ts": "Start",
81 aad81f98 Iustin Pop
      "end_ts": "End",
82 aad81f98 Iustin Pop
      "received_ts": "Received",
83 7a1ecaed Iustin Pop
      }
84 7a1ecaed Iustin Pop
  else:
85 7a1ecaed Iustin Pop
    headers = None
86 7a1ecaed Iustin Pop
87 7a1ecaed Iustin Pop
  # change raw values to nicer strings
88 dcbd6288 Guido Trotter
  for row_id, row in enumerate(output):
89 dcbd6288 Guido Trotter
    if row is None:
90 dcbd6288 Guido Trotter
      ToStderr("No such job: %s" % args[row_id])
91 dcbd6288 Guido Trotter
      continue
92 dcbd6288 Guido Trotter
93 7a1ecaed Iustin Pop
    for idx, field in enumerate(selected_fields):
94 7a1ecaed Iustin Pop
      val = row[idx]
95 7a1ecaed Iustin Pop
      if field == "status":
96 af30b2fd Michael Hanselmann
        if val in _USER_JOB_STATUS:
97 af30b2fd Michael Hanselmann
          val = _USER_JOB_STATUS[val]
98 7a1ecaed Iustin Pop
        else:
99 7a1ecaed Iustin Pop
          raise errors.ProgrammerError("Unknown job status code '%s'" % val)
100 60dd1473 Iustin Pop
      elif field == "summary":
101 60dd1473 Iustin Pop
        val = ",".join(val)
102 aad81f98 Iustin Pop
      elif field in ("start_ts", "end_ts", "received_ts"):
103 aad81f98 Iustin Pop
        val = FormatTimestamp(val)
104 b9b5abcb Iustin Pop
      elif field in ("opstart", "opexec", "opend"):
105 aad81f98 Iustin Pop
        val = [FormatTimestamp(entry) for entry in val]
106 7a1ecaed Iustin Pop
107 7a1ecaed Iustin Pop
      row[idx] = str(val)
108 7a1ecaed Iustin Pop
109 7a1ecaed Iustin Pop
  data = GenerateTable(separator=opts.separator, headers=headers,
110 f1de3563 Iustin Pop
                       fields=selected_fields, data=output)
111 7a1ecaed Iustin Pop
  for line in data:
112 3a24c527 Iustin Pop
    ToStdout(line)
113 7a1ecaed Iustin Pop
114 7a1ecaed Iustin Pop
  return 0
115 7a1ecaed Iustin Pop
116 7a1ecaed Iustin Pop
117 0ad64cf8 Michael Hanselmann
def ArchiveJobs(opts, args):
118 917b4e56 Iustin Pop
  """Archive jobs.
119 917b4e56 Iustin Pop
120 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
121 917b4e56 Iustin Pop
  @type args: list
122 917b4e56 Iustin Pop
  @param args: should contain the job IDs to be archived
123 917b4e56 Iustin Pop
  @rtype: int
124 917b4e56 Iustin Pop
  @return: the desired exit code
125 917b4e56 Iustin Pop
126 917b4e56 Iustin Pop
  """
127 0ad64cf8 Michael Hanselmann
  client = GetClient()
128 0ad64cf8 Michael Hanselmann
129 aa9f8167 Iustin Pop
  rcode = 0
130 0ad64cf8 Michael Hanselmann
  for job_id in args:
131 aa9f8167 Iustin Pop
    if not client.ArchiveJob(job_id):
132 aa9f8167 Iustin Pop
      ToStderr("Failed to archive job with ID '%s'", job_id)
133 aa9f8167 Iustin Pop
      rcode = 1
134 0ad64cf8 Michael Hanselmann
135 aa9f8167 Iustin Pop
  return rcode
136 0ad64cf8 Michael Hanselmann
137 0ad64cf8 Michael Hanselmann
138 07cd723a Iustin Pop
def AutoArchiveJobs(opts, args):
139 917b4e56 Iustin Pop
  """Archive jobs based on age.
140 917b4e56 Iustin Pop
141 917b4e56 Iustin Pop
  This will archive jobs based on their age, or all jobs if a 'all' is
142 917b4e56 Iustin Pop
  passed.
143 917b4e56 Iustin Pop
144 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
145 917b4e56 Iustin Pop
  @type args: list
146 917b4e56 Iustin Pop
  @param args: should contain only one element, the age as a time spec
147 c41eea6e Iustin Pop
      that can be parsed by L{ganeti.cli.ParseTimespec} or the
148 c41eea6e Iustin Pop
      keyword I{all}, which will cause all jobs to be archived
149 917b4e56 Iustin Pop
  @rtype: int
150 917b4e56 Iustin Pop
  @return: the desired exit code
151 917b4e56 Iustin Pop
152 917b4e56 Iustin Pop
  """
153 07cd723a Iustin Pop
  client = GetClient()
154 07cd723a Iustin Pop
155 07cd723a Iustin Pop
  age = args[0]
156 07cd723a Iustin Pop
157 07cd723a Iustin Pop
  if age == 'all':
158 07cd723a Iustin Pop
    age = -1
159 07cd723a Iustin Pop
  else:
160 07cd723a Iustin Pop
    age = ParseTimespec(age)
161 07cd723a Iustin Pop
162 f8ad5591 Michael Hanselmann
  (archived_count, jobs_left) = client.AutoArchiveJobs(age)
163 f8ad5591 Michael Hanselmann
  ToStdout("Archived %s jobs, %s unchecked left", archived_count, jobs_left)
164 f8ad5591 Michael Hanselmann
165 07cd723a Iustin Pop
  return 0
166 07cd723a Iustin Pop
167 07cd723a Iustin Pop
168 d2b92ffc Michael Hanselmann
def CancelJobs(opts, args):
169 917b4e56 Iustin Pop
  """Cancel not-yet-started jobs.
170 917b4e56 Iustin Pop
171 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
172 917b4e56 Iustin Pop
  @type args: list
173 917b4e56 Iustin Pop
  @param args: should contain the job IDs to be cancelled
174 917b4e56 Iustin Pop
  @rtype: int
175 917b4e56 Iustin Pop
  @return: the desired exit code
176 917b4e56 Iustin Pop
177 917b4e56 Iustin Pop
  """
178 d2b92ffc Michael Hanselmann
  client = GetClient()
179 d2b92ffc Michael Hanselmann
180 d2b92ffc Michael Hanselmann
  for job_id in args:
181 f4ad2ef0 Iustin Pop
    (_, msg) = client.CancelJob(job_id)
182 b28a3e8b Michael Hanselmann
    ToStdout(msg)
183 d2b92ffc Michael Hanselmann
184 b28a3e8b Michael Hanselmann
  # TODO: Different exit value if not all jobs were canceled?
185 d2b92ffc Michael Hanselmann
  return 0
186 d2b92ffc Michael Hanselmann
187 d2b92ffc Michael Hanselmann
188 191712c0 Iustin Pop
def ShowJobs(opts, args):
189 917b4e56 Iustin Pop
  """Show detailed information about jobs.
190 917b4e56 Iustin Pop
191 917b4e56 Iustin Pop
  @param opts: the command line options selected by the user
192 917b4e56 Iustin Pop
  @type args: list
193 917b4e56 Iustin Pop
  @param args: should contain the job IDs to be queried
194 917b4e56 Iustin Pop
  @rtype: int
195 917b4e56 Iustin Pop
  @return: the desired exit code
196 191712c0 Iustin Pop
197 191712c0 Iustin Pop
  """
198 c04bc777 Iustin Pop
  def format_msg(level, text):
199 191712c0 Iustin Pop
    """Display the text indented."""
200 3a24c527 Iustin Pop
    ToStdout("%s%s", "  " * level, text)
201 191712c0 Iustin Pop
202 191712c0 Iustin Pop
  def result_helper(value):
203 191712c0 Iustin Pop
    """Format a result field in a nice way."""
204 191712c0 Iustin Pop
    if isinstance(value, (tuple, list)):
205 1f864b60 Iustin Pop
      return "[%s]" % utils.CommaJoin(value)
206 191712c0 Iustin Pop
    else:
207 191712c0 Iustin Pop
      return str(value)
208 191712c0 Iustin Pop
209 aad81f98 Iustin Pop
  selected_fields = [
210 aad81f98 Iustin Pop
    "id", "status", "ops", "opresult", "opstatus", "oplog",
211 b9b5abcb Iustin Pop
    "opstart", "opexec", "opend", "received_ts", "start_ts", "end_ts",
212 aad81f98 Iustin Pop
    ]
213 191712c0 Iustin Pop
214 191712c0 Iustin Pop
  result = GetClient().QueryJobs(args, selected_fields)
215 191712c0 Iustin Pop
216 191712c0 Iustin Pop
  first = True
217 191712c0 Iustin Pop
218 b27b39b0 Iustin Pop
  for idx, entry in enumerate(result):
219 191712c0 Iustin Pop
    if not first:
220 c04bc777 Iustin Pop
      format_msg(0, "")
221 191712c0 Iustin Pop
    else:
222 191712c0 Iustin Pop
      first = False
223 aad81f98 Iustin Pop
224 aad81f98 Iustin Pop
    if entry is None:
225 b27b39b0 Iustin Pop
      if idx <= len(args):
226 c04bc777 Iustin Pop
        format_msg(0, "Job ID %s not found" % args[idx])
227 b27b39b0 Iustin Pop
      else:
228 b27b39b0 Iustin Pop
        # this should not happen, when we don't pass args it will be a
229 b27b39b0 Iustin Pop
        # valid job returned
230 c04bc777 Iustin Pop
        format_msg(0, "Job ID requested as argument %s not found" % (idx + 1))
231 aad81f98 Iustin Pop
      continue
232 aad81f98 Iustin Pop
233 aad81f98 Iustin Pop
    (job_id, status, ops, opresult, opstatus, oplog,
234 b9b5abcb Iustin Pop
     opstart, opexec, opend, recv_ts, start_ts, end_ts) = entry
235 c04bc777 Iustin Pop
    format_msg(0, "Job ID: %s" % job_id)
236 191712c0 Iustin Pop
    if status in _USER_JOB_STATUS:
237 191712c0 Iustin Pop
      status = _USER_JOB_STATUS[status]
238 191712c0 Iustin Pop
    else:
239 2f79bd34 Iustin Pop
      raise errors.ProgrammerError("Unknown job status code '%s'" % status)
240 191712c0 Iustin Pop
241 c04bc777 Iustin Pop
    format_msg(1, "Status: %s" % status)
242 aad81f98 Iustin Pop
243 aad81f98 Iustin Pop
    if recv_ts is not None:
244 c04bc777 Iustin Pop
      format_msg(1, "Received:         %s" % FormatTimestamp(recv_ts))
245 aad81f98 Iustin Pop
    else:
246 c04bc777 Iustin Pop
      format_msg(1, "Missing received timestamp (%s)" % str(recv_ts))
247 aad81f98 Iustin Pop
248 aad81f98 Iustin Pop
    if start_ts is not None:
249 aad81f98 Iustin Pop
      if recv_ts is not None:
250 aad81f98 Iustin Pop
        d1 = start_ts[0] - recv_ts[0] + (start_ts[1] - recv_ts[1]) / 1000000.0
251 aad81f98 Iustin Pop
        delta = " (delta %.6fs)" % d1
252 aad81f98 Iustin Pop
      else:
253 aad81f98 Iustin Pop
        delta = ""
254 c04bc777 Iustin Pop
      format_msg(1, "Processing start: %s%s" %
255 c04bc777 Iustin Pop
                 (FormatTimestamp(start_ts), delta))
256 aad81f98 Iustin Pop
    else:
257 c04bc777 Iustin Pop
      format_msg(1, "Processing start: unknown (%s)" % str(start_ts))
258 aad81f98 Iustin Pop
259 aad81f98 Iustin Pop
    if end_ts is not None:
260 aad81f98 Iustin Pop
      if start_ts is not None:
261 aad81f98 Iustin Pop
        d2 = end_ts[0] - start_ts[0] + (end_ts[1] - start_ts[1]) / 1000000.0
262 aad81f98 Iustin Pop
        delta = " (delta %.6fs)" % d2
263 aad81f98 Iustin Pop
      else:
264 aad81f98 Iustin Pop
        delta = ""
265 c04bc777 Iustin Pop
      format_msg(1, "Processing end:   %s%s" %
266 c04bc777 Iustin Pop
                 (FormatTimestamp(end_ts), delta))
267 aad81f98 Iustin Pop
    else:
268 c04bc777 Iustin Pop
      format_msg(1, "Processing end:   unknown (%s)" % str(end_ts))
269 aad81f98 Iustin Pop
270 aad81f98 Iustin Pop
    if end_ts is not None and recv_ts is not None:
271 aad81f98 Iustin Pop
      d3 = end_ts[0] - recv_ts[0] + (end_ts[1] - recv_ts[1]) / 1000000.0
272 c04bc777 Iustin Pop
      format_msg(1, "Total processing time: %.6f seconds" % d3)
273 aad81f98 Iustin Pop
    else:
274 c04bc777 Iustin Pop
      format_msg(1, "Total processing time: N/A")
275 c04bc777 Iustin Pop
    format_msg(1, "Opcodes:")
276 b9b5abcb Iustin Pop
    for (opcode, result, status, log, s_ts, x_ts, e_ts) in \
277 b9b5abcb Iustin Pop
            zip(ops, opresult, opstatus, oplog, opstart, opexec, opend):
278 c04bc777 Iustin Pop
      format_msg(2, "%s" % opcode["OP_ID"])
279 c04bc777 Iustin Pop
      format_msg(3, "Status: %s" % status)
280 aad81f98 Iustin Pop
      if isinstance(s_ts, (tuple, list)):
281 c04bc777 Iustin Pop
        format_msg(3, "Processing start: %s" % FormatTimestamp(s_ts))
282 aad81f98 Iustin Pop
      else:
283 c04bc777 Iustin Pop
        format_msg(3, "No processing start time")
284 b9b5abcb Iustin Pop
      if isinstance(x_ts, (tuple, list)):
285 c04bc777 Iustin Pop
        format_msg(3, "Execution start:  %s" % FormatTimestamp(x_ts))
286 b9b5abcb Iustin Pop
      else:
287 c04bc777 Iustin Pop
        format_msg(3, "No execution start time")
288 aad81f98 Iustin Pop
      if isinstance(e_ts, (tuple, list)):
289 c04bc777 Iustin Pop
        format_msg(3, "Processing end:   %s" % FormatTimestamp(e_ts))
290 aad81f98 Iustin Pop
      else:
291 c04bc777 Iustin Pop
        format_msg(3, "No processing end time")
292 c04bc777 Iustin Pop
      format_msg(3, "Input fields:")
293 191712c0 Iustin Pop
      for key, val in opcode.iteritems():
294 191712c0 Iustin Pop
        if key == "OP_ID":
295 191712c0 Iustin Pop
          continue
296 191712c0 Iustin Pop
        if isinstance(val, (tuple, list)):
297 08db7c5c Iustin Pop
          val = ",".join([str(item) for item in val])
298 c04bc777 Iustin Pop
        format_msg(4, "%s: %s" % (key, val))
299 191712c0 Iustin Pop
      if result is None:
300 c04bc777 Iustin Pop
        format_msg(3, "No output data")
301 191712c0 Iustin Pop
      elif isinstance(result, (tuple, list)):
302 191712c0 Iustin Pop
        if not result:
303 c04bc777 Iustin Pop
          format_msg(3, "Result: empty sequence")
304 191712c0 Iustin Pop
        else:
305 c04bc777 Iustin Pop
          format_msg(3, "Result:")
306 191712c0 Iustin Pop
          for elem in result:
307 c04bc777 Iustin Pop
            format_msg(4, result_helper(elem))
308 191712c0 Iustin Pop
      elif isinstance(result, dict):
309 191712c0 Iustin Pop
        if not result:
310 c04bc777 Iustin Pop
          format_msg(3, "Result: empty dictionary")
311 191712c0 Iustin Pop
        else:
312 191712c0 Iustin Pop
          for key, val in result.iteritems():
313 c04bc777 Iustin Pop
            format_msg(4, "%s: %s" % (key, result_helper(val)))
314 191712c0 Iustin Pop
      else:
315 c04bc777 Iustin Pop
        format_msg(3, "Result: %s" % result)
316 c04bc777 Iustin Pop
      format_msg(3, "Execution log:")
317 3386e7a9 Iustin Pop
      for serial, log_ts, log_type, log_msg in log:
318 3386e7a9 Iustin Pop
        time_txt = FormatTimestamp(log_ts)
319 8a7f1c61 Michael Hanselmann
        encoded = FormatLogMessage(log_type, log_msg)
320 c04bc777 Iustin Pop
        format_msg(4, "%s:%s:%s %s" % (serial, time_txt, log_type, encoded))
321 191712c0 Iustin Pop
  return 0
322 191712c0 Iustin Pop
323 191712c0 Iustin Pop
324 e7d6946c Michael Hanselmann
def WatchJob(opts, args):
325 e7d6946c Michael Hanselmann
  """Follow a job and print its output as it arrives.
326 e7d6946c Michael Hanselmann
327 e7d6946c Michael Hanselmann
  @param opts: the command line options selected by the user
328 e7d6946c Michael Hanselmann
  @type args: list
329 e7d6946c Michael Hanselmann
  @param args: Contains the job ID
330 e7d6946c Michael Hanselmann
  @rtype: int
331 e7d6946c Michael Hanselmann
  @return: the desired exit code
332 e7d6946c Michael Hanselmann
333 e7d6946c Michael Hanselmann
  """
334 e7d6946c Michael Hanselmann
  job_id = args[0]
335 e7d6946c Michael Hanselmann
336 e7d6946c Michael Hanselmann
  msg = ("Output from job %s follows" % job_id)
337 e7d6946c Michael Hanselmann
  ToStdout(msg)
338 e7d6946c Michael Hanselmann
  ToStdout("-" * len(msg))
339 e7d6946c Michael Hanselmann
340 e7d6946c Michael Hanselmann
  retcode = 0
341 e7d6946c Michael Hanselmann
  try:
342 e7d6946c Michael Hanselmann
    cli.PollJob(job_id)
343 e7d6946c Michael Hanselmann
  except errors.GenericError, err:
344 e7d6946c Michael Hanselmann
    (retcode, job_result) = cli.FormatError(err)
345 e7d6946c Michael Hanselmann
    ToStderr("Job %s failed: %s", job_id, job_result)
346 e7d6946c Michael Hanselmann
347 e7d6946c Michael Hanselmann
  return retcode
348 e7d6946c Michael Hanselmann
349 e7d6946c Michael Hanselmann
350 7a1ecaed Iustin Pop
commands = {
351 6ea815cf Iustin Pop
  'list': (
352 6ea815cf Iustin Pop
    ListJobs, [ArgJobId()],
353 064c21f8 Iustin Pop
    [NOHDR_OPT, SEP_OPT, FIELDS_OPT],
354 6ea815cf Iustin Pop
    "[job_id ...]",
355 6ea815cf Iustin Pop
    "List the jobs and their status. The available fields are"
356 6ea815cf Iustin Pop
    " (see the man page for details): id, status, op_list,"
357 6ea815cf Iustin Pop
    " op_status, op_result."
358 6ea815cf Iustin Pop
    " The default field"
359 1f864b60 Iustin Pop
    " list is (in order): %s." % utils.CommaJoin(_LIST_DEF_FIELDS)),
360 6ea815cf Iustin Pop
  'archive': (
361 064c21f8 Iustin Pop
    ArchiveJobs, [ArgJobId(min=1)], [],
362 6ea815cf Iustin Pop
    "<job-id> [<job-id> ...]", "Archive specified jobs"),
363 6ea815cf Iustin Pop
  'autoarchive': (
364 6ea815cf Iustin Pop
    AutoArchiveJobs,
365 94182b63 Iustin Pop
    [ArgSuggest(min=1, max=1, choices=["1d", "1w", "4w", "all"])],
366 064c21f8 Iustin Pop
    [],
367 6ea815cf Iustin Pop
    "<age>", "Auto archive jobs older than the given age"),
368 6ea815cf Iustin Pop
  'cancel': (
369 064c21f8 Iustin Pop
    CancelJobs, [ArgJobId(min=1)], [],
370 6ea815cf Iustin Pop
    "<job-id> [<job-id> ...]", "Cancel specified jobs"),
371 6ea815cf Iustin Pop
  'info': (
372 064c21f8 Iustin Pop
    ShowJobs, [ArgJobId(min=1)], [],
373 6ea815cf Iustin Pop
    "<job-id> [<job-id> ...]",
374 6ea815cf Iustin Pop
    "Show detailed information about the specified jobs"),
375 6ea815cf Iustin Pop
  'watch': (
376 064c21f8 Iustin Pop
    WatchJob, [ArgJobId(min=1, max=1)], [],
377 6ea815cf Iustin Pop
    "<job-id>", "Follows a job and prints its output as it arrives"),
378 7a1ecaed Iustin Pop
  }
379 7a1ecaed Iustin Pop
380 7a1ecaed Iustin Pop
381 7a1ecaed Iustin Pop
if __name__ == '__main__':
382 7a1ecaed Iustin Pop
  sys.exit(GenericMain(commands))