Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-job @ 1f587d3d

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