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
return wrapper
return decorator
+@six.add_metaclass(CLICommandMeta)
class CephadmOrchestrator(MgrModule, orchestrator.OrchestratorClientMixin):
_STORE_HOST_PREFIX = "host"
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=""):
# 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, \
import sys
import time
from collections import namedtuple
-from functools import wraps
+from functools import wraps, partial
import uuid
import string
import random
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
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):
_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()
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.
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 = [
{