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