From 60dd1473fbe8b37b1455430edffdc1ba07cd6b4f Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Mon, 29 Sep 2008 13:09:34 +0000
Subject: [PATCH] Implement job summary in gnt-job list
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

It is not currently possibly to show a summary of the job in the output
of β€œgnt-job list”. The closes is listing the whole opcode(s), but that
is too verbose. Also, the default output (id, status) is not very
useful, unless one looks for (and knows about) an exact job ID.

The patch adds a β€œsummary” description of a job composed of the list of
OP_ID of the individual opcodes. Moreover, if an opcode has a β€˜logical’
target in a certain opcode field (e.g. start instance has the instance
name as the target), then it is included in the formatting also. It's
easier to explain via a sample output:

gnt-job list
ID Status  Summary
1  error   NODE_QUERY
2  success NODE_ADD(gnta2)
3  success CLUSTER_QUERY
4  success NODE_REMOVE(gnta2.example.com)
5  error   NODE_QUERY
6  success NODE_ADD(gnta2)
7  success NODE_QUERY
8  success OS_DIAGNOSE
9  success INSTANCE_CREATE(instance1.example.com)
10 success INSTANCE_REMOVE(instance1.example.com)
11 error   INSTANCE_CREATE(instance1.example.com)
12 success INSTANCE_CREATE(instance1.example.com)
13 success INSTANCE_SHUTDOWN(instance1.example.com)
14 success INSTANCE_ACTIVATE_DISKS(instance1.example.com)
15 error   INSTANCE_CREATE(instance2.example.com)
16 error   INSTANCE_CREATE(instance2.example.com)
17 success INSTANCE_CREATE(instance2.example.com)
18 success INSTANCE_ACTIVATE_DISKS(instance1.example.com)
19 success INSTANCE_ACTIVATE_DISKS(instance2.example.com)
20 success INSTANCE_SHUTDOWN(instance1.example.com)
21 success INSTANCE_SHUTDOWN(instance2.example.com)

This is done by a simple change to the opcode classes, which allows an
opcode to format itself. The additional function is small enough that it
can go in opcodes.py, where it could also be used by a client if needed.

Reviewed-by: imsnah
---
 lib/jqueue.py   |  2 ++
 lib/opcodes.py  | 34 ++++++++++++++++++++++++++++++++++
 scripts/gnt-job |  5 ++++-
 3 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/lib/jqueue.py b/lib/jqueue.py
index b44b8636d..2ae424cd7 100644
--- a/lib/jqueue.py
+++ b/lib/jqueue.py
@@ -696,6 +696,8 @@ class JobQueue(object):
         row.append([op.result for op in job.ops])
       elif fname == "opstatus":
         row.append([op.status for op in job.ops])
+      elif fname == "summary":
+        row.append([op.input.Summary() for op in job.ops])
       else:
         raise errors.OpExecError("Invalid job query field '%s'" % fname)
     return row
diff --git a/lib/opcodes.py b/lib/opcodes.py
index 2984185a8..689ccb468 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -158,6 +158,18 @@ class OpCode(BaseOpCode):
     op.__setstate__(new_data)
     return op
 
+  def Summary(self):
+    """Generates a summary description of this opcode.
+
+    """
+    # all OP_ID start with OP_, we remove that
+    txt = self.OP_ID[3:]
+    field_name = getattr(self, "OP_DSC_FIELD", None)
+    if field_name:
+      field_value = getattr(self, field_name, None)
+      txt = "%s(%s)" % (txt, field_value)
+    return txt
+
 
 class OpDestroyCluster(OpCode):
   """Destroy the cluster.
@@ -231,6 +243,7 @@ class OpRenameCluster(OpCode):
 
   """
   OP_ID = "OP_CLUSTER_RENAME"
+  OP_DSC_FIELD = "name"
   __slots__ = ["name"]
 
 
@@ -256,6 +269,7 @@ class OpRemoveNode(OpCode):
 
   """
   OP_ID = "OP_NODE_REMOVE"
+  OP_DSC_FIELD = "node_name"
   __slots__ = ["node_name"]
 
 
@@ -282,6 +296,7 @@ class OpAddNode(OpCode):
 
   """
   OP_ID = "OP_NODE_ADD"
+  OP_DSC_FIELD = "node_name"
   __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"]
 
 
