Abstract the timestamp formatting into cli.py
[ganeti-local] / scripts / gnt-job
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 import sys
23 import os
24 import itertools
25 import time
26 from optparse import make_option
27 from cStringIO import StringIO
28
29 from ganeti.cli import *
30 from ganeti import opcodes
31 from ganeti import logger
32 from ganeti import constants
33 from ganeti import utils
34 from ganeti import errors
35
36
37 _LIST_DEF_FIELDS = ["id", "status", "summary"]
38
39 _USER_JOB_STATUS = {
40   constants.JOB_STATUS_QUEUED: "queued",
41   constants.JOB_STATUS_RUNNING: "running",
42   constants.JOB_STATUS_CANCELED: "canceled",
43   constants.JOB_STATUS_SUCCESS: "success",
44   constants.JOB_STATUS_ERROR: "error",
45   }
46
47
48 def ListJobs(opts, args):
49   """List the jobs
50
51   """
52   if opts.output is None:
53     selected_fields = _LIST_DEF_FIELDS
54   elif opts.output.startswith("+"):
55     selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
56   else:
57     selected_fields = opts.output.split(",")
58
59   output = GetClient().QueryJobs(None, selected_fields)
60   if not opts.no_headers:
61     # TODO: Implement more fields
62     headers = {
63       "id": "ID",
64       "status": "Status",
65       "ops": "OpCodes",
66       "opresult": "OpCode_result",
67       "opstatus": "OpCode_status",
68       "oplog": "OpCode_log",
69       "summary": "Summary",
70       }
71   else:
72     headers = None
73
74   # we don't have yet unitfields here
75   unitfields = None
76   numfields = None
77
78   # change raw values to nicer strings
79   for row in output:
80     for idx, field in enumerate(selected_fields):
81       val = row[idx]
82       if field == "status":
83         if val in _USER_JOB_STATUS:
84           val = _USER_JOB_STATUS[val]
85         else:
86           raise errors.ProgrammerError("Unknown job status code '%s'" % val)
87       elif field == "summary":
88         val = ",".join(val)
89
90       row[idx] = str(val)
91
92   data = GenerateTable(separator=opts.separator, headers=headers,
93                        fields=selected_fields, unitfields=unitfields,
94                        numfields=numfields, data=output)
95   for line in data:
96     print line
97
98   return 0
99
100
101 def ArchiveJobs(opts, args):
102   client = GetClient()
103
104   for job_id in args:
105     client.ArchiveJob(job_id)
106
107   return 0
108
109
110 def CancelJobs(opts, args):
111   client = GetClient()
112
113   for job_id in args:
114     client.CancelJob(job_id)
115
116   return 0
117
118
119 def ShowJobs(opts, args):
120   """List the jobs
121
122   """
123   def format(level, text):
124     """Display the text indented."""
125     print "%s%s" % ("  " * level, text)
126
127   def result_helper(value):
128     """Format a result field in a nice way."""
129     if isinstance(value, (tuple, list)):
130       return "[%s]" % (", ".join(str(elem) for elem in value))
131     else:
132       return str(value)
133
134   selected_fields = ["id", "status", "ops", "opresult", "opstatus", "oplog"]
135
136   result = GetClient().QueryJobs(args, selected_fields)
137
138   first = True
139
140   for job_id, status, ops, opresult, opstatus, oplog in result:
141     if not first:
142       format(0, "")
143     else:
144       first = False
145     format(0, "Job ID: %s" % job_id)
146     if status in _USER_JOB_STATUS:
147       status = _USER_JOB_STATUS[status]
148     else:
149       raise errors.ProgrammerError("Unknown job status code '%s'" % val)
150
151     format(1, "Status: %s" % status)
152     format(1, "Opcodes:")
153     for opcode, result, status, log in zip(ops, opresult, opstatus, oplog):
154       format(2, "%s" % opcode["OP_ID"])
155       format(3, "Status: %s" % status)
156       format(3, "Input fields:")
157       for key, val in opcode.iteritems():
158         if key == "OP_ID":
159           continue
160         if isinstance(val, (tuple, list)):
161           val = ",".join(val)
162         format(4, "%s: %s" % (key, val))
163       if result is None:
164         format(3, "No output data")
165       elif isinstance(result, (tuple, list)):
166         if not result:
167           format(3, "Result: empty sequence")
168         else:
169           format(3, "Result:")
170           for elem in result:
171             format(4, result_helper(elem))
172       elif isinstance(result, dict):
173         if not result:
174           format(3, "Result: empty dictionary")
175         else:
176           for key, val in result.iteritems():
177             format(4, "%s: %s" % (key, result_helper(val)))
178       else:
179         format(3, "Result: %s" % result)
180       format(3, "Execution log:")
181       for serial, log_ts, log_type, log_msg in log:
182         time_txt = FormatTimestamp(log_ts)
183         encoded = str(log_msg).encode('string_escape')
184         format(4, "%s:%s:%s %s" % (serial, time_txt, log_type, encoded))
185   return 0
186
187
188 commands = {
189   'list': (ListJobs, ARGS_NONE,
190             [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
191             "", "List the jobs and their status. The available fields are"
192            " (see the man page for details): id, status, op_list,"
193            " op_status, op_result."
194            " The default field"
195            " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS)),
196   'archive': (ArchiveJobs, ARGS_ANY,
197               [DEBUG_OPT],
198               "<job-id> [<job-id> ...]",
199               "Archive specified jobs"),
200   'cancel': (CancelJobs, ARGS_ANY,
201              [DEBUG_OPT],
202              "<job-id> [<job-id> ...]",
203              "Cancel specified jobs"),
204   'info': (ShowJobs, ARGS_ANY, [DEBUG_OPT],
205            "<job-id> [<job-id> ...]",
206            "Show detailed information about the specified jobs"),
207   }
208
209
210 if __name__ == '__main__':
211   sys.exit(GenericMain(commands))