From e5933d5e47fb2e2b77f37678ce770a1887d54c08 Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Wed, 12 Feb 2020 16:21:05 +0100 Subject: [PATCH] mgr/orchestrator: Use CLICommand, except it's global variable `CLICommand.COMMANDS` is a global varialbe that prevents anyone from importing other modules, as the `COMMANS` are then merged together. Let's use a meta class instead of a global variable. Signed-off-by: Sebastian Wagner --- src/pybind/mgr/cephadm/module.py | 4 ++- src/pybind/mgr/mgr_module.py | 13 +++++--- src/pybind/mgr/orchestrator/__init__.py | 2 +- src/pybind/mgr/orchestrator/_interface.py | 38 +++++++++++++++++++++-- src/pybind/mgr/orchestrator/module.py | 7 +++-- 5 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py index 66d9ccad198..a64d046315e 100644 --- a/src/pybind/mgr/cephadm/module.py +++ b/src/pybind/mgr/cephadm/module.py @@ -30,7 +30,8 @@ from ceph.deployment.drive_selection import selector from mgr_module import MgrModule import mgr_util import orchestrator -from orchestrator import OrchestratorError, HostPlacementSpec, OrchestratorValidationError, HostSpec +from orchestrator import OrchestratorError, HostPlacementSpec, OrchestratorValidationError, HostSpec, \ + CLICommandMeta from . import remotes @@ -284,6 +285,7 @@ def with_daemons(daemon_type=None, return wrapper return decorator +@six.add_metaclass(CLICommandMeta) class CephadmOrchestrator(MgrModule, orchestrator.OrchestratorClientMixin): _STORE_HOST_PREFIX = "host" diff --git a/src/pybind/mgr/mgr_module.py b/src/pybind/mgr/mgr_module.py index 8cd4bbe3924..1681e7c998a 100644 --- a/src/pybind/mgr/mgr_module.py +++ b/src/pybind/mgr/mgr_module.py @@ -308,13 +308,16 @@ class CLICommand(object): assert self.func return self.func(mgr, **kwargs) + def dump_cmd(self): + return { + 'cmd': '{} {}'.format(self.prefix, self.args), + 'desc': self.desc, + 'perm': self.perm + } + @classmethod def dump_cmd_list(cls): - return [{ - 'cmd': '{} {}'.format(cmd.prefix, cmd.args), - 'desc': cmd.desc, - 'perm': cmd.perm - } for _, cmd in cls.COMMANDS.items()] + return [cmd.dump_cmd() for cmd in cls.COMMANDS.values()] def CLIReadCommand(prefix, args="", desc=""): diff --git a/src/pybind/mgr/orchestrator/__init__.py b/src/pybind/mgr/orchestrator/__init__.py index 946ddb05867..857d4b5ffc0 100644 --- a/src/pybind/mgr/orchestrator/__init__.py +++ b/src/pybind/mgr/orchestrator/__init__.py @@ -5,7 +5,7 @@ from .module import OrchestratorCli # usage: E.g. `from orchestrator import StatelessServiceSpec` from ._interface import \ Completion, TrivialReadCompletion, raise_if_exception, ProgressReference, pretty_print, _Promise, \ - CLICommand, _cli_write_command, _cli_read_command, \ + CLICommand, _cli_write_command, _cli_read_command, CLICommandMeta, \ Orchestrator, OrchestratorClientMixin, \ OrchestratorValidationError, OrchestratorError, NoOrchestrator, \ ServiceSpec, NFSServiceSpec, RGWSpec, HostPlacementSpec, \ diff --git a/src/pybind/mgr/orchestrator/_interface.py b/src/pybind/mgr/orchestrator/_interface.py index 1a911464726..8901c4a9c1f 100644 --- a/src/pybind/mgr/orchestrator/_interface.py +++ b/src/pybind/mgr/orchestrator/_interface.py @@ -11,7 +11,7 @@ import pickle import sys import time from collections import namedtuple -from functools import wraps +from functools import wraps, partial import uuid import string import random @@ -29,7 +29,7 @@ from mgr_util import format_bytes try: from ceph.deployment.drive_group import DriveGroupSpec from typing import TypeVar, Generic, List, Optional, Union, Tuple, Iterator, Callable, Any, \ - Type, Sequence + Type, Sequence, Dict except ImportError: pass @@ -148,7 +148,13 @@ def handle_exception(prefix, cmd_args, desc, perm, func): msg = 'This Orchestrator does not support `{}`'.format(prefix) return HandleCommandResult(-errno.ENOENT, stderr=msg) - return CLICommand(prefix, cmd_args, desc, perm)(wrapper) + # misuse partial to copy `wrapper` + wrapper_copy = partial(wrapper) + wrapper_copy._prefix = prefix # type: ignore + wrapper_copy._cli_command = CLICommand(prefix, cmd_args, desc, perm) # type: ignore + wrapper_copy._cli_command.func = wrapper_copy # type: ignore + + return wrapper_copy def _cli_command(perm): @@ -161,6 +167,32 @@ _cli_read_command = _cli_command('r') _cli_write_command = _cli_command('rw') +class CLICommandMeta(type): + """ + This is a workaround for the use of a global variable CLICommand.COMMANDS which + prevents modules from importing any other module. + + We make use of CLICommand, except for the use of the global variable. + """ + def __init__(cls, name, bases, dct): + super(CLICommandMeta, cls).__init__(name, bases, dct) + dispatch = {} # type: Dict[str, CLICommand] + for v in dct.values(): + try: + dispatch[v._prefix] = v._cli_command + except AttributeError: + pass + + def handle_command(self, inbuf, cmd): + if cmd['prefix'] not in dispatch: + return self.handle_command(inbuf, cmd) + + return dispatch[cmd['prefix']].call(self, cmd, inbuf) + + cls.COMMANDS = [cmd.dump_cmd() for cmd in dispatch.values()] + cls.handle_command = handle_command + + def _no_result(): return object() diff --git a/src/pybind/mgr/orchestrator/module.py b/src/pybind/mgr/orchestrator/module.py index 277743fb7a0..7dd39931973 100644 --- a/src/pybind/mgr/orchestrator/module.py +++ b/src/pybind/mgr/orchestrator/module.py @@ -3,13 +3,15 @@ import errno import json import yaml +import six + from ceph.deployment.inventory import Device from prettytable import PrettyTable from mgr_util import format_bytes, to_pretty_timedelta try: - from typing import List, Set, Optional + from typing import List, Set, Optional, Dict except ImportError: pass # just for type checking. @@ -21,9 +23,10 @@ from mgr_module import MgrModule, HandleCommandResult from ._interface import OrchestratorClientMixin, DeviceLightLoc, _cli_read_command, \ raise_if_exception, _cli_write_command, TrivialReadCompletion, OrchestratorError, \ NoOrchestrator, ServiceSpec, PlacementSpec, OrchestratorValidationError, NFSServiceSpec, \ - RGWSpec, InventoryFilter, InventoryNode, HostPlacementSpec, HostSpec + RGWSpec, InventoryFilter, InventoryNode, HostPlacementSpec, HostSpec, CLICommandMeta +@six.add_metaclass(CLICommandMeta) class OrchestratorCli(OrchestratorClientMixin, MgrModule): MODULE_OPTIONS = [ { -- 2.39.5