@@ -302,6 +317,7 @@ class OpQueryNodeVolumes(OpCode):
 class OpCreateInstance(OpCode):
   """Create an instance."""
   OP_ID = "OP_INSTANCE_CREATE"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = [
     "instance_name", "mem_size", "disk_size", "os_type", "pnode",
     "disk_template", "snode", "swap_size", "mode",
@@ -317,12 +333,14 @@ class OpCreateInstance(OpCode):
 class OpReinstallInstance(OpCode):
   """Reinstall an instance's OS."""
   OP_ID = "OP_INSTANCE_REINSTALL"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name", "os_type"]
 
 
 class OpRemoveInstance(OpCode):
   """Remove an instance."""
   OP_ID = "OP_INSTANCE_REMOVE"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name", "ignore_failures"]
 
 
@@ -335,18 +353,21 @@ class OpRenameInstance(OpCode):
 class OpStartupInstance(OpCode):
   """Startup an instance."""
   OP_ID = "OP_INSTANCE_STARTUP"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name", "force", "extra_args"]
 
 
 class OpShutdownInstance(OpCode):
   """Shutdown an instance."""
   OP_ID = "OP_INSTANCE_SHUTDOWN"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name"]
 
 
 class OpRebootInstance(OpCode):
   """Reboot an instance."""
   OP_ID = "OP_INSTANCE_REBOOT"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name", "reboot_type", "extra_args",
                "ignore_secondaries" ]
 
@@ -354,30 +375,35 @@ class OpRebootInstance(OpCode):
 class OpReplaceDisks(OpCode):
   """Replace the disks of an instance."""
   OP_ID = "OP_INSTANCE_REPLACE_DISKS"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name", "remote_node", "mode", "disks", "iallocator"]
 
 
 class OpFailoverInstance(OpCode):
   """Failover an instance."""
   OP_ID = "OP_INSTANCE_FAILOVER"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name", "ignore_consistency"]
 
 
 class OpConnectConsole(OpCode):
   """Connect to an instance's console."""
   OP_ID = "OP_INSTANCE_CONSOLE"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name"]
 
 
 class OpActivateInstanceDisks(OpCode):
   """Activate an instance's disks."""
   OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name"]
 
 
 class OpDeactivateInstanceDisks(OpCode):
   """Deactivate an instance's disks."""
   OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name"]
 
 
@@ -396,6 +422,7 @@ class OpQueryInstanceData(OpCode):
 class OpSetInstanceParams(OpCode):
   """Change the parameters of an instance."""
   OP_ID = "OP_INSTANCE_SET_PARAMS"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = [
     "instance_name", "mem", "vcpus", "ip", "bridge", "mac",
     "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
@@ -407,6 +434,7 @@ class OpSetInstanceParams(OpCode):
 class OpGrowDisk(OpCode):
   """Grow a disk of an instance."""
   OP_ID = "OP_INSTANCE_GROW_DISK"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name", "disk", "amount"]
 
 
@@ -427,12 +455,14 @@ class OpQueryExports(OpCode):
 class OpExportInstance(OpCode):
   """Export an instance."""
   OP_ID = "OP_BACKUP_EXPORT"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name", "target_node", "shutdown"]
 
 
 class OpRemoveExport(OpCode):
   """Remove an instance's export."""
   OP_ID = "OP_BACKUP_REMOVE"
+  OP_DSC_FIELD = "instance_name"
   __slots__ = ["instance_name"]
 
 
@@ -440,12 +470,14 @@ class OpRemoveExport(OpCode):
 class OpGetTags(OpCode):
   """Returns the tags of the given object."""
   OP_ID = "OP_TAGS_GET"
+  OP_DSC_FIELD = "name"
   __slots__ = ["kind", "name"]
 
 
 class OpSearchTags(OpCode):
   """Searches the tags in the cluster for a given pattern."""
   OP_ID = "OP_TAGS_SEARCH"
+  OP_DSC_FIELD = "pattern"
   __slots__ = ["pattern"]
 
 
@@ -484,6 +516,7 @@ class OpTestDelay(OpCode):
 
   """
   OP_ID = "OP_TEST_DELAY"
+  OP_DSC_FIELD = "duration"
   __slots__ = ["duration", "on_master", "on_nodes"]
 
 
@@ -499,6 +532,7 @@ class OpTestAllocator(OpCode):
 
   """
   OP_ID = "OP_TEST_ALLOCATOR"
+  OP_DSC_FIELD = "allocator"
   __slots__ = [
     "direction", "mode", "allocator", "name",
     "mem_size", "disks", "disk_template",
diff --git a/scripts/gnt-job b/scripts/gnt-job
index b10afc814..7d561c645 100755
--- a/scripts/gnt-job
+++ b/scripts/gnt-job
@@ -33,7 +33,7 @@ from ganeti import utils
 from ganeti import errors
 
 
-_LIST_DEF_FIELDS = ["id", "status"]
+_LIST_DEF_FIELDS = ["id", "status", "summary"]
 
 _USER_JOB_STATUS = {
   constants.JOB_STATUS_QUEUED: "queued",
@@ -64,6 +64,7 @@ def ListJobs(opts, args):
       "ops": "OpCodes",
       "opresult": "OpCode_result",
       "opstatus": "OpCode_status",
+      "summary": "Summary",
       }
   else:
     headers = None
@@ -81,6 +82,8 @@ def ListJobs(opts, args):
           val = _USER_JOB_STATUS[val]
         else:
           raise errors.ProgrammerError("Unknown job status code '%s'" % val)
+      elif field == "summary":
+        val = ",".join(val)
 
       row[idx] = str(val)
 
-- 
GitLab