Package ganeti :: Package client :: Module gnt_job
[hide private]
[frames] | no frames]

Source Code for Module ganeti.client.gnt_job

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