From 7a1ecaed85932102dd42a2f5ef84e07081e1a297 Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Fri, 4 Apr 2008 12:44:20 +0000 Subject: [PATCH] Add a simple gnt-job script This patch adds a very basic gnt-job script that allows job querying. This goes on top of the previous master daemon patches. Currently, because of the not-changed cmd lock, you can't query the jobs as long as a job is running - you have to rm the cmd lock and then you can query the jobs. Reviewed-by: imsnah --- daemons/ganeti-masterd | 13 +++--- lib/jqueue.py | 29 ++++++++++++ scripts/Makefile.am | 2 +- scripts/gnt-job | 101 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 scripts/gnt-job diff --git a/daemons/ganeti-masterd b/daemons/ganeti-masterd index 427f2a431..b18a8bb2b 100644 --- a/daemons/ganeti-masterd +++ b/daemons/ganeti-masterd @@ -166,9 +166,7 @@ class ClientOps: if operation == "submit": return self.put(args) elif operation == "query": - path = args["object"] - if path == "instances": - return self.query(args) + return self.query(args) else: raise ValueError("Invalid operation") @@ -183,6 +181,9 @@ class ClientOps: names = args["names"] if path == "instances": opclass = opcodes.OpQueryInstances + elif path == "jobs": + # early exit because job query-ing is special (not via opcodes) + return self.query_jobs(fields, names) else: raise ValueError("Invalid object %s" % path) @@ -191,10 +192,8 @@ class ClientOps: result = cpu.ExecOpCode(op) return result - def query_job(self, rid): - rid = int(data) - job = self.server.queue.query(rid) - return job + def query_jobs(self, fields, names): + return self.server.queue.query_jobs(fields, names) def JobRunner(proc, job): diff --git a/lib/jqueue.py b/lib/jqueue.py index 1283710da..e9b0c2910 100644 --- a/lib/jqueue.py +++ b/lib/jqueue.py @@ -25,6 +25,7 @@ import threading import Queue from ganeti import opcodes +from ganeti import errors class JobObject: """In-memory job representation. @@ -90,3 +91,31 @@ class QueueManager: result = self.job_queue.get(rid, None) self.lock.release() return result + + def query_jobs(self, fields, names): + """Query all jobs. + + The fields and names parameters are similar to the ones passed to + the OpQueryInstances. + + """ + result = [] + self.lock.acquire() + try: + for jobj in self.job_queue.itervalues(): + row = [] + jdata = jobj.data + for fname in fields: + if fname == "id": + row.append(jdata.job_id) + elif fname == "status": + row.append(jdata.status) + elif fname == "opcodes": + row.append(",".join([op.OP_ID for op in jdata.op_list])) + else: + raise errors.OpExecError("Invalid job query field '%s'" % + fname) + result.append(row) + finally: + self.lock.release() + return result diff --git a/scripts/Makefile.am b/scripts/Makefile.am index a24b40777..ba191b979 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1 +1 @@ -dist_sbin_SCRIPTS = gnt-instance gnt-cluster gnt-node gnt-os gnt-backup gnt-debug +dist_sbin_SCRIPTS = gnt-instance gnt-cluster gnt-node gnt-os gnt-backup gnt-debug gnt-job diff --git a/scripts/gnt-job b/scripts/gnt-job new file mode 100644 index 000000000..e438f5aed --- /dev/null +++ b/scripts/gnt-job @@ -0,0 +1,101 @@ +#!/usr/bin/python +# + +# Copyright (C) 2006, 2007 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +import sys +import os +import itertools +from optparse import make_option +from cStringIO import StringIO + +from ganeti.cli import * +from ganeti import opcodes +from ganeti import logger +from ganeti import constants +from ganeti import utils +from ganeti import errors + + +def ListJobs(opts, args): + """List the jobs + + """ + if opts.output is None: + selected_fields = ["id", "status"] + else: + selected_fields = opts.output.split(",") + + query = { + "object": "jobs", + "fields": selected_fields, + "names": [], + } + + output = SubmitQuery(query) + if not opts.no_headers: + headers = { + "id": "ID", + "status": "Status", + "opcodes": "OpCodes", + } + else: + headers = None + + # we don't have yet unitfields here + unitfields = None + numfields = ["id"] + + # change raw values to nicer strings + for row in output: + for idx, field in enumerate(selected_fields): + val = row[idx] + if field == "status": + if val == opcodes.Job.STATUS_PENDING: + val = "pending" + elif val == opcodes.Job.STATUS_RUNNING: + val = "running" + elif val == opcodes.Job.STATUS_FINISHED: + val = "finished" + else: + raise errors.ProgrammerError("Unknown job status code '%s'" % val) + + row[idx] = str(val) + + data = GenerateTable(separator=opts.separator, headers=headers, + fields=selected_fields, unitfields=unitfields, + numfields=numfields, data=output) + for line in data: + print line + + return 0 + + +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, opcodes." + " The default field" + " list is (in order): id, status."), + } + + +if __name__ == '__main__': + sys.exit(GenericMain(commands)) -- GitLab