root / lib / client / gnt_job.py @ e7a77eb8
History | View | Annotate | Download (16 kB)
1 | a09b9e3d | Michael Hanselmann | #
|
---|---|---|---|
2 | 7a1ecaed | Iustin Pop | #
|
3 | 7a1ecaed | Iustin Pop | |
4 | 76b62028 | Iustin Pop | # Copyright (C) 2006, 2007, 2012 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 | b459a848 | Andrea Spadaccini | # pylint: disable=W0401,W0613,W0614,C0103
|
24 | 2f79bd34 | Iustin Pop | # W0401: Wildcard import ganeti.cli
|
25 | 2d54e29c | Iustin Pop | # W0613: Unused argument, since all functions follow the same API
|
26 | 2f79bd34 | Iustin Pop | # W0614: Unused import %s from wildcard import (since we need cli)
|
27 | 7260cfbe | Iustin Pop | # C0103: Invalid name gnt-job
|
28 | 2f79bd34 | Iustin Pop | |
29 | 7a1ecaed | Iustin Pop | from ganeti.cli import * |
30 | 7a1ecaed | Iustin Pop | from ganeti import constants |
31 | 7a1ecaed | Iustin Pop | from ganeti import errors |
32 | 26f15862 | Iustin Pop | from ganeti import utils |
33 | e7d6946c | Michael Hanselmann | from ganeti import cli |
34 | 3086220e | Michael Hanselmann | from ganeti import qlang |
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 | 47099cd1 | Michael Hanselmann | constants.JOB_STATUS_WAITING: "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 | 3086220e | Michael Hanselmann | def _FormatStatus(value): |
54 | 3086220e | Michael Hanselmann | """Formats a job status.
|
55 | 3086220e | Michael Hanselmann |
|
56 | 3086220e | Michael Hanselmann | """
|
57 | 3086220e | Michael Hanselmann | try:
|
58 | 3086220e | Michael Hanselmann | return _USER_JOB_STATUS[value]
|
59 | 3086220e | Michael Hanselmann | except KeyError: |
60 | 3086220e | Michael Hanselmann | raise errors.ProgrammerError("Unknown job status code '%s'" % value) |
61 | 3086220e | Michael Hanselmann | |
62 | 3086220e | Michael Hanselmann | |
63 | 04bcb621 | Helga Velroyen | def _FormatSummary(value): |
64 | 04bcb621 | Helga Velroyen | """Formats a job's summary. Takes possible non-ascii encoding into account.
|
65 | 04bcb621 | Helga Velroyen |
|
66 | 04bcb621 | Helga Velroyen | """
|
67 | 04bcb621 | Helga Velroyen | return ','.encode('utf-8').join(item.encode('utf-8') for item in value) |
68 | 04bcb621 | Helga Velroyen | |
69 | 04bcb621 | Helga Velroyen | |
70 | e1c701e7 | Michael Hanselmann | _JOB_LIST_FORMAT = { |
71 | e1c701e7 | Michael Hanselmann | "status": (_FormatStatus, False), |
72 | 04bcb621 | Helga Velroyen | "summary": (_FormatSummary, False), |
73 | e1c701e7 | Michael Hanselmann | } |
74 | e1c701e7 | Michael Hanselmann | _JOB_LIST_FORMAT.update(dict.fromkeys(["opstart", "opexec", "opend"], |
75 | e1c701e7 | Michael Hanselmann | (lambda value: map(FormatTimestamp, |
76 | e1c701e7 | Michael Hanselmann | value), |
77 | e1c701e7 | Michael Hanselmann | None)))
|
78 | e1c701e7 | Michael Hanselmann | |
79 | e1c701e7 | Michael Hanselmann | |
80 | 76b62028 | Iustin Pop | def _ParseJobIds(args): |
81 | 76b62028 | Iustin Pop | """Parses a list of string job IDs into integers.
|
82 | 76b62028 | Iustin Pop |
|
83 | 76b62028 | Iustin Pop | @param args: list of strings
|
84 | 76b62028 | Iustin Pop | @return: list of integers
|
85 | 76b62028 | Iustin Pop | @raise OpPrereqError: in case of invalid values
|
86 | 76b62028 | Iustin Pop |
|
87 | 76b62028 | Iustin Pop | """
|
88 | 76b62028 | Iustin Pop | try:
|
89 | 76b62028 | Iustin Pop | return [int(a) for a in args] |
90 | 76b62028 | Iustin Pop | except (ValueError, TypeError), err: |
91 | 76b62028 | Iustin Pop | raise errors.OpPrereqError("Invalid job ID passed: %s" % err, |
92 | 76b62028 | Iustin Pop | errors.ECODE_INVAL) |
93 | 76b62028 | Iustin Pop | |
94 | 76b62028 | Iustin Pop | |
95 | 7a1ecaed | Iustin Pop | def ListJobs(opts, args): |
96 | 7a1ecaed | Iustin Pop | """List the jobs
|
97 | 7a1ecaed | Iustin Pop |
|
98 | 917b4e56 | Iustin Pop | @param opts: the command line options selected by the user
|
99 | 917b4e56 | Iustin Pop | @type args: list
|
100 | 917b4e56 | Iustin Pop | @param args: should be an empty list
|
101 | 917b4e56 | Iustin Pop | @rtype: int
|
102 | 917b4e56 | Iustin Pop | @return: the desired exit code
|
103 | 917b4e56 | Iustin Pop |
|
104 | 7a1ecaed | Iustin Pop | """
|
105 | a4ebd726 | Michael Hanselmann | selected_fields = ParseFields(opts.output, _LIST_DEF_FIELDS) |
106 | 7a1ecaed | Iustin Pop | |
107 | 43d51ef2 | Michael Hanselmann | if opts.archived and "archived" not in selected_fields: |
108 | 43d51ef2 | Michael Hanselmann | selected_fields.append("archived")
|
109 | 43d51ef2 | Michael Hanselmann | |
110 | f037e9d7 | Michael Hanselmann | qfilter = qlang.MakeSimpleFilter("status", opts.status_filter)
|
111 | f037e9d7 | Michael Hanselmann | |
112 | d45a824b | Iustin Pop | cl = GetClient(query=True)
|
113 | d45a824b | Iustin Pop | |
114 | 3086220e | Michael Hanselmann | return GenericList(constants.QR_JOB, selected_fields, args, None, |
115 | 3086220e | Michael Hanselmann | opts.separator, not opts.no_headers,
|
116 | e1c701e7 | Michael Hanselmann | format_override=_JOB_LIST_FORMAT, verbose=opts.verbose, |
117 | f037e9d7 | Michael Hanselmann | force_filter=opts.force_filter, namefield="id",
|
118 | d45a824b | Iustin Pop | qfilter=qfilter, isnumeric=True, cl=cl)
|
119 | b8802cc4 | Michael Hanselmann | |
120 | dcbd6288 | Guido Trotter | |
121 | 3086220e | Michael Hanselmann | def ListJobFields(opts, args): |
122 | 3086220e | Michael Hanselmann | """List job fields.
|
123 | 7a1ecaed | Iustin Pop |
|
124 | 3086220e | Michael Hanselmann | @param opts: the command line options selected by the user
|
125 | 3086220e | Michael Hanselmann | @type args: list
|
126 | 3086220e | Michael Hanselmann | @param args: fields to list, or empty for all
|
127 | 3086220e | Michael Hanselmann | @rtype: int
|
128 | 3086220e | Michael Hanselmann | @return: the desired exit code
|
129 | 3086220e | Michael Hanselmann |
|
130 | 3086220e | Michael Hanselmann | """
|
131 | d45a824b | Iustin Pop | cl = GetClient(query=True)
|
132 | d45a824b | Iustin Pop | |
133 | 3086220e | Michael Hanselmann | return GenericListFields(constants.QR_JOB, args, opts.separator,
|
134 | d45a824b | Iustin Pop | not opts.no_headers, cl=cl)
|
135 | 7a1ecaed | Iustin Pop | |
136 | 7a1ecaed | Iustin Pop | |
137 | 0ad64cf8 | Michael Hanselmann | def ArchiveJobs(opts, args): |
138 | 917b4e56 | Iustin Pop | """Archive jobs.
|
139 | 917b4e56 | Iustin Pop |
|
140 | 917b4e56 | Iustin Pop | @param opts: the command line options selected by the user
|
141 | 917b4e56 | Iustin Pop | @type args: list
|
142 | 917b4e56 | Iustin Pop | @param args: should contain the job IDs to be archived
|
143 | 917b4e56 | Iustin Pop | @rtype: int
|
144 | 917b4e56 | Iustin Pop | @return: the desired exit code
|
145 | 917b4e56 | Iustin Pop |
|
146 | 917b4e56 | Iustin Pop | """
|
147 | 0ad64cf8 | Michael Hanselmann | client = GetClient() |
148 | 0ad64cf8 | Michael Hanselmann | |
149 | aa9f8167 | Iustin Pop | rcode = 0
|
150 | 0ad64cf8 | Michael Hanselmann | for job_id in args: |
151 | aa9f8167 | Iustin Pop | if not client.ArchiveJob(job_id): |
152 | aa9f8167 | Iustin Pop | ToStderr("Failed to archive job with ID '%s'", job_id)
|
153 | aa9f8167 | Iustin Pop | rcode = 1
|
154 | 0ad64cf8 | Michael Hanselmann | |
155 | aa9f8167 | Iustin Pop | return rcode
|
156 | 0ad64cf8 | Michael Hanselmann | |
157 | 0ad64cf8 | Michael Hanselmann | |
158 | 07cd723a | Iustin Pop | def AutoArchiveJobs(opts, args): |
159 | 917b4e56 | Iustin Pop | """Archive jobs based on age.
|
160 | 917b4e56 | Iustin Pop |
|
161 | 917b4e56 | Iustin Pop | This will archive jobs based on their age, or all jobs if a 'all' is
|
162 | 917b4e56 | Iustin Pop | passed.
|
163 | 917b4e56 | Iustin Pop |
|
164 | 917b4e56 | Iustin Pop | @param opts: the command line options selected by the user
|
165 | 917b4e56 | Iustin Pop | @type args: list
|
166 | 917b4e56 | Iustin Pop | @param args: should contain only one element, the age as a time spec
|
167 | c41eea6e | Iustin Pop | that can be parsed by L{ganeti.cli.ParseTimespec} or the
|
168 | c41eea6e | Iustin Pop | keyword I{all}, which will cause all jobs to be archived
|
169 | 917b4e56 | Iustin Pop | @rtype: int
|
170 | 917b4e56 | Iustin Pop | @return: the desired exit code
|
171 | 917b4e56 | Iustin Pop |
|
172 | 917b4e56 | Iustin Pop | """
|
173 | 07cd723a | Iustin Pop | client = GetClient() |
174 | 07cd723a | Iustin Pop | |
175 | 07cd723a | Iustin Pop | age = args[0]
|
176 | 07cd723a | Iustin Pop | |
177 | d0c8c01d | Iustin Pop | if age == "all": |
178 | 07cd723a | Iustin Pop | age = -1
|
179 | 07cd723a | Iustin Pop | else:
|
180 | 07cd723a | Iustin Pop | age = ParseTimespec(age) |
181 | 07cd723a | Iustin Pop | |
182 | f8ad5591 | Michael Hanselmann | (archived_count, jobs_left) = client.AutoArchiveJobs(age) |
183 | f8ad5591 | Michael Hanselmann | ToStdout("Archived %s jobs, %s unchecked left", archived_count, jobs_left)
|
184 | f8ad5591 | Michael Hanselmann | |
185 | 07cd723a | Iustin Pop | return 0 |
186 | 07cd723a | Iustin Pop | |
187 | 07cd723a | Iustin Pop | |
188 | feec4cc5 | Michael Hanselmann | def _MultiJobAction(opts, args, cl, stdout_fn, ask_fn, question, action_fn): |
189 | feec4cc5 | Michael Hanselmann | """Applies a function to multipe jobs.
|
190 | 917b4e56 | Iustin Pop |
|
191 | feec4cc5 | Michael Hanselmann | @param opts: Command line options
|
192 | 917b4e56 | Iustin Pop | @type args: list
|
193 | feec4cc5 | Michael Hanselmann | @param args: Job IDs
|
194 | 917b4e56 | Iustin Pop | @rtype: int
|
195 | feec4cc5 | Michael Hanselmann | @return: Exit code
|
196 | 917b4e56 | Iustin Pop |
|
197 | 917b4e56 | Iustin Pop | """
|
198 | e1c701e7 | Michael Hanselmann | if cl is None: |
199 | e1c701e7 | Michael Hanselmann | cl = GetClient() |
200 | e1c701e7 | Michael Hanselmann | |
201 | feec4cc5 | Michael Hanselmann | if stdout_fn is None: |
202 | feec4cc5 | Michael Hanselmann | stdout_fn = ToStdout |
203 | feec4cc5 | Michael Hanselmann | |
204 | feec4cc5 | Michael Hanselmann | if ask_fn is None: |
205 | feec4cc5 | Michael Hanselmann | ask_fn = AskUser |
206 | feec4cc5 | Michael Hanselmann | |
207 | 06fef5e0 | Michael Hanselmann | result = constants.EXIT_SUCCESS |
208 | d2b92ffc | Michael Hanselmann | |
209 | e1c701e7 | Michael Hanselmann | if bool(args) ^ (opts.status_filter is None): |
210 | e1c701e7 | Michael Hanselmann | raise errors.OpPrereqError("Either a status filter or job ID(s) must be" |
211 | e1c701e7 | Michael Hanselmann | " specified and never both", errors.ECODE_INVAL)
|
212 | e1c701e7 | Michael Hanselmann | |
213 | e1c701e7 | Michael Hanselmann | if opts.status_filter is not None: |
214 | e1c701e7 | Michael Hanselmann | response = cl.Query(constants.QR_JOB, ["id", "status", "summary"], |
215 | e1c701e7 | Michael Hanselmann | qlang.MakeSimpleFilter("status", opts.status_filter))
|
216 | e1c701e7 | Michael Hanselmann | |
217 | e1c701e7 | Michael Hanselmann | jobs = [i for ((_, i), _, _) in response.data] |
218 | e1c701e7 | Michael Hanselmann | if not jobs: |
219 | e1c701e7 | Michael Hanselmann | raise errors.OpPrereqError("No jobs with the requested status have been" |
220 | e1c701e7 | Michael Hanselmann | " found", errors.ECODE_STATE)
|
221 | e1c701e7 | Michael Hanselmann | |
222 | e1c701e7 | Michael Hanselmann | if not opts.force: |
223 | e1c701e7 | Michael Hanselmann | (_, table) = FormatQueryResult(response, header=True,
|
224 | e1c701e7 | Michael Hanselmann | format_override=_JOB_LIST_FORMAT) |
225 | e1c701e7 | Michael Hanselmann | for line in table: |
226 | feec4cc5 | Michael Hanselmann | stdout_fn(line) |
227 | e1c701e7 | Michael Hanselmann | |
228 | feec4cc5 | Michael Hanselmann | if not ask_fn(question): |
229 | e1c701e7 | Michael Hanselmann | return constants.EXIT_CONFIRMATION
|
230 | e1c701e7 | Michael Hanselmann | else:
|
231 | e1c701e7 | Michael Hanselmann | jobs = args |
232 | e1c701e7 | Michael Hanselmann | |
233 | e1c701e7 | Michael Hanselmann | for job_id in jobs: |
234 | feec4cc5 | Michael Hanselmann | (success, msg) = action_fn(cl, job_id) |
235 | 06fef5e0 | Michael Hanselmann | |
236 | 06fef5e0 | Michael Hanselmann | if not success: |
237 | 06fef5e0 | Michael Hanselmann | result = constants.EXIT_FAILURE |
238 | 06fef5e0 | Michael Hanselmann | |
239 | feec4cc5 | Michael Hanselmann | stdout_fn(msg) |
240 | d2b92ffc | Michael Hanselmann | |
241 | 06fef5e0 | Michael Hanselmann | return result
|
242 | d2b92ffc | Michael Hanselmann | |
243 | d2b92ffc | Michael Hanselmann | |
244 | feec4cc5 | Michael Hanselmann | def CancelJobs(opts, args, cl=None, _stdout_fn=ToStdout, _ask_fn=AskUser): |
245 | feec4cc5 | Michael Hanselmann | """Cancel not-yet-started jobs.
|
246 | feec4cc5 | Michael Hanselmann |
|
247 | feec4cc5 | Michael Hanselmann | @param opts: the command line options selected by the user
|
248 | feec4cc5 | Michael Hanselmann | @type args: list
|
249 | feec4cc5 | Michael Hanselmann | @param args: should contain the job IDs to be cancelled
|
250 | feec4cc5 | Michael Hanselmann | @rtype: int
|
251 | feec4cc5 | Michael Hanselmann | @return: the desired exit code
|
252 | feec4cc5 | Michael Hanselmann |
|
253 | feec4cc5 | Michael Hanselmann | """
|
254 | feec4cc5 | Michael Hanselmann | return _MultiJobAction(opts, args, cl, _stdout_fn, _ask_fn,
|
255 | feec4cc5 | Michael Hanselmann | "Cancel job(s) listed above?",
|
256 | feec4cc5 | Michael Hanselmann | lambda cl, job_id: cl.CancelJob(job_id))
|
257 | feec4cc5 | Michael Hanselmann | |
258 | feec4cc5 | Michael Hanselmann | |
259 | e9e07c9c | Michael Hanselmann | def ChangePriority(opts, args): |
260 | e9e07c9c | Michael Hanselmann | """Change priority of jobs.
|
261 | e9e07c9c | Michael Hanselmann |
|
262 | e9e07c9c | Michael Hanselmann | @param opts: Command line options
|
263 | e9e07c9c | Michael Hanselmann | @type args: list
|
264 | e9e07c9c | Michael Hanselmann | @param args: Job IDs
|
265 | e9e07c9c | Michael Hanselmann | @rtype: int
|
266 | e9e07c9c | Michael Hanselmann | @return: Exit code
|
267 | e9e07c9c | Michael Hanselmann |
|
268 | e9e07c9c | Michael Hanselmann | """
|
269 | e9e07c9c | Michael Hanselmann | if opts.priority is None: |
270 | e9e07c9c | Michael Hanselmann | ToStderr("--priority option must be given.")
|
271 | e9e07c9c | Michael Hanselmann | return constants.EXIT_FAILURE
|
272 | e9e07c9c | Michael Hanselmann | |
273 | e9e07c9c | Michael Hanselmann | return _MultiJobAction(opts, args, None, None, None, |
274 | e9e07c9c | Michael Hanselmann | "Change priority of job(s) listed above?",
|
275 | e9e07c9c | Michael Hanselmann | lambda cl, job_id:
|
276 | e9e07c9c | Michael Hanselmann | cl.ChangeJobPriority(job_id, opts.priority)) |
277 | e9e07c9c | Michael Hanselmann | |
278 | e9e07c9c | Michael Hanselmann | |
279 | 191712c0 | Iustin Pop | def ShowJobs(opts, args): |
280 | 917b4e56 | Iustin Pop | """Show detailed information about jobs.
|
281 | 917b4e56 | Iustin Pop |
|
282 | 917b4e56 | Iustin Pop | @param opts: the command line options selected by the user
|
283 | 917b4e56 | Iustin Pop | @type args: list
|
284 | 917b4e56 | Iustin Pop | @param args: should contain the job IDs to be queried
|
285 | 917b4e56 | Iustin Pop | @rtype: int
|
286 | 917b4e56 | Iustin Pop | @return: the desired exit code
|
287 | 191712c0 | Iustin Pop |
|
288 | 191712c0 | Iustin Pop | """
|
289 | c04bc777 | Iustin Pop | def format_msg(level, text): |
290 | 191712c0 | Iustin Pop | """Display the text indented."""
|
291 | 3a24c527 | Iustin Pop | ToStdout("%s%s", " " * level, text) |
292 | 191712c0 | Iustin Pop | |
293 | 191712c0 | Iustin Pop | def result_helper(value): |
294 | 191712c0 | Iustin Pop | """Format a result field in a nice way."""
|
295 | 191712c0 | Iustin Pop | if isinstance(value, (tuple, list)): |
296 | 1f864b60 | Iustin Pop | return "[%s]" % utils.CommaJoin(value) |
297 | 191712c0 | Iustin Pop | else:
|
298 | 191712c0 | Iustin Pop | return str(value) |
299 | 191712c0 | Iustin Pop | |
300 | aad81f98 | Iustin Pop | selected_fields = [ |
301 | aad81f98 | Iustin Pop | "id", "status", "ops", "opresult", "opstatus", "oplog", |
302 | b9b5abcb | Iustin Pop | "opstart", "opexec", "opend", "received_ts", "start_ts", "end_ts", |
303 | aad81f98 | Iustin Pop | ] |
304 | 191712c0 | Iustin Pop | |
305 | 76b62028 | Iustin Pop | qfilter = qlang.MakeSimpleFilter("id", _ParseJobIds(args))
|
306 | d45a824b | Iustin Pop | cl = GetClient(query=True)
|
307 | d45a824b | Iustin Pop | result = cl.Query(constants.QR_JOB, selected_fields, qfilter).data |
308 | 191712c0 | Iustin Pop | |
309 | 191712c0 | Iustin Pop | first = True
|
310 | 191712c0 | Iustin Pop | |
311 | eba1aaad | Michael Hanselmann | for entry in result: |
312 | 191712c0 | Iustin Pop | if not first: |
313 | c04bc777 | Iustin Pop | format_msg(0, "") |
314 | 191712c0 | Iustin Pop | else:
|
315 | 191712c0 | Iustin Pop | first = False
|
316 | aad81f98 | Iustin Pop | |
317 | eba1aaad | Michael Hanselmann | ((_, job_id), (rs_status, status), (_, ops), (_, opresult), (_, opstatus), |
318 | eba1aaad | Michael Hanselmann | (_, oplog), (_, opstart), (_, opexec), (_, opend), (_, recv_ts), |
319 | eba1aaad | Michael Hanselmann | (_, start_ts), (_, end_ts)) = entry |
320 | eba1aaad | Michael Hanselmann | |
321 | eba1aaad | Michael Hanselmann | # Detect non-normal results
|
322 | eba1aaad | Michael Hanselmann | if rs_status != constants.RS_NORMAL:
|
323 | eba1aaad | Michael Hanselmann | format_msg(0, "Job ID %s not found" % job_id) |
324 | aad81f98 | Iustin Pop | continue
|
325 | aad81f98 | Iustin Pop | |
326 | c04bc777 | Iustin Pop | format_msg(0, "Job ID: %s" % job_id) |
327 | 191712c0 | Iustin Pop | if status in _USER_JOB_STATUS: |
328 | 191712c0 | Iustin Pop | status = _USER_JOB_STATUS[status] |
329 | 191712c0 | Iustin Pop | else:
|
330 | 2f79bd34 | Iustin Pop | raise errors.ProgrammerError("Unknown job status code '%s'" % status) |
331 | 191712c0 | Iustin Pop | |
332 | c04bc777 | Iustin Pop | format_msg(1, "Status: %s" % status) |
333 | aad81f98 | Iustin Pop | |
334 | aad81f98 | Iustin Pop | if recv_ts is not None: |
335 | c04bc777 | Iustin Pop | format_msg(1, "Received: %s" % FormatTimestamp(recv_ts)) |
336 | aad81f98 | Iustin Pop | else:
|
337 | c04bc777 | Iustin Pop | format_msg(1, "Missing received timestamp (%s)" % str(recv_ts)) |
338 | aad81f98 | Iustin Pop | |
339 | aad81f98 | Iustin Pop | if start_ts is not None: |
340 | aad81f98 | Iustin Pop | if recv_ts is not None: |
341 | aad81f98 | Iustin Pop | d1 = start_ts[0] - recv_ts[0] + (start_ts[1] - recv_ts[1]) / 1000000.0 |
342 | aad81f98 | Iustin Pop | delta = " (delta %.6fs)" % d1
|
343 | aad81f98 | Iustin Pop | else:
|
344 | aad81f98 | Iustin Pop | delta = ""
|
345 | c04bc777 | Iustin Pop | format_msg(1, "Processing start: %s%s" % |
346 | c04bc777 | Iustin Pop | (FormatTimestamp(start_ts), delta)) |
347 | aad81f98 | Iustin Pop | else:
|
348 | c04bc777 | Iustin Pop | format_msg(1, "Processing start: unknown (%s)" % str(start_ts)) |
349 | aad81f98 | Iustin Pop | |
350 | aad81f98 | Iustin Pop | if end_ts is not None: |
351 | aad81f98 | Iustin Pop | if start_ts is not None: |
352 | aad81f98 | Iustin Pop | d2 = end_ts[0] - start_ts[0] + (end_ts[1] - start_ts[1]) / 1000000.0 |
353 | aad81f98 | Iustin Pop | delta = " (delta %.6fs)" % d2
|
354 | aad81f98 | Iustin Pop | else:
|
355 | aad81f98 | Iustin Pop | delta = ""
|
356 | c04bc777 | Iustin Pop | format_msg(1, "Processing end: %s%s" % |
357 | c04bc777 | Iustin Pop | (FormatTimestamp(end_ts), delta)) |
358 | aad81f98 | Iustin Pop | else:
|
359 | c04bc777 | Iustin Pop | format_msg(1, "Processing end: unknown (%s)" % str(end_ts)) |
360 | aad81f98 | Iustin Pop | |
361 | aad81f98 | Iustin Pop | if end_ts is not None and recv_ts is not None: |
362 | aad81f98 | Iustin Pop | d3 = end_ts[0] - recv_ts[0] + (end_ts[1] - recv_ts[1]) / 1000000.0 |
363 | c04bc777 | Iustin Pop | format_msg(1, "Total processing time: %.6f seconds" % d3) |
364 | aad81f98 | Iustin Pop | else:
|
365 | c04bc777 | Iustin Pop | format_msg(1, "Total processing time: N/A") |
366 | c04bc777 | Iustin Pop | format_msg(1, "Opcodes:") |
367 | b9b5abcb | Iustin Pop | for (opcode, result, status, log, s_ts, x_ts, e_ts) in \ |
368 | b9b5abcb | Iustin Pop | zip(ops, opresult, opstatus, oplog, opstart, opexec, opend):
|
369 | c04bc777 | Iustin Pop | format_msg(2, "%s" % opcode["OP_ID"]) |
370 | c04bc777 | Iustin Pop | format_msg(3, "Status: %s" % status) |
371 | aad81f98 | Iustin Pop | if isinstance(s_ts, (tuple, list)): |
372 | c04bc777 | Iustin Pop | format_msg(3, "Processing start: %s" % FormatTimestamp(s_ts)) |
373 | aad81f98 | Iustin Pop | else:
|
374 | c04bc777 | Iustin Pop | format_msg(3, "No processing start time") |
375 | b9b5abcb | Iustin Pop | if isinstance(x_ts, (tuple, list)): |
376 | c04bc777 | Iustin Pop | format_msg(3, "Execution start: %s" % FormatTimestamp(x_ts)) |
377 | b9b5abcb | Iustin Pop | else:
|
378 | c04bc777 | Iustin Pop | format_msg(3, "No execution start time") |
379 | aad81f98 | Iustin Pop | if isinstance(e_ts, (tuple, list)): |
380 | c04bc777 | Iustin Pop | format_msg(3, "Processing end: %s" % FormatTimestamp(e_ts)) |
381 | aad81f98 | Iustin Pop | else:
|
382 | c04bc777 | Iustin Pop | format_msg(3, "No processing end time") |
383 | c04bc777 | Iustin Pop | format_msg(3, "Input fields:") |
384 | 598b5255 | Michael Hanselmann | for key in utils.NiceSort(opcode.keys()): |
385 | 191712c0 | Iustin Pop | if key == "OP_ID": |
386 | 191712c0 | Iustin Pop | continue
|
387 | 598b5255 | Michael Hanselmann | val = opcode[key] |
388 | 191712c0 | Iustin Pop | if isinstance(val, (tuple, list)): |
389 | 08db7c5c | Iustin Pop | val = ",".join([str(item) for item in val]) |
390 | c04bc777 | Iustin Pop | format_msg(4, "%s: %s" % (key, val)) |
391 | 191712c0 | Iustin Pop | if result is None: |
392 | c04bc777 | Iustin Pop | format_msg(3, "No output data") |
393 | 191712c0 | Iustin Pop | elif isinstance(result, (tuple, list)): |
394 | 191712c0 | Iustin Pop | if not result: |
395 | c04bc777 | Iustin Pop | format_msg(3, "Result: empty sequence") |
396 | 191712c0 | Iustin Pop | else:
|
397 | c04bc777 | Iustin Pop | format_msg(3, "Result:") |
398 | 191712c0 | Iustin Pop | for elem in result: |
399 | c04bc777 | Iustin Pop | format_msg(4, result_helper(elem))
|
400 | 191712c0 | Iustin Pop | elif isinstance(result, dict): |
401 | 191712c0 | Iustin Pop | if not result: |
402 | c04bc777 | Iustin Pop | format_msg(3, "Result: empty dictionary") |
403 | 191712c0 | Iustin Pop | else:
|
404 | d1b47b16 | Michael Hanselmann | format_msg(3, "Result:") |
405 | 191712c0 | Iustin Pop | for key, val in result.iteritems(): |
406 | c04bc777 | Iustin Pop | format_msg(4, "%s: %s" % (key, result_helper(val))) |
407 | 191712c0 | Iustin Pop | else:
|
408 | c04bc777 | Iustin Pop | format_msg(3, "Result: %s" % result) |
409 | c04bc777 | Iustin Pop | format_msg(3, "Execution log:") |
410 | 3386e7a9 | Iustin Pop | for serial, log_ts, log_type, log_msg in log: |
411 | 3386e7a9 | Iustin Pop | time_txt = FormatTimestamp(log_ts) |
412 | 8a7f1c61 | Michael Hanselmann | encoded = FormatLogMessage(log_type, log_msg) |
413 | c04bc777 | Iustin Pop | format_msg(4, "%s:%s:%s %s" % (serial, time_txt, log_type, encoded)) |
414 | 191712c0 | Iustin Pop | return 0 |
415 | 191712c0 | Iustin Pop | |
416 | 191712c0 | Iustin Pop | |
417 | e7d6946c | Michael Hanselmann | def WatchJob(opts, args): |
418 | e7d6946c | Michael Hanselmann | """Follow a job and print its output as it arrives.
|
419 | e7d6946c | Michael Hanselmann |
|
420 | e7d6946c | Michael Hanselmann | @param opts: the command line options selected by the user
|
421 | e7d6946c | Michael Hanselmann | @type args: list
|
422 | e7d6946c | Michael Hanselmann | @param args: Contains the job ID
|
423 | e7d6946c | Michael Hanselmann | @rtype: int
|
424 | e7d6946c | Michael Hanselmann | @return: the desired exit code
|
425 | e7d6946c | Michael Hanselmann |
|
426 | e7d6946c | Michael Hanselmann | """
|
427 | e7d6946c | Michael Hanselmann | job_id = args[0]
|
428 | e7d6946c | Michael Hanselmann | |
429 | e7d6946c | Michael Hanselmann | msg = ("Output from job %s follows" % job_id)
|
430 | e7d6946c | Michael Hanselmann | ToStdout(msg) |
431 | e7d6946c | Michael Hanselmann | ToStdout("-" * len(msg)) |
432 | e7d6946c | Michael Hanselmann | |
433 | e7d6946c | Michael Hanselmann | retcode = 0
|
434 | e7d6946c | Michael Hanselmann | try:
|
435 | e7d6946c | Michael Hanselmann | cli.PollJob(job_id) |
436 | e7d6946c | Michael Hanselmann | except errors.GenericError, err:
|
437 | e7d6946c | Michael Hanselmann | (retcode, job_result) = cli.FormatError(err) |
438 | e7d6946c | Michael Hanselmann | ToStderr("Job %s failed: %s", job_id, job_result)
|
439 | e7d6946c | Michael Hanselmann | |
440 | e7d6946c | Michael Hanselmann | return retcode
|
441 | e7d6946c | Michael Hanselmann | |
442 | e7d6946c | Michael Hanselmann | |
443 | f037e9d7 | Michael Hanselmann | _PENDING_OPT = \ |
444 | f037e9d7 | Michael Hanselmann | cli_option("--pending", default=None, |
445 | f037e9d7 | Michael Hanselmann | action="store_const", dest="status_filter", |
446 | e1c701e7 | Michael Hanselmann | const=constants.JOBS_PENDING, |
447 | e1c701e7 | Michael Hanselmann | help="Select jobs pending execution or being cancelled")
|
448 | f037e9d7 | Michael Hanselmann | |
449 | f037e9d7 | Michael Hanselmann | _RUNNING_OPT = \ |
450 | f037e9d7 | Michael Hanselmann | cli_option("--running", default=None, |
451 | f037e9d7 | Michael Hanselmann | action="store_const", dest="status_filter", |
452 | f037e9d7 | Michael Hanselmann | const=frozenset([
|
453 | f037e9d7 | Michael Hanselmann | constants.JOB_STATUS_RUNNING, |
454 | f037e9d7 | Michael Hanselmann | ]), |
455 | f037e9d7 | Michael Hanselmann | help="Show jobs currently running only")
|
456 | f037e9d7 | Michael Hanselmann | |
457 | f037e9d7 | Michael Hanselmann | _ERROR_OPT = \ |
458 | f037e9d7 | Michael Hanselmann | cli_option("--error", default=None, |
459 | f037e9d7 | Michael Hanselmann | action="store_const", dest="status_filter", |
460 | f037e9d7 | Michael Hanselmann | const=frozenset([
|
461 | f037e9d7 | Michael Hanselmann | constants.JOB_STATUS_ERROR, |
462 | f037e9d7 | Michael Hanselmann | ]), |
463 | f037e9d7 | Michael Hanselmann | help="Show failed jobs only")
|
464 | f037e9d7 | Michael Hanselmann | |
465 | f037e9d7 | Michael Hanselmann | _FINISHED_OPT = \ |
466 | f037e9d7 | Michael Hanselmann | cli_option("--finished", default=None, |
467 | f037e9d7 | Michael Hanselmann | action="store_const", dest="status_filter", |
468 | f037e9d7 | Michael Hanselmann | const=constants.JOBS_FINALIZED, |
469 | f037e9d7 | Michael Hanselmann | help="Show finished jobs only")
|
470 | f037e9d7 | Michael Hanselmann | |
471 | 43d51ef2 | Michael Hanselmann | _ARCHIVED_OPT = \ |
472 | 43d51ef2 | Michael Hanselmann | cli_option("--archived", default=False, |
473 | 43d51ef2 | Michael Hanselmann | action="store_true", dest="archived", |
474 | 43d51ef2 | Michael Hanselmann | help="Include archived jobs in list (slow and expensive)")
|
475 | 43d51ef2 | Michael Hanselmann | |
476 | e1c701e7 | Michael Hanselmann | _QUEUED_OPT = \ |
477 | e1c701e7 | Michael Hanselmann | cli_option("--queued", default=None, |
478 | e1c701e7 | Michael Hanselmann | action="store_const", dest="status_filter", |
479 | e1c701e7 | Michael Hanselmann | const=frozenset([
|
480 | e1c701e7 | Michael Hanselmann | constants.JOB_STATUS_QUEUED, |
481 | e1c701e7 | Michael Hanselmann | ]), |
482 | e1c701e7 | Michael Hanselmann | help="Select queued jobs only")
|
483 | e1c701e7 | Michael Hanselmann | |
484 | e1c701e7 | Michael Hanselmann | _WAITING_OPT = \ |
485 | e1c701e7 | Michael Hanselmann | cli_option("--waiting", default=None, |
486 | e1c701e7 | Michael Hanselmann | action="store_const", dest="status_filter", |
487 | e1c701e7 | Michael Hanselmann | const=frozenset([
|
488 | e1c701e7 | Michael Hanselmann | constants.JOB_STATUS_WAITING, |
489 | e1c701e7 | Michael Hanselmann | ]), |
490 | e1c701e7 | Michael Hanselmann | help="Select waiting jobs only")
|
491 | e1c701e7 | Michael Hanselmann | |
492 | f037e9d7 | Michael Hanselmann | |
493 | 7a1ecaed | Iustin Pop | commands = { |
494 | d0c8c01d | Iustin Pop | "list": (
|
495 | 6ea815cf | Iustin Pop | ListJobs, [ArgJobId()], |
496 | f037e9d7 | Michael Hanselmann | [NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT, FORCE_FILTER_OPT, |
497 | 43d51ef2 | Michael Hanselmann | _PENDING_OPT, _RUNNING_OPT, _ERROR_OPT, _FINISHED_OPT, _ARCHIVED_OPT], |
498 | 6ea815cf | Iustin Pop | "[job_id ...]",
|
499 | 3086220e | Michael Hanselmann | "Lists the jobs and their status. The available fields can be shown"
|
500 | 3086220e | Michael Hanselmann | " using the \"list-fields\" command (see the man page for details)."
|
501 | 3086220e | Michael Hanselmann | " The default field list is (in order): %s." %
|
502 | 3086220e | Michael Hanselmann | utils.CommaJoin(_LIST_DEF_FIELDS)), |
503 | 3086220e | Michael Hanselmann | "list-fields": (
|
504 | 3086220e | Michael Hanselmann | ListJobFields, [ArgUnknown()], |
505 | 3086220e | Michael Hanselmann | [NOHDR_OPT, SEP_OPT], |
506 | 3086220e | Michael Hanselmann | "[fields...]",
|
507 | 3086220e | Michael Hanselmann | "Lists all available fields for jobs"),
|
508 | d0c8c01d | Iustin Pop | "archive": (
|
509 | 064c21f8 | Iustin Pop | ArchiveJobs, [ArgJobId(min=1)], [],
|
510 | 6ea815cf | Iustin Pop | "<job-id> [<job-id> ...]", "Archive specified jobs"), |
511 | d0c8c01d | Iustin Pop | "autoarchive": (
|
512 | 6ea815cf | Iustin Pop | AutoArchiveJobs, |
513 | 94182b63 | Iustin Pop | [ArgSuggest(min=1, max=1, choices=["1d", "1w", "4w", "all"])], |
514 | 064c21f8 | Iustin Pop | [], |
515 | 6ea815cf | Iustin Pop | "<age>", "Auto archive jobs older than the given age"), |
516 | d0c8c01d | Iustin Pop | "cancel": (
|
517 | e1c701e7 | Michael Hanselmann | CancelJobs, [ArgJobId()], |
518 | e1c701e7 | Michael Hanselmann | [FORCE_OPT, _PENDING_OPT, _QUEUED_OPT, _WAITING_OPT], |
519 | e1c701e7 | Michael Hanselmann | "{[--force] {--pending | --queued | --waiting} |"
|
520 | e1c701e7 | Michael Hanselmann | " <job-id> [<job-id> ...]}",
|
521 | e1c701e7 | Michael Hanselmann | "Cancel jobs"),
|
522 | d0c8c01d | Iustin Pop | "info": (
|
523 | 064c21f8 | Iustin Pop | ShowJobs, [ArgJobId(min=1)], [],
|
524 | 6ea815cf | Iustin Pop | "<job-id> [<job-id> ...]",
|
525 | 6ea815cf | Iustin Pop | "Show detailed information about the specified jobs"),
|
526 | d0c8c01d | Iustin Pop | "watch": (
|
527 | 064c21f8 | Iustin Pop | WatchJob, [ArgJobId(min=1, max=1)], [], |
528 | 6ea815cf | Iustin Pop | "<job-id>", "Follows a job and prints its output as it arrives"), |
529 | e9e07c9c | Michael Hanselmann | "change-priority": (
|
530 | e9e07c9c | Michael Hanselmann | ChangePriority, [ArgJobId()], |
531 | e9e07c9c | Michael Hanselmann | [PRIORITY_OPT, FORCE_OPT, _PENDING_OPT, _QUEUED_OPT, _WAITING_OPT], |
532 | e9e07c9c | Michael Hanselmann | "--priority <priority> {[--force] {--pending | --queued | --waiting} |"
|
533 | e9e07c9c | Michael Hanselmann | " <job-id> [<job-id> ...]}",
|
534 | e9e07c9c | Michael Hanselmann | "Change the priority of jobs"),
|
535 | 7a1ecaed | Iustin Pop | } |
536 | 7a1ecaed | Iustin Pop | |
537 | 7a1ecaed | Iustin Pop | |
538 | 029fe503 | Guido Trotter | #: dictionary with aliases for commands
|
539 | 029fe503 | Guido Trotter | aliases = { |
540 | 029fe503 | Guido Trotter | "show": "info", |
541 | 029fe503 | Guido Trotter | } |
542 | 029fe503 | Guido Trotter | |
543 | 029fe503 | Guido Trotter | |
544 | a09b9e3d | Michael Hanselmann | def Main(): |
545 | 029fe503 | Guido Trotter | return GenericMain(commands, aliases=aliases) |