From: Sebastian Wagner Date: Fri, 30 Nov 2018 15:12:08 +0000 (+0100) Subject: mgr/orchestrator: Add JSON output to CLI commands X-Git-Tag: v14.1.0~577^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=aabf9140d1d5b447fd01811a533504f8711bb756;p=ceph-ci.git mgr/orchestrator: Add JSON output to CLI commands `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 --- diff --git a/src/pybind/mgr/orchestrator.py b/src/pybind/mgr/orchestrator.py index 48007c1d305..9eca2b5c442 100644 --- a/src/pybind/mgr/orchestrator.py +++ b/src/pybind/mgr/orchestrator.py @@ -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]} diff --git a/src/pybind/mgr/orchestrator_cli/module.py b/src/pybind/mgr/orchestrator_cli/module.py index 0dab0d0d5b7..3374b53493e 100644 --- a/src/pybind/mgr/orchestrator_cli/module.py +++ b/src/pybind/mgr/orchestrator_cli/module.py @@ -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() +