Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-job @ f4ad2ef0

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