]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/orchestrator: Add JSON output to CLI commands 25340/head
authorSebastian Wagner <sebastian.wagner@suse.com>
Fri, 30 Nov 2018 15:12:08 +0000 (16:12 +0100)
committerSebastian Wagner <sebastian.wagner@suse.com>
Tue, 18 Dec 2018 15:29:01 +0000 (16:29 +0100)
`device ls`, `service ls` and `service status` now support `--format json`

Plus some minor refactoring:

* `s/node/host/`
* Merged `_list_services` and `_service_status`.

Signed-off-by: Sebastian Wagner <sebastian.wagner@suse.com>
src/pybind/mgr/orchestrator.py
src/pybind/mgr/orchestrator_cli/module.py

index 48007c1d305b42fd66a2ff7191598c7eb6edc44f..9eca2b5c4428dc6bce08a9d38a4198c56021ddbd 100644 (file)
@@ -365,6 +365,20 @@ class ServiceDescription(object):
         # Service status description when status == -1.
         self.status_desc = None
 
+    def to_json(self):
+        out = {
+            'nodename': self.nodename,
+            'container_id': self.container_id,
+            'daemon_name': self.daemon_name,
+            'service_type': self.service_type,
+            'version': self.version,
+            'rados_config_location': self.rados_config_location,
+            'service_url': self.service_url,
+            'status': self.status,
+            'status_desc': self.status_desc,
+        }
+        return {k:v for (k,v) in out.items() if v is not None}
+
 
 class DriveGroupSpec(object):
     """
@@ -476,9 +490,19 @@ class InventoryDevice(object):
         # how much space is available.
         self.metadata_space_free = None
 
+    def to_json(self):
+        return dict(type=self.type, blank=self.blank, id=self.id, size=self.size, **self.extended)
+
 
 class InventoryNode(object):
+    """
+    When fetching inventory, all Devices are groups inside of an
+    InventoryNode.
+    """
     def __init__(self, name, devices):
         assert isinstance(devices, list)
         self.name = name  # unique within cluster.  For example a hostname.
-        self.devices = devices  # list of InventoryDevice
+        self.devices = devices  # type: List[InventoryDevice]
+
+    def to_json(self):
+       return {'name': self.name, 'devices': [d.to_json() for d in self.devices]}
index 0dab0d0d5b7ccbdac68ec65e8529f7d702b52909..3374b53493ed71c10bdb3b9bc69397308c846537 100644 (file)
@@ -1,4 +1,5 @@
 import errno
+import json
 import time
 from mgr_module import MgrModule, HandleCommandResult
 
@@ -16,21 +17,26 @@ class OrchestratorCli(MgrModule):
     COMMANDS = [
         {
             'cmd': "orchestrator device ls "
-                   "name=node,type=CephString,req=false",
+                   "name=host,type=CephString,req=false"
+                   "name=format,type=CephChoices,strings=json|plain,req=false ",
             "desc": "List devices on a node",
             "perm": "r"
         },
         {
             'cmd': "orchestrator service ls "
                    "name=host,type=CephString,req=false "
-                   "name=type,type=CephString,req=false ",
+                   "name=svc_type,type=CephString,req=false "
+                   "name=svc_id,type=CephString,req=false "
+                   "name=format,type=CephChoices,strings=json|plain,req=false ",
             "desc": "List services known to orchestrator" ,
             "perm": "r"
         },
         {
             'cmd': "orchestrator service status "
+                   "name=host,type=CephString,req=false "
                    "name=svc_type,type=CephString "
-                   "name=svc_id,type=CephString ",
+                   "name=svc_id,type=CephString "
+                   "name=format,type=CephChoices,strings=json|plain,req=false ",
             "desc": "Get orchestrator state for Ceph service",
             "perm": "r"
         },
@@ -117,11 +123,11 @@ class OrchestratorCli(MgrModule):
         date hardware inventory is fine as long as hardware ultimately appears
         in the output of this command.
         """
-        node = cmd.get('node', None)
+        host = cmd.get('host', None)
 
-        if node:
+        if host:
             nf = orchestrator.InventoryFilter()
-            nf.nodes = [node]
+            nf.nodes = [host]
         else:
             nf = None
 
@@ -129,31 +135,43 @@ class OrchestratorCli(MgrModule):
 
         self._wait([completion])
 
