From 24bdce1f1bfcd3f3dc5954656dfd1869901b7716 Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Thu, 11 Jun 2020 12:00:11 +0200 Subject: [PATCH] mgr/orch: YAML of DaemonDescription readable Signed-off-by: Sebastian Wagner (cherry picked from commit 4c7ad07b20fd5ae125bad34fcebdd84a886432b9) --- src/pybind/mgr/orchestrator/_interface.py | 39 +++++++++----- src/pybind/mgr/orchestrator/module.py | 64 ++++++++++++++++------- 2 files changed, 72 insertions(+), 31 deletions(-) diff --git a/src/pybind/mgr/orchestrator/_interface.py b/src/pybind/mgr/orchestrator/_interface.py index 7de5c00d6caa6..e2d543a241cf9 100644 --- a/src/pybind/mgr/orchestrator/_interface.py +++ b/src/pybind/mgr/orchestrator/_interface.py @@ -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, diff --git a/src/pybind/mgr/orchestrator/module.py b/src/pybind/mgr/orchestrator/module.py index 73b64e543678e..0c66f9b68cd49 100644 --- a/src/pybind/mgr/orchestrator/module.py +++ b/src/pybind/mgr/orchestrator/module.py @@ -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'], -- 2.39.5