X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/08db7c5cd2748c55676018f2aad9499b0f5530dc..a10caf87a59b8ad16b9d234bc9b0175dfe6f0c5a:/scripts/gnt-job diff --git a/scripts/gnt-job b/scripts/gnt-job index 39f0743..80def4b 100755 --- a/scripts/gnt-job +++ b/scripts/gnt-job @@ -28,6 +28,8 @@ import sys from ganeti.cli import * from ganeti import constants from ganeti import errors +from ganeti import utils +from ganeti import cli #: default list of fields for L{ListJobs} @@ -38,6 +40,7 @@ _LIST_DEF_FIELDS = ["id", "status", "summary"] _USER_JOB_STATUS = { constants.JOB_STATUS_QUEUED: "queued", constants.JOB_STATUS_WAITLOCK: "waiting", + constants.JOB_STATUS_CANCELING: "canceling", constants.JOB_STATUS_RUNNING: "running", constants.JOB_STATUS_CANCELED: "canceled", constants.JOB_STATUS_SUCCESS: "success", @@ -62,7 +65,7 @@ def ListJobs(opts, args): else: selected_fields = opts.output.split(",") - output = GetClient().QueryJobs(None, selected_fields) + output = GetClient().QueryJobs(args, selected_fields) if not opts.no_headers: # TODO: Implement more fields headers = { @@ -78,16 +81,17 @@ def ListJobs(opts, args): "start_ts": "Start", "end_ts": "End", "received_ts": "Received", + "lock_status": "LockStatus", } else: headers = None - # we don't have yet unitfields here - unitfields = None - numfields = None - # change raw values to nicer strings - for row in output: + for row_id, row in enumerate(output): + if row is None: + ToStderr("No such job: %s" % args[row_id]) + continue + for idx, field in enumerate(selected_fields): val = row[idx] if field == "status": @@ -101,12 +105,13 @@ def ListJobs(opts, args): val = FormatTimestamp(val) elif field in ("opstart", "opend"): val = [FormatTimestamp(entry) for entry in val] + elif field == "lock_status" and not val: + val = "-" row[idx] = str(val) data = GenerateTable(separator=opts.separator, headers=headers, - fields=selected_fields, unitfields=unitfields, - numfields=numfields, data=output) + fields=selected_fields, data=output) for line in data: ToStdout(line) @@ -140,8 +145,8 @@ def AutoArchiveJobs(opts, args): @param opts: the command line options selected by the user @type args: list @param args: should contain only one element, the age as a time spec - that can be parsed by L{cli.ParseTimespec} or the keyword I{all}, - which will cause all jobs to be archived + that can be parsed by L{ganeti.cli.ParseTimespec} or the + keyword I{all}, which will cause all jobs to be archived @rtype: int @return: the desired exit code @@ -155,7 +160,9 @@ def AutoArchiveJobs(opts, args): else: age = ParseTimespec(age) - client.AutoArchiveJobs(age) + (archived_count, jobs_left) = client.AutoArchiveJobs(age) + ToStdout("Archived %s jobs, %s unchecked left", archived_count, jobs_left) + return 0 @@ -172,8 +179,10 @@ def CancelJobs(opts, args): client = GetClient() for job_id in args: - client.CancelJob(job_id) + (success, msg) = client.CancelJob(job_id) + ToStdout(msg) + # TODO: Different exit value if not all jobs were canceled? return 0 @@ -302,34 +311,65 @@ def ShowJobs(opts, args): format(3, "Execution log:") for serial, log_ts, log_type, log_msg in log: time_txt = FormatTimestamp(log_ts) - encoded = str(log_msg).encode('string_escape') + encoded = utils.SafeEncode(log_msg) format(4, "%s:%s:%s %s" % (serial, time_txt, log_type, encoded)) return 0 +def WatchJob(opts, args): + """Follow a job and print its output as it arrives. + + @param opts: the command line options selected by the user + @type args: list + @param args: Contains the job ID + @rtype: int + @return: the desired exit code + + """ + job_id = args[0] + + msg = ("Output from job %s follows" % job_id) + ToStdout(msg) + ToStdout("-" * len(msg)) + + retcode = 0 + try: + cli.PollJob(job_id) + except errors.GenericError, err: + (retcode, job_result) = cli.FormatError(err) + ToStderr("Job %s failed: %s", job_id, job_result) + + return retcode + + commands = { - 'list': (ListJobs, ARGS_NONE, - [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], - "", "List the jobs and their status. The available fields are" - " (see the man page for details): id, status, op_list," - " op_status, op_result." - " The default field" - " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS)), - 'archive': (ArchiveJobs, ARGS_ANY, - [DEBUG_OPT], - " [ ...]", - "Archive specified jobs"), - 'autoarchive': (AutoArchiveJobs, ARGS_ONE, - [DEBUG_OPT], - "", - "Auto archive jobs older than the given age"), - 'cancel': (CancelJobs, ARGS_ANY, - [DEBUG_OPT], - " [ ...]", - "Cancel specified jobs"), - 'info': (ShowJobs, ARGS_ANY, [DEBUG_OPT], - " [ ...]", - "Show detailed information about the specified jobs"), + 'list': ( + ListJobs, [ArgJobId()], + [NOHDR_OPT, SEP_OPT, FIELDS_OPT], + "[job_id ...]", + "List the jobs and their status. The available fields are" + " (see the man page for details): id, status, op_list," + " op_status, op_result." + " The default field" + " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS)), + 'archive': ( + ArchiveJobs, [ArgJobId(min=1)], [], + " [ ...]", "Archive specified jobs"), + 'autoarchive': ( + AutoArchiveJobs, + [ArgSuggest(min=1, max=1, choices=["1d", "1w", "4w"])], + [], + "", "Auto archive jobs older than the given age"), + 'cancel': ( + CancelJobs, [ArgJobId(min=1)], [], + " [ ...]", "Cancel specified jobs"), + 'info': ( + ShowJobs, [ArgJobId(min=1)], [], + " [ ...]", + "Show detailed information about the specified jobs"), + 'watch': ( + WatchJob, [ArgJobId(min=1, max=1)], [], + "", "Follows a job and prints its output as it arrives"), }