Skip to content
Snippets Groups Projects
Commit 60dd1473 authored by Iustin Pop's avatar Iustin Pop
Browse files

Implement job summary in gnt-job list

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
parent 3b87986e
No related branches found
No related tags found
No related merge requests found
...@@ -696,6 +696,8 @@ class JobQueue(object): ...@@ -696,6 +696,8 @@ class JobQueue(object):
row.append([op.result for op in job.ops]) row.append([op.result for op in job.ops])
elif fname == "opstatus": elif fname == "opstatus":
row.append([op.status for op in job.ops]) row.append([op.status for op in job.ops])
elif fname == "summary":
row.append([op.input.Summary() for op in job.ops])
else: else:
raise errors.OpExecError("Invalid job query field '%s'" % fname) raise errors.OpExecError("Invalid job query field '%s'" % fname)
return row return row
......
...@@ -158,6 +158,18 @@ class OpCode(BaseOpCode): ...@@ -158,6 +158,18 @@ class OpCode(BaseOpCode):
op.__setstate__(new_data) op.__setstate__(new_data)
return op 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): class OpDestroyCluster(OpCode):
"""Destroy the cluster. """Destroy the cluster.
...@@ -231,6 +243,7 @@ class OpRenameCluster(OpCode): ...@@ -231,6 +243,7 @@ class OpRenameCluster(OpCode):
""" """
OP_ID = "OP_CLUSTER_RENAME" OP_ID = "OP_CLUSTER_RENAME"
OP_DSC_FIELD = "name"
__slots__ = ["name"] __slots__ = ["name"]
...@@ -256,6 +269,7 @@ class OpRemoveNode(OpCode): ...@@ -256,6 +269,7 @@ class OpRemoveNode(OpCode):
""" """
OP_ID = "OP_NODE_REMOVE" OP_ID = "OP_NODE_REMOVE"
OP_DSC_FIELD = "node_name"
__slots__ = ["node_name"] __slots__ = ["node_name"]
...@@ -282,6 +296,7 @@ class OpAddNode(OpCode): ...@@ -282,6 +296,7 @@ class OpAddNode(OpCode):
""" """
OP_ID = "OP_NODE_ADD" OP_ID = "OP_NODE_ADD"
OP_DSC_FIELD = "node_name"
__slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"] __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"]
...@@ -302,6 +317,7 @@ class OpQueryNodeVolumes(OpCode): ...@@ -302,6 +317,7 @@ class OpQueryNodeVolumes(OpCode):
class OpCreateInstance(OpCode): class OpCreateInstance(OpCode):
"""Create an instance.""" """Create an instance."""
OP_ID = "OP_INSTANCE_CREATE" OP_ID = "OP_INSTANCE_CREATE"
OP_DSC_FIELD = "instance_name"
__slots__ = [ __slots__ = [
"instance_name", "mem_size", "disk_size", "os_type", "pnode", "instance_name", "mem_size", "disk_size", "os_type", "pnode",
"disk_template", "snode", "swap_size", "mode", "disk_template", "snode", "swap_size", "mode",
...@@ -317,12 +333,14 @@ class OpCreateInstance(OpCode): ...@@ -317,12 +333,14 @@ class OpCreateInstance(OpCode):
class OpReinstallInstance(OpCode): class OpReinstallInstance(OpCode):
"""Reinstall an instance's OS.""" """Reinstall an instance's OS."""
OP_ID = "OP_INSTANCE_REINSTALL" OP_ID = "OP_INSTANCE_REINSTALL"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name", "os_type"] __slots__ = ["instance_name", "os_type"]
class OpRemoveInstance(OpCode): class OpRemoveInstance(OpCode):
"""Remove an instance.""" """Remove an instance."""
OP_ID = "OP_INSTANCE_REMOVE" OP_ID = "OP_INSTANCE_REMOVE"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name", "ignore_failures"] __slots__ = ["instance_name", "ignore_failures"]
...@@ -335,18 +353,21 @@ class OpRenameInstance(OpCode): ...@@ -335,18 +353,21 @@ class OpRenameInstance(OpCode):
class OpStartupInstance(OpCode): class OpStartupInstance(OpCode):
"""Startup an instance.""" """Startup an instance."""
OP_ID = "OP_INSTANCE_STARTUP" OP_ID = "OP_INSTANCE_STARTUP"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name", "force", "extra_args"] __slots__ = ["instance_name", "force", "extra_args"]
class OpShutdownInstance(OpCode): class OpShutdownInstance(OpCode):
"""Shutdown an instance.""" """Shutdown an instance."""
OP_ID = "OP_INSTANCE_SHUTDOWN" OP_ID = "OP_INSTANCE_SHUTDOWN"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name"] __slots__ = ["instance_name"]
class OpRebootInstance(OpCode): class OpRebootInstance(OpCode):
"""Reboot an instance.""" """Reboot an instance."""
OP_ID = "OP_INSTANCE_REBOOT" OP_ID = "OP_INSTANCE_REBOOT"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name", "reboot_type", "extra_args", __slots__ = ["instance_name", "reboot_type", "extra_args",
"ignore_secondaries" ] "ignore_secondaries" ]
...@@ -354,30 +375,35 @@ class OpRebootInstance(OpCode): ...@@ -354,30 +375,35 @@ class OpRebootInstance(OpCode):
class OpReplaceDisks(OpCode): class OpReplaceDisks(OpCode):
"""Replace the disks of an instance.""" """Replace the disks of an instance."""
OP_ID = "OP_INSTANCE_REPLACE_DISKS" OP_ID = "OP_INSTANCE_REPLACE_DISKS"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name", "remote_node", "mode", "disks", "iallocator"] __slots__ = ["instance_name", "remote_node", "mode", "disks", "iallocator"]
class OpFailoverInstance(OpCode): class OpFailoverInstance(OpCode):
"""Failover an instance.""" """Failover an instance."""
OP_ID = "OP_INSTANCE_FAILOVER" OP_ID = "OP_INSTANCE_FAILOVER"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name", "ignore_consistency"] __slots__ = ["instance_name", "ignore_consistency"]
class OpConnectConsole(OpCode): class OpConnectConsole(OpCode):
"""Connect to an instance's console.""" """Connect to an instance's console."""
OP_ID = "OP_INSTANCE_CONSOLE" OP_ID = "OP_INSTANCE_CONSOLE"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name"] __slots__ = ["instance_name"]
class OpActivateInstanceDisks(OpCode): class OpActivateInstanceDisks(OpCode):
"""Activate an instance's disks.""" """Activate an instance's disks."""
OP_ID = "OP_INSTANCE_ACTIVATE_DISKS" OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name"] __slots__ = ["instance_name"]
class OpDeactivateInstanceDisks(OpCode): class OpDeactivateInstanceDisks(OpCode):
"""Deactivate an instance's disks.""" """Deactivate an instance's disks."""
OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS" OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name"] __slots__ = ["instance_name"]
...@@ -396,6 +422,7 @@ class OpQueryInstanceData(OpCode): ...@@ -396,6 +422,7 @@ class OpQueryInstanceData(OpCode):
class OpSetInstanceParams(OpCode): class OpSetInstanceParams(OpCode):
"""Change the parameters of an instance.""" """Change the parameters of an instance."""
OP_ID = "OP_INSTANCE_SET_PARAMS" OP_ID = "OP_INSTANCE_SET_PARAMS"
OP_DSC_FIELD = "instance_name"
__slots__ = [ __slots__ = [
"instance_name", "mem", "vcpus", "ip", "bridge", "mac", "instance_name", "mem", "vcpus", "ip", "bridge", "mac",
"kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi", "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
...@@ -407,6 +434,7 @@ class OpSetInstanceParams(OpCode): ...@@ -407,6 +434,7 @@ class OpSetInstanceParams(OpCode):
class OpGrowDisk(OpCode): class OpGrowDisk(OpCode):
"""Grow a disk of an instance.""" """Grow a disk of an instance."""
OP_ID = "OP_INSTANCE_GROW_DISK" OP_ID = "OP_INSTANCE_GROW_DISK"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name", "disk", "amount"] __slots__ = ["instance_name", "disk", "amount"]
...@@ -427,12 +455,14 @@ class OpQueryExports(OpCode): ...@@ -427,12 +455,14 @@ class OpQueryExports(OpCode):
class OpExportInstance(OpCode): class OpExportInstance(OpCode):
"""Export an instance.""" """Export an instance."""
OP_ID = "OP_BACKUP_EXPORT" OP_ID = "OP_BACKUP_EXPORT"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name", "target_node", "shutdown"] __slots__ = ["instance_name", "target_node", "shutdown"]
class OpRemoveExport(OpCode): class OpRemoveExport(OpCode):
"""Remove an instance's export.""" """Remove an instance's export."""
OP_ID = "OP_BACKUP_REMOVE" OP_ID = "OP_BACKUP_REMOVE"
OP_DSC_FIELD = "instance_name"
__slots__ = ["instance_name"] __slots__ = ["instance_name"]
...@@ -440,12 +470,14 @@ class OpRemoveExport(OpCode): ...@@ -440,12 +470,14 @@ class OpRemoveExport(OpCode):
class OpGetTags(OpCode): class OpGetTags(OpCode):
"""Returns the tags of the given object.""" """Returns the tags of the given object."""
OP_ID = "OP_TAGS_GET" OP_ID = "OP_TAGS_GET"
OP_DSC_FIELD = "name"
__slots__ = ["kind", "name"] __slots__ = ["kind", "name"]
class OpSearchTags(OpCode): class OpSearchTags(OpCode):
"""Searches the tags in the cluster for a given pattern.""" """Searches the tags in the cluster for a given pattern."""
OP_ID = "OP_TAGS_SEARCH" OP_ID = "OP_TAGS_SEARCH"
OP_DSC_FIELD = "pattern"
__slots__ = ["pattern"] __slots__ = ["pattern"]
...@@ -484,6 +516,7 @@ class OpTestDelay(OpCode): ...@@ -484,6 +516,7 @@ class OpTestDelay(OpCode):
""" """
OP_ID = "OP_TEST_DELAY" OP_ID = "OP_TEST_DELAY"
OP_DSC_FIELD = "duration"
__slots__ = ["duration", "on_master", "on_nodes"] __slots__ = ["duration", "on_master", "on_nodes"]
...@@ -499,6 +532,7 @@ class OpTestAllocator(OpCode): ...@@ -499,6 +532,7 @@ class OpTestAllocator(OpCode):
""" """
OP_ID = "OP_TEST_ALLOCATOR" OP_ID = "OP_TEST_ALLOCATOR"
OP_DSC_FIELD = "allocator"
__slots__ = [ __slots__ = [
"direction", "mode", "allocator", "name", "direction", "mode", "allocator", "name",
"mem_size", "disks", "disk_template", "mem_size", "disks", "disk_template",
......
...@@ -33,7 +33,7 @@ from ganeti import utils ...@@ -33,7 +33,7 @@ from ganeti import utils
from ganeti import errors from ganeti import errors
_LIST_DEF_FIELDS = ["id", "status"] _LIST_DEF_FIELDS = ["id", "status", "summary"]
_USER_JOB_STATUS = { _USER_JOB_STATUS = {
constants.JOB_STATUS_QUEUED: "queued", constants.JOB_STATUS_QUEUED: "queued",
...@@ -64,6 +64,7 @@ def ListJobs(opts, args): ...@@ -64,6 +64,7 @@ def ListJobs(opts, args):
"ops": "OpCodes", "ops": "OpCodes",
"opresult": "OpCode_result", "opresult": "OpCode_result",
"opstatus": "OpCode_status", "opstatus": "OpCode_status",
"summary": "Summary",
} }
else: else:
headers = None headers = None
...@@ -81,6 +82,8 @@ def ListJobs(opts, args): ...@@ -81,6 +82,8 @@ def ListJobs(opts, args):
val = _USER_JOB_STATUS[val] val = _USER_JOB_STATUS[val]
else: else:
raise errors.ProgrammerError("Unknown job status code '%s'" % val) raise errors.ProgrammerError("Unknown job status code '%s'" % val)
elif field == "summary":
val = ",".join(val)
row[idx] = str(val) row[idx] = str(val)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment