]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/orch: YAML of DaemonDescription readable
authorSebastian Wagner <sebastian.wagner@suse.com>
Thu, 11 Jun 2020 10:00:11 +0000 (12:00 +0200)
committerSebastian Wagner <sebastian.wagner@suse.com>
Tue, 14 Jul 2020 09:39:06 +0000 (11:39 +0200)
Signed-off-by: Sebastian Wagner <sebastian.wagner@suse.com>
(cherry picked from commit 4c7ad07b20fd5ae125bad34fcebdd84a886432b9)

src/pybind/mgr/orchestrator/_interface.py
src/pybind/mgr/orchestrator/module.py

index 7de5c00d6caa6366341308afaf13f252083d43e4..e2d543a241cf9b8275547453777bb5aa92e6a5b4 100644 (file)
@@ -14,9 +14,11 @@ import re
 import time
 import uuid
 
-from collections import namedtuple
+from collections import namedtuple, OrderedDict
 from functools import wraps
 
+import yaml
+
 from ceph.deployment import inventory
 from ceph.deployment.service_spec import ServiceSpec, NFSServiceSpec, RGWSpec, \
     ServiceSpecValidationError, IscsiServiceSpec
@@ -1321,22 +1323,26 @@ class DaemonDescription(object):
                                                          id=self.daemon_id)
 
     def to_json(self):
-        out = {
-            'hostname': self.hostname,
-            'container_id': self.container_id,
-            'container_image_id': self.container_image_id,
-            'container_image_name': self.container_image_name,
-            'daemon_id': self.daemon_id,
-            'daemon_type': self.daemon_type,
-            'version': self.version,
-            'status': self.status,
-            'status_desc': self.status_desc,
-        }
+        out = OrderedDict()
+        out['daemon_type'] = self.daemon_type
+        out['daemon_id'] = self.daemon_id
+        out['hostname'] = self.hostname
+        out['container_id'] = self.container_id
+        out['container_image_id'] = self.container_image_id
+        out['container_image_name'] = self.container_image_name
+        out['version'] = self.version
+        out['status'] = self.status
+        out['status_desc'] = self.status_desc
+
         for k in ['last_refresh', 'created', 'started', 'last_deployed',
                   'last_configured']:
             if getattr(self, k):
                 out[k] = getattr(self, k).strftime(DATEFMT)
-        return {k: v for (k, v) in out.items() if v is not None}
+
+        empty = [k for k, v in out.items() if v is None]
+        for e in empty:
+            del out[e]
+        return out
 
     @classmethod
     @handle_type_error
@@ -1352,6 +1358,13 @@ class DaemonDescription(object):
         # feel free to change this:
         return DaemonDescription.from_json(self.to_json())
 
+    @staticmethod
+    def yaml_representer(dumper: 'yaml.SafeDumper', data: 'DaemonDescription'):
+        return dumper.represent_dict(data.to_json().items())
+
+
+yaml.add_representer(DaemonDescription, DaemonDescription.yaml_representer)
+
 class ServiceDescription(object):
     """
     For responding to queries about the status of a particular service,
index 73b64e543678e6af1770ccaa31fcf61d12034537..0c66f9b68cd496eca479726658ca10c44c0b19e3 100644 (file)
@@ -30,13 +30,44 @@ def nice_delta(now, t, suffix=''):
         return '-'
 
 
-def to_format(what, format):
+def to_format(what, format: str, many: bool, cls):
+    def to_json_1(obj):
+        if hasattr(obj, 'to_json'):
+            return obj.to_json()
+        return obj
+
+    def to_json_n(objs):
+        return [to_json_1(o) for o in objs]
+
+    to_json = to_json_n if many else to_json_1
+
     if format == 'json':
-        return json.dumps(what, sort_keys=True)
+        return json.dumps(to_json(what), sort_keys=True)
     elif format == 'json-pretty':
-        return json.dumps(what, indent=2, sort_keys=True)
+        return json.dumps(to_json(what), indent=2, sort_keys=True)
     elif format == 'yaml':
-        return yaml.safe_dump_all(what, default_flow_style=False)
+        # fun with subinterpreters again. pyyaml depends on object identity.
+        # as what originates from a different subinterpreter we have to copy things here.
+        if cls:
+            flat = to_json(what)
+            copy = [cls.from_json(o) for o in flat] if many else cls.from_json(flat)
+        else:
+            copy = what
+
+        def to_yaml_1(obj):
+            if hasattr(obj, 'yaml_representer'):
+                return obj
+            return to_json_1(obj)
+
+        def to_yaml_n(objs):
+            return [to_yaml_1(o) for o in objs]
+
+        to_yaml = to_yaml_n if many else to_yaml_1
+
+        if many:
+            return yaml.dump_all(to_yaml(copy), default_flow_style=False)
+        return yaml.dump(to_yaml(copy), default_flow_style=False)
+
 
 
 @six.add_metaclass(CLICommandMeta)
@@ -216,9 +247,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule):
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         if format != 'plain':
-            hosts = [host.to_json()
-                     for host in completion.result]
-            output = to_format(hosts, format)
+            output = to_format(completion.result, format, many=True, cls=HostSpec)
         else:
             table = PrettyTable(
                 ['HOST', 'ADDR', 'LABELS', 'STATUS'],
@@ -276,8 +305,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule):
         raise_if_exception(completion)
 
         if format != 'plain':
-            data = [n.to_json() for n in completion.result]
-            return HandleCommandResult(stdout=to_format(data, format))
+            return HandleCommandResult(stdout=to_format(completion.result, format, many=True, cls=InventoryHost))
         else:
             out = []
 
@@ -349,10 +377,10 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule):
             return HandleCommandResult(stdout="No services reported")
         elif format != 'plain':
             if export:
-                data = [s.spec.to_json() for s in services]
+                data = [s.spec for s in services]
+                return HandleCommandResult(stdout=to_format(data, format, many=True, cls=ServiceSpec))
             else:
-                data = [s.to_json() for s in services]
-            return HandleCommandResult(stdout=to_format(data, format))
+                return HandleCommandResult(stdout=to_format(services, format, many=True, cls=ServiceDescription))
         else:
             now = datetime.datetime.utcnow()
             table = PrettyTable(
@@ -413,12 +441,12 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule):
         # Sort the list for display
         daemons.sort(key=lambda s: (ukn(s.daemon_type), ukn(s.hostname), ukn(s.daemon_id)))
 
-        if len(daemons) == 0:
-            return HandleCommandResult(stdout="No daemons reported")
-        elif format != 'plain':
-            data = [s.to_json() for s in daemons]
-            return HandleCommandResult(stdout=to_format(data, format))
+        if format != 'plain':
+            return HandleCommandResult(stdout=to_format(daemons, format, many=True, cls=DaemonDescription))
         else:
+            if len(daemons) == 0:
+                return HandleCommandResult(stdout="No daemons reported")
+
             now = datetime.datetime.utcnow()
             table = PrettyTable(
                 ['NAME', 'HOST', 'STATUS', 'REFRESHED', 'AGE',
@@ -536,7 +564,7 @@ Examples:
 
         def print_preview(previews, format_to):
             if format != 'plain':
-                return to_format(previews, format_to)
+                return to_format(previews, format_to, many=False, cls=None)
             else:
                 table = PrettyTable(
                     ['NAME', 'HOST', 'DATA', 'DB', 'WAL'],