-        # Spit out a human readable version
-        result = ""
-
-        for inventory_node in completion.result:
-            result += "{0}:\n".format(inventory_node.name)
-            for d in inventory_node.devices:
-                result += "  {0} ({1}, {2}b)\n".format(
-                    d.id, d.type, d.size)
-            result += "\n"
+        if cmd.get('format', 'plain') == 'json':
+            data = [n.to_json() for n in completion.result]
+            return HandleCommandResult(odata=json.dumps(data))
+        else:
+            # Return a human readable version
+            result = ""
+            for inventory_node in completion.result:
+                result += "{0}:\n".format(inventory_node.name)
+                for d in inventory_node.devices:
+                    result += "  {0} ({1}, {2}b)\n".format(
+                        d.id, d.type, d.size)
+                result += "\n"
 
-        return HandleCommandResult(odata=result)
+            return HandleCommandResult(odata=result)
 
     def _list_services(self, cmd):
         hostname = cmd.get('host', None)
-        service_type = cmd.get('type', None)
+        svc_id = cmd.get('svc_id', None)
+        svc_type = cmd.get('svc_type', None)
+        # XXX this is kind of confusing for people because in the orchestrator
+        # context the service ID for MDS is the filesystem ID, not the daemon ID
+
+        completion = self._oremote("describe_service", svc_type, svc_id, hostname)
 
-        completion = self._oremote("describe_service", service_type, None, hostname)
         self._wait([completion])
+
         services = completion.result
 
+        # Sort the list for display
+        services.sort(key=lambda s: (s.service_type, s.nodename, s.daemon_name))
+
         if len(services) == 0:
             return HandleCommandResult(rs="No services reported")
+        elif cmd.get('format', 'plain') == 'json':
+            data = [s.to_json() for s in services]
+            return HandleCommandResult(odata=json.dumps(data))
         else:
-            # Sort the list for display
-            services.sort(key = lambda s: (s.service_type, s.nodename, s.daemon_name))
             lines = []
             for s in services:
                 lines.append("{0}.{1} {2} {3} {4} {5}".format(
@@ -166,32 +184,6 @@ class OrchestratorCli(MgrModule):
 
             return HandleCommandResult(odata="\n".join(lines))
 
-    def _service_status(self, cmd):
-        svc_type = cmd['svc_type']
-        svc_id = cmd['svc_id']
-
-        # XXX this is kind of confusing for people because in the orchestrator
-        # context the service ID for MDS is the filesystem ID, not the daemon ID
-
-        completion = self._oremote("describe_service", svc_type, svc_id, None)
-
-        self._wait([completion])
-
-        service_list = completion.result
-
-        if len(service_list) == 0:
-            return HandleCommandResult(rs="No locations reported")
-        else:
-            lines = []
-            for l in service_list:
-                lines.append("{0}.{1} {2} {3}".format(
-                    svc_type,
-                    l.daemon_name,
-                    l.nodename,
-                    l.container_id))
-
-            return HandleCommandResult(odata="\n".join(lines))
-
     def _service_add(self, cmd):
         svc_type = cmd['svc_type']
         if svc_type == "osd":
@@ -278,7 +270,9 @@ class OrchestratorCli(MgrModule):
             enabled = module['name'] in mgr_map['modules']
             if not enabled:
                 return HandleCommandResult(-errno.EINVAL,
-                                           rs="Module '{0}' is not enabled".format(module_name))
+                                           rs="Module '{module_name}' is not enabled. \n Run `ceph "
+                                              "mgr module enable {module_name}` "
+                                              "to enable.".format(module_name=module_name))
 
             try:
                 is_orchestrator = self.remote(module_name,
@@ -329,7 +323,7 @@ class OrchestratorCli(MgrModule):
         elif cmd['prefix'] == "orchestrator service ls":
             return self._list_services(cmd)
         elif cmd['prefix'] == "orchestrator service status":
-            return self._service_status(cmd)
+            return self._list_services(cmd)  # TODO: create more detailed output
         elif cmd['prefix'] == "orchestrator service add":
             return self._service_add(cmd)
         elif cmd['prefix'] == "orchestrator service rm":
@@ -340,3 +334,4 @@ class OrchestratorCli(MgrModule):
             return self._status()
         else:
             raise NotImplementedError()
+