Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-job @ 6873a52a

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