From c95ba878c66aae44816b1451049449685d444f0a Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Mon, 18 Jan 2021 16:02:38 +0100 Subject: [PATCH] mgr/orchestrator: disallow_untyped_defs = True Signed-off-by: Sebastian Wagner --- src/mypy.ini | 3 + src/pybind/mgr/cephadm/inventory.py | 1 + src/pybind/mgr/cephadm/module.py | 14 +- src/pybind/mgr/cephadm/serve.py | 8 +- .../mgr/cephadm/services/cephadmservice.py | 7 + src/pybind/mgr/cephadm/services/ha_rgw.py | 1 + src/pybind/mgr/cephadm/services/iscsi.py | 1 + src/pybind/mgr/cephadm/services/monitoring.py | 9 + src/pybind/mgr/cephadm/services/nfs.py | 1 + src/pybind/mgr/cephadm/upgrade.py | 6 + src/pybind/mgr/orchestrator/_interface.py | 242 +++++++++--------- src/pybind/mgr/orchestrator/module.py | 98 +++---- src/pybind/mgr/test_orchestrator/module.py | 2 +- 13 files changed, 227 insertions(+), 166 deletions(-) diff --git a/src/mypy.ini b/src/mypy.ini index 1c7594bd114e4..45a57327c475c 100755 --- a/src/mypy.ini +++ b/src/mypy.ini @@ -24,6 +24,9 @@ disallow_untyped_defs = True [mypy-cephadm.*] disallow_untyped_defs = True +[mypy-orchestrator.*] +disallow_untyped_defs = True + # Make cephadm and rook happy [mypy-OpenSSL] ignore_missing_imports = True diff --git a/src/pybind/mgr/cephadm/inventory.py b/src/pybind/mgr/cephadm/inventory.py index ebae690483c68..3283957372a73 100644 --- a/src/pybind/mgr/cephadm/inventory.py +++ b/src/pybind/mgr/cephadm/inventory.py @@ -467,6 +467,7 @@ class HostCache(): """Provide a list of the types of daemons on the host""" result = set() for _d, dm in self.daemons[hostname].items(): + assert dm.daemon_type is not None result.add(dm.daemon_type) return list(result) diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py index e0e26ce3f4a62..2885b1875aabb 100644 --- a/src/pybind/mgr/cephadm/module.py +++ b/src/pybind/mgr/cephadm/module.py @@ -1253,8 +1253,11 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, def _host_ok_to_stop(self, hostname: str) -> Tuple[int, str]: self.log.debug("running host-ok-to-stop checks") daemons = self.cache.get_daemons() - daemon_map = defaultdict(lambda: []) + daemon_map: Dict[str, List[str]] = defaultdict(lambda: []) for dd in daemons: + assert dd.hostname is not None + assert dd.daemon_type is not None + assert dd.daemon_id is not None if dd.hostname == hostname: daemon_map[dd.daemon_type].append(dd.daemon_id) @@ -1446,6 +1449,9 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, osd_count = 0 for h, dm in self.cache.get_daemons_with_volatile_status(): for name, dd in dm.items(): + assert dd.hostname is not None + assert dd.daemon_type is not None + if service_type and service_type != dd.daemon_type: continue n: str = dd.service_name() @@ -1619,6 +1625,8 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, @trivial_completion def daemon_action(self, action: str, daemon_name: str, image: Optional[str] = None) -> str: d = self.cache.get_daemon(daemon_name) + assert d.daemon_type is not None + assert d.daemon_id is not None if action == 'redeploy' and self.daemon_is_self(d.daemon_type, d.daemon_id) \ and not self.mgr_service.mgr_map_has_standby(): @@ -1635,6 +1643,9 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, def _schedule_daemon_action(self, daemon_name: str, action: str) -> str: dd = self.cache.get_daemon(daemon_name) + assert dd.daemon_type is not None + assert dd.daemon_id is not None + assert dd.hostname is not None if action == 'redeploy' and self.daemon_is_self(dd.daemon_type, dd.daemon_id) \ and not self.mgr_service.mgr_map_has_standby(): raise OrchestratorError( @@ -2225,6 +2236,7 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, return f"Unable to find OSDs: {osd_ids}" for daemon in to_remove_daemons: + assert daemon.daemon_id is not None try: self.to_remove_osds.enqueue(OSD(osd_id=int(daemon.daemon_id), replace=replace, diff --git a/src/pybind/mgr/cephadm/serve.py b/src/pybind/mgr/cephadm/serve.py index 20a9f94ef4c2e..515a04c0b91f3 100644 --- a/src/pybind/mgr/cephadm/serve.py +++ b/src/pybind/mgr/cephadm/serve.py @@ -592,7 +592,8 @@ class CephadmServe: # remove any? def _ok_to_stop(remove_daemon_hosts: Set[orchestrator.DaemonDescription]) -> bool: daemon_ids = [d.daemon_id for d in remove_daemon_hosts] - r = self.mgr.cephadm_services[service_type].ok_to_stop(daemon_ids) + assert None not in daemon_ids + r = self.mgr.cephadm_services[service_type].ok_to_stop(cast(List[str], daemon_ids)) return not r.retval while remove_daemon_hosts and not _ok_to_stop(remove_daemon_hosts): @@ -602,6 +603,7 @@ class CephadmServe: r = True # NOTE: we are passing the 'force' flag here, which means # we can delete a mon instances data. + assert d.hostname is not None self._remove_daemon(d.name(), d.hostname) if r is None: @@ -615,6 +617,9 @@ class CephadmServe: for dd in daemons: # orphan? spec = self.mgr.spec_store.specs.get(dd.service_name(), None) + assert dd.hostname is not None + assert dd.daemon_type is not None + assert dd.daemon_id is not None if not spec and dd.daemon_type not in ['mon', 'mgr', 'osd']: # (mon and mgr specs should always exist; osds aren't matched # to a service spec) @@ -720,6 +725,7 @@ class CephadmServe: ha_rgw_daemons = self.mgr.cache.get_daemons_by_service(spec.service_name()) for daemon in ha_rgw_daemons: if daemon.hostname in [h.hostname for h in hosts] and daemon.hostname not in add_hosts: + assert daemon.hostname is not None self.mgr.cache.schedule_daemon_action( daemon.hostname, daemon.name(), 'reconfig') return spec diff --git a/src/pybind/mgr/cephadm/services/cephadmservice.py b/src/pybind/mgr/cephadm/services/cephadmservice.py index fca6589833739..b6df0cd9b8e40 100644 --- a/src/pybind/mgr/cephadm/services/cephadmservice.py +++ b/src/pybind/mgr/cephadm/services/cephadmservice.py @@ -235,6 +235,7 @@ class CephadmService(metaclass=ABCMeta): """ Called before the daemon is removed. """ + assert daemon.daemon_type is not None assert self.TYPE == daemon_type_to_service(daemon.daemon_type) logger.debug(f'Pre remove daemon {self.TYPE}.{daemon.daemon_id}') @@ -242,6 +243,7 @@ class CephadmService(metaclass=ABCMeta): """ Called after the daemon is removed. """ + assert daemon.daemon_type is not None assert self.TYPE == daemon_type_to_service(daemon.daemon_type) logger.debug(f'Post remove daemon {self.TYPE}.{daemon.daemon_id}') @@ -314,6 +316,8 @@ class CephService(CephadmService): } def remove_keyring(self, daemon: DaemonDescription) -> None: + assert daemon.daemon_id is not None + assert daemon.hostname is not None daemon_id: str = daemon.daemon_id host: str = daemon.hostname @@ -407,6 +411,7 @@ class MonService(CephService): def pre_remove(self, daemon: DaemonDescription) -> None: super().pre_remove(daemon) + assert daemon.daemon_id is not None daemon_id: str = daemon.daemon_id self._check_safe_to_destroy(daemon_id) @@ -464,6 +469,8 @@ class MgrService(CephService): def get_active_daemon(self, daemon_descrs: List[DaemonDescription]) -> DaemonDescription: for daemon in daemon_descrs: + assert daemon.daemon_type is not None + assert daemon.daemon_id is not None if self.mgr.daemon_is_self(daemon.daemon_type, daemon.daemon_id): return daemon # if no active mgr found, return empty Daemon Desc diff --git a/src/pybind/mgr/cephadm/services/ha_rgw.py b/src/pybind/mgr/cephadm/services/ha_rgw.py index 1635d48a901f7..f8670fdae7978 100644 --- a/src/pybind/mgr/cephadm/services/ha_rgw.py +++ b/src/pybind/mgr/cephadm/services/ha_rgw.py @@ -78,6 +78,7 @@ class HA_RGWService(CephService): rgw_daemons = self.mgr.cache.get_daemons_by_type('rgw') rgw_servers = [] for daemon in rgw_daemons: + assert daemon.hostname is not None rgw_servers.append(self.rgw_server( daemon.name(), resolve_ip(daemon.hostname))) diff --git a/src/pybind/mgr/cephadm/services/iscsi.py b/src/pybind/mgr/cephadm/services/iscsi.py index f20bf2fbb3785..93db4e622ddef 100644 --- a/src/pybind/mgr/cephadm/services/iscsi.py +++ b/src/pybind/mgr/cephadm/services/iscsi.py @@ -90,6 +90,7 @@ class IscsiService(CephService): 'value': "true" }) for dd in daemon_descrs: + assert dd.hostname is not None spec = cast(IscsiServiceSpec, self.mgr.spec_store.specs.get(dd.service_name(), None)) if not spec: diff --git a/src/pybind/mgr/cephadm/services/monitoring.py b/src/pybind/mgr/cephadm/services/monitoring.py index 998acc38951c5..cf6823e2fe120 100644 --- a/src/pybind/mgr/cephadm/services/monitoring.py +++ b/src/pybind/mgr/cephadm/services/monitoring.py @@ -24,6 +24,7 @@ class GrafanaService(CephadmService): prom_services = [] # type: List[str] for dd in self.mgr.cache.get_daemons_by_service('prometheus'): + assert dd.hostname is not None prom_services.append(dd.hostname) deps.append(dd.name()) grafana_data_sources = self.mgr.template.render( @@ -69,6 +70,7 @@ class GrafanaService(CephadmService): def config_dashboard(self, daemon_descrs: List[DaemonDescription]) -> None: # TODO: signed cert dd = self.get_active_daemon(daemon_descrs) + assert dd.hostname is not None service_url = 'https://{}:{}'.format( self._inventory_get_addr(dd.hostname), self.DEFAULT_SERVICE_PORT) self._set_service_url_on_dashboard( @@ -119,6 +121,7 @@ class AlertmanagerService(CephadmService): continue if dd.daemon_id == self.mgr.get_mgr_id(): continue + assert dd.hostname is not None addr = self.mgr.inventory.get_addr(dd.hostname) dashboard_urls.append('%s//%s:%s/' % (proto, addr.split(':')[0], port)) @@ -132,6 +135,7 @@ class AlertmanagerService(CephadmService): peers = [] port = '9094' for dd in self.mgr.cache.get_daemons_by_service('alertmanager'): + assert dd.hostname is not None deps.append(dd.name()) addr = self.mgr.inventory.get_addr(dd.hostname) peers.append(addr.split(':')[0] + ':' + port) @@ -151,6 +155,7 @@ class AlertmanagerService(CephadmService): def config_dashboard(self, daemon_descrs: List[DaemonDescription]) -> None: dd = self.get_active_daemon(daemon_descrs) + assert dd.hostname is not None service_url = 'http://{}:{}'.format(self._inventory_get_addr(dd.hostname), self.DEFAULT_SERVICE_PORT) self._set_service_url_on_dashboard( @@ -194,12 +199,14 @@ class PrometheusService(CephadmService): continue if dd.daemon_id == self.mgr.get_mgr_id(): continue + assert dd.hostname is not None addr = self.mgr.inventory.get_addr(dd.hostname) mgr_scrape_list.append(addr.split(':')[0] + ':' + port) # scrape node exporters nodes = [] for dd in self.mgr.cache.get_daemons_by_service('node-exporter'): + assert dd.hostname is not None deps.append(dd.name()) addr = self.mgr.inventory.get_addr(dd.hostname) nodes.append({ @@ -210,6 +217,7 @@ class PrometheusService(CephadmService): # scrape alert managers alertmgr_targets = [] for dd in self.mgr.cache.get_daemons_by_service('alertmanager'): + assert dd.hostname is not None deps.append(dd.name()) addr = self.mgr.inventory.get_addr(dd.hostname) alertmgr_targets.append("'{}:9093'".format(addr.split(':')[0])) @@ -245,6 +253,7 @@ class PrometheusService(CephadmService): def config_dashboard(self, daemon_descrs: List[DaemonDescription]) -> None: dd = self.get_active_daemon(daemon_descrs) + assert dd.hostname is not None service_url = 'http://{}:{}'.format( self._inventory_get_addr(dd.hostname), self.DEFAULT_SERVICE_PORT) self._set_service_url_on_dashboard( diff --git a/src/pybind/mgr/cephadm/services/nfs.py b/src/pybind/mgr/cephadm/services/nfs.py index 0323b4110d5a4..7ca3723ef820e 100644 --- a/src/pybind/mgr/cephadm/services/nfs.py +++ b/src/pybind/mgr/cephadm/services/nfs.py @@ -156,6 +156,7 @@ class NFSService(CephService): return keyring def remove_rgw_keyring(self, daemon: DaemonDescription) -> None: + assert daemon.daemon_id is not None daemon_id: str = daemon.daemon_id entity: AuthEntity = self.get_auth_entity(f'{daemon_id}-rgw') diff --git a/src/pybind/mgr/cephadm/upgrade.py b/src/pybind/mgr/cephadm/upgrade.py index 3a0f5cf346fcc..a1356b8e749b8 100644 --- a/src/pybind/mgr/cephadm/upgrade.py +++ b/src/pybind/mgr/cephadm/upgrade.py @@ -172,6 +172,8 @@ class CephadmUpgrade: def _wait_for_ok_to_stop(self, s: DaemonDescription) -> bool: # only wait a little bit; the service might go away for something + assert s.daemon_type is not None + assert s.daemon_id is not None tries = 4 while tries > 0: if not self.upgrade_state or self.upgrade_state.paused: @@ -287,6 +289,10 @@ class CephadmUpgrade: daemon_type, d.daemon_id, d.container_image_name, d.container_image_id, d.version)) + assert d.daemon_type is not None + assert d.daemon_id is not None + assert d.hostname is not None + if self.mgr.daemon_is_self(d.daemon_type, d.daemon_id): logger.info('Upgrade: Need to upgrade myself (mgr.%s)' % self.mgr.get_mgr_id()) diff --git a/src/pybind/mgr/orchestrator/_interface.py b/src/pybind/mgr/orchestrator/_interface.py index 8d925ad19b1b7..fe65012285f4a 100644 --- a/src/pybind/mgr/orchestrator/_interface.py +++ b/src/pybind/mgr/orchestrator/_interface.py @@ -18,6 +18,15 @@ from collections import namedtuple, OrderedDict from contextlib import contextmanager from functools import wraps +from typing import TypeVar, Generic, List, Optional, Union, Tuple, Iterator, Callable, Any, \ + Sequence, Dict, cast + +try: + from typing import Protocol # Protocol was added in Python 3.8 +except ImportError: + class Protocol: pass # type: ignore + + import yaml from ceph.deployment import inventory @@ -29,15 +38,11 @@ from ceph.utils import datetime_to_str, str_to_datetime from mgr_module import MgrModule, CLICommand, HandleCommandResult -try: - from typing import TypeVar, Generic, List, Optional, Union, Tuple, Iterator, Callable, Any, \ - Type, Sequence, Dict, cast -except ImportError: - pass logger = logging.getLogger(__name__) T = TypeVar('T') +FuncT = TypeVar('FuncT', bound=Callable[..., Any]) class OrchestratorError(Exception): @@ -52,7 +57,7 @@ class OrchestratorError(Exception): def __init__(self, msg: str, errno: int = -errno.EINVAL, - event_kind_subject: Optional[Tuple[str, str]] = None): + event_kind_subject: Optional[Tuple[str, str]] = None) -> None: super(Exception, self).__init__(msg) self.errno = errno # See OrchestratorEvent.subject @@ -64,7 +69,7 @@ class NoOrchestrator(OrchestratorError): No orchestrator in configured. """ - def __init__(self, msg="No orchestrator configured (try `ceph orch set backend`)"): + def __init__(self, msg: str = "No orchestrator configured (try `ceph orch set backend`)") -> None: super(NoOrchestrator, self).__init__(msg, errno=-errno.ENOENT) @@ -75,7 +80,7 @@ class OrchestratorValidationError(OrchestratorError): @contextmanager -def set_exception_subject(kind, subject, overwrite=False): +def set_exception_subject(kind: str, subject: str, overwrite: bool = False) -> Iterator[None]: try: yield except OrchestratorError as e: @@ -84,9 +89,9 @@ def set_exception_subject(kind, subject, overwrite=False): raise -def handle_exception(prefix, perm, func): +def handle_exception(prefix: str, perm: str, func: FuncT) -> FuncT: @wraps(func) - def wrapper(*args, **kwargs): + def wrapper(*args: Any, **kwargs: Any) -> Any: try: return func(*args, **kwargs) except (OrchestratorError, ServiceSpecValidationError) as e: @@ -105,11 +110,14 @@ def handle_exception(prefix, perm, func): wrapper_copy._cli_command.store_func_metadata(func) # type: ignore wrapper_copy._cli_command.func = wrapper_copy # type: ignore - return wrapper_copy + return cast(FuncT, wrapper_copy) + +class InnerCliCommandCallable(Protocol): + def __call__(self, prefix: str) -> Callable[[FuncT], FuncT]: ... -def _cli_command(perm): - def inner_cli_command(prefix): +def _cli_command(perm: str) -> InnerCliCommandCallable: + def inner_cli_command(prefix: str) -> Callable[[FuncT], FuncT]: return lambda func: handle_exception(prefix, perm, func) return inner_cli_command @@ -125,7 +133,7 @@ class CLICommandMeta(type): We make use of CLICommand, except for the use of the global variable. """ - def __init__(cls, name, bases, dct): + def __init__(cls, name: str, bases: Any, dct: Any) -> None: super(CLICommandMeta, cls).__init__(name, bases, dct) dispatch: Dict[str, CLICommand] = {} for v in dct.values(): @@ -134,7 +142,7 @@ class CLICommandMeta(type): except AttributeError: pass - def handle_command(self, inbuf, cmd): + def handle_command(self: Any, inbuf: Optional[str], cmd: dict) -> Any: if cmd['prefix'] not in dispatch: return self.handle_command(inbuf, cmd) @@ -144,8 +152,8 @@ class CLICommandMeta(type): cls.handle_command = handle_command -def _no_result(): - return object() +def _no_result() -> None: + return object() # type: ignore class _Promise(object): @@ -163,7 +171,7 @@ class _Promise(object): RUNNING = 2 FINISHED = 3 # we have a final result - NO_RESULT: None = _no_result() + NO_RESULT: None = _no_result() # type: ignore ASYNC_RESULT = object() def __init__(self, @@ -191,7 +199,7 @@ class _Promise(object): return getattr(self, '_exception_', None) @_exception.setter - def _exception(self, e): + def _exception(self, e: Exception) -> None: self._exception_ = e try: self._serialized_exception_ = pickle.dumps(e) if e is not None else None @@ -217,7 +225,7 @@ class _Promise(object): def _on_complete(self, val: Optional[Callable]) -> None: self._on_complete_ = val - def __repr__(self): + def __repr__(self) -> str: name = self._name or getattr(self._on_complete, '__name__', '??') if self._on_complete else 'None' val = repr(self._value) if self._value is not self.NO_RESULT else 'NA' @@ -226,7 +234,7 @@ class _Promise(object): next, '_progress_reference', 'NA'), repr(self._next_promise) ) - def pretty_print_1(self): + def pretty_print_1(self) -> str: if self._name: name = self._name elif self._on_complete is None: @@ -273,7 +281,7 @@ class _Promise(object): for p in iter(self._next_promise): p._first_promise = self._first_promise - def _finalize(self, value=NO_RESULT): + def _finalize(self, value: Optional[T] = NO_RESULT) -> None: """ Sets this promise to complete. @@ -322,7 +330,7 @@ class _Promise(object): # asynchronous promise pass - def propagate_to_next(self): + def propagate_to_next(self) -> None: self._state = self.FINISHED logger.debug('finalized {}'.format(repr(self))) if self._next_promise: @@ -344,17 +352,17 @@ class _Promise(object): self._next_promise.fail(e) self._state = self.FINISHED - def __contains__(self, item): + def __contains__(self, item: '_Promise') -> bool: return any(item is p for p in iter(self._first_promise)) - def __iter__(self): + def __iter__(self) -> Iterator['_Promise']: yield self elem = self._next_promise while elem is not None: yield elem elem = elem._next_promise - def _append_promise(self, other): + def _append_promise(self, other: Optional['_Promise']) -> None: if other is not None: assert self not in other assert other not in self @@ -367,7 +375,7 @@ class _Promise(object): class ProgressReference(object): def __init__(self, message: str, - mgr, + mgr: Any, completion: Optional[Callable[[], 'Completion']] = None ): """ @@ -400,23 +408,23 @@ class ProgressReference(object): self._completion_has_result = False self.mgr.all_progress_references.append(self) - def __str__(self): + def __str__(self) -> str: """ ``__str__()`` is used for determining the message for progress events. """ return self.message or super(ProgressReference, self).__str__() - def __call__(self, arg): + def __call__(self, arg: T) -> T: self._completion_has_result = True self.progress = 1.0 return arg @property - def progress(self): + def progress(self) -> float: return self._progress @progress.setter - def progress(self, progress): + def progress(self, progress: float) -> None: assert progress <= 1.0 self._progress = progress try: @@ -434,11 +442,11 @@ class ProgressReference(object): pass @property - def effective(self): + def effective(self) -> bool: return self.progress == 1 and self._completion_has_result - def update(self): - def progress_run(progress): + def update(self) -> None: + def progress_run(progress: float) -> None: self.progress = progress if self.completion: c = self.completion().then(progress_run) @@ -446,7 +454,7 @@ class ProgressReference(object): else: self.progress = 1 - def fail(self): + def fail(self) -> None: self._completion_has_result = True self.progress = 1 @@ -488,7 +496,7 @@ class Completion(_Promise, Generic[T]): value: Any = _Promise.NO_RESULT, on_complete: Optional[Callable] = None, name: Optional[str] = None, - ): + ) -> None: super(Completion, self).__init__(_first_promise, value, on_complete, name) @property @@ -504,8 +512,8 @@ class Completion(_Promise, Generic[T]): as a write completeion. """ - references = [c._progress_reference for c in iter( - self) if c._progress_reference is not None] + references = [c._progress_reference for c in iter( # type: ignore + self) if c._progress_reference is not None] # type: ignore if references: assert len(references) == 1 return references[0] @@ -514,7 +522,7 @@ class Completion(_Promise, Generic[T]): @classmethod def with_progress(cls: Any, message: str, - mgr, + mgr: Any, _first_promise: Optional["Completion"] = None, value: Any = _Promise.NO_RESULT, on_complete: Optional[Callable] = None, @@ -531,9 +539,9 @@ class Completion(_Promise, Generic[T]): def add_progress(self, message: str, - mgr, + mgr: Any, calc_percent: Optional[Callable[[], Any]] = None - ): + ) -> Any: return self.then( on_complete=ProgressReference( message=message, @@ -542,12 +550,12 @@ class Completion(_Promise, Generic[T]): ) ) - def fail(self, e: Exception): + def fail(self, e: Exception) -> None: super(Completion, self).fail(e) if self._progress_reference: self._progress_reference.fail() - def finalize(self, result: Union[None, object, T] = _Promise.NO_RESULT): + def finalize(self, result: Union[None, object, T] = _Promise.NO_RESULT) -> None: if self._first_promise._state == self.INITIALIZED: self._first_promise._finalize(result) @@ -620,7 +628,7 @@ class Completion(_Promise, Generic[T]): """ return self.is_errored or (self.has_result) - def pretty_print(self): + def pretty_print(self) -> str: reprs = '\n'.join(p.pretty_print_1() for p in iter(self._first_promise)) return """<{}>[\n{}\n]""".format(self.__class__.__name__, reprs) @@ -654,11 +662,10 @@ class TrivialReadCompletion(Completion[T]): self.finalize(result) -def _hide_in_features(f): - f._hide_in_features = True +def _hide_in_features(f: FuncT) -> FuncT: + f._hide_in_features = True # type: ignore return f - class Orchestrator(object): """ Calls in this class may do long running remote operations, with time @@ -683,7 +690,7 @@ class Orchestrator(object): """ @_hide_in_features - def is_orchestrator_module(self): + def is_orchestrator_module(self) -> bool: """ Enable other modules to interrogate this module to discover whether it's usable as an orchestrator module. @@ -734,7 +741,7 @@ class Orchestrator(object): raise NotImplementedError() @_hide_in_features - def get_feature_set(self): + def get_feature_set(self) -> Dict[str, dict]: """Describes which methods this orchestrator implements .. note:: @@ -895,17 +902,17 @@ class Orchestrator(object): 'cephadm-exporter': self.apply_cephadm_exporter, } - def merge(ls, r): + def merge(ls: Union[List[T], T], r: Union[List[T], T]) -> List[T]: if isinstance(ls, list): - return ls + [r] - return [ls, r] + return ls + [r] # type: ignore + return [ls, r] # type: ignore spec, *specs = specs fn = cast(Callable[["GenericSpec"], Completion], fns[spec.service_type]) completion = fn(spec) for s in specs: - def next(ls): + def next(ls: list) -> Any: fn = cast(Callable[["GenericSpec"], Completion], fns[spec.service_type]) return fn(s).then(lambda r: merge(ls, r)) completion = completion.then(next) @@ -976,7 +983,7 @@ class Orchestrator(object): def set_unmanaged_flag(self, unmanaged_flag: bool, service_type: str = 'osd', - service_name=None + service_name: Optional[str] = None ) -> HandleCommandResult: raise NotImplementedError() @@ -1224,22 +1231,22 @@ def service_to_daemon_types(stype: str) -> List[str]: class UpgradeStatusSpec(object): # Orchestrator's report on what's going on with any ongoing upgrade - def __init__(self): + def __init__(self) -> None: self.in_progress = False # Is an upgrade underway? - self.target_image = None - self.services_complete = [] # Which daemon types are fully updated? + self.target_image: Optional[str] = None + self.services_complete: List[str] = [] # Which daemon types are fully updated? self.message = "" # Freeform description -def handle_type_error(method): +def handle_type_error(method: FuncT) -> FuncT: @wraps(method) - def inner(cls, *args, **kwargs): + def inner(cls: Any, *args: Any, **kwargs: Any) -> Any: try: return method(cls, *args, **kwargs) except TypeError as e: error_msg = '{}: {}'.format(cls.__name__, e) raise OrchestratorValidationError(error_msg) - return inner + return cast(FuncT, inner) class DaemonDescription(object): @@ -1256,26 +1263,26 @@ class DaemonDescription(object): """ def __init__(self, - daemon_type=None, - daemon_id=None, - hostname=None, - container_id=None, - container_image_id=None, - container_image_name=None, - version=None, - status=None, - status_desc=None, - last_refresh=None, - created=None, - started=None, - last_configured=None, - osdspec_affinity=None, - last_deployed=None, + daemon_type: Optional[str] = None, + daemon_id: Optional[str] = None, + hostname: Optional[str] = None, + container_id: Optional[str] = None, + container_image_id: Optional[str] = None, + container_image_name: Optional[str] = None, + version: Optional[str] = None, + status: Optional[int] = None, + status_desc: Optional[str] = None, + last_refresh: Optional[datetime.datetime] = None, + created: Optional[datetime.datetime] = None, + started: Optional[datetime.datetime] = None, + last_configured: Optional[datetime.datetime] = None, + osdspec_affinity: Optional[str] = None, + last_deployed: Optional[datetime.datetime] = None, events: Optional[List['OrchestratorEvent']] = None, - is_active: bool = False): + is_active: bool = False) -> None: # Host is at the same granularity as InventoryHost - self.hostname: str = hostname + self.hostname: Optional[str] = hostname # Not everyone runs in containers, but enough people do to # justify having the container_id (runtime id) and container_image @@ -1293,7 +1300,7 @@ class DaemonDescription(object): # typically either based on hostnames or on pod names. # This is the in mds., the ID that will appear # in the FSMap/ServiceMap. - self.daemon_id: str = daemon_id + self.daemon_id: Optional[str] = daemon_id # Service version that was deployed self.version = version @@ -1319,19 +1326,24 @@ class DaemonDescription(object): self.is_active = is_active - def name(self): + def name(self) -> str: return '%s.%s' % (self.daemon_type, self.daemon_id) def matches_service(self, service_name: Optional[str]) -> bool: + assert self.daemon_id is not None + assert self.daemon_type is not None if service_name: return (daemon_type_to_service(self.daemon_type) + '.' + self.daemon_id).startswith(service_name + '.') return False - def service_id(self): + def service_id(self) -> str: + assert self.daemon_id is not None + assert self.daemon_type is not None if self.daemon_type == 'osd' and self.osdspec_affinity: return self.osdspec_affinity - def _match(): + def _match() -> str: + assert self.daemon_id is not None err = OrchestratorError("DaemonDescription: Cannot calculate service_id: " f"daemon_id='{self.daemon_id}' hostname='{self.hostname}'") @@ -1376,17 +1388,18 @@ class DaemonDescription(object): return self.daemon_id - def service_name(self): + def service_name(self) -> str: + assert self.daemon_type is not None if daemon_type_to_service(self.daemon_type) in ServiceSpec.REQUIRES_SERVICE_ID: return f'{daemon_type_to_service(self.daemon_type)}.{self.service_id()}' return daemon_type_to_service(self.daemon_type) - def __repr__(self): + def __repr__(self) -> str: return "({type}.{id})".format(type=self.daemon_type, id=self.daemon_id) - def to_json(self): - out = OrderedDict() + def to_json(self) -> dict: + out: Dict[str, Any] = OrderedDict() out['daemon_type'] = self.daemon_type out['daemon_id'] = self.daemon_id out['hostname'] = self.hostname @@ -1415,7 +1428,7 @@ class DaemonDescription(object): @classmethod @handle_type_error - def from_json(cls, data): + def from_json(cls, data: dict) -> 'DaemonDescription': c = data.copy() event_strs = c.pop('events', []) for k in ['last_refresh', 'created', 'started', 'last_deployed', @@ -1425,12 +1438,12 @@ class DaemonDescription(object): events = [OrchestratorEvent.from_json(e) for e in event_strs] return cls(events=events, **c) - def __copy__(self): + def __copy__(self) -> 'DaemonDescription': # feel free to change this: return DaemonDescription.from_json(self.to_json()) @staticmethod - def yaml_representer(dumper: 'yaml.SafeDumper', data: 'DaemonDescription'): + def yaml_representer(dumper: 'yaml.SafeDumper', data: 'DaemonDescription') -> Any: return dumper.represent_dict(data.to_json().items()) @@ -1452,15 +1465,15 @@ class ServiceDescription(object): def __init__(self, spec: ServiceSpec, - container_image_id=None, - container_image_name=None, - rados_config_location=None, - service_url=None, - last_refresh=None, - created=None, - size=0, - running=0, - events: Optional[List['OrchestratorEvent']] = None): + container_image_id: Optional[str] = None, + container_image_name: Optional[str] = None, + rados_config_location: Optional[str] = None, + service_url: Optional[str] = None, + last_refresh: Optional[datetime.datetime] = None, + created: Optional[datetime.datetime] = None, + size: int = 0, + running: int = 0, + events: Optional[List['OrchestratorEvent']] = None) -> None: # Not everyone runs in containers, but enough people do to # justify having the container_image_id (image hash) and container_image # (image name) @@ -1489,10 +1502,10 @@ class ServiceDescription(object): self.events: List[OrchestratorEvent] = events or [] - def service_type(self): + def service_type(self) -> str: return self.spec.service_type - def __repr__(self): + def __repr__(self) -> str: return f"" def to_json(self) -> OrderedDict: @@ -1518,7 +1531,7 @@ class ServiceDescription(object): @classmethod @handle_type_error - def from_json(cls, data: dict): + def from_json(cls, data: dict) -> 'ServiceDescription': c = data.copy() status = c.pop('status', {}) event_strs = c.pop('events', []) @@ -1532,7 +1545,7 @@ class ServiceDescription(object): return cls(spec=spec, events=events, **c_status) @staticmethod - def yaml_representer(dumper: 'yaml.SafeDumper', data: 'DaemonDescription'): + def yaml_representer(dumper: 'yaml.SafeDumper', data: 'DaemonDescription') -> Any: return dumper.represent_dict(data.to_json().items()) @@ -1580,7 +1593,7 @@ class InventoryHost(object): self.devices = devices self.labels = labels - def to_json(self): + def to_json(self) -> dict: return { 'name': self.name, 'addr': self.addr, @@ -1589,7 +1602,7 @@ class InventoryHost(object): } @classmethod - def from_json(cls, data): + def from_json(cls, data: dict) -> 'InventoryHost': try: _data = copy.deepcopy(data) name = _data.pop('name') @@ -1607,18 +1620,18 @@ class InventoryHost(object): raise OrchestratorValidationError('Failed to read inventory: {}'.format(e)) @classmethod - def from_nested_items(cls, hosts): + def from_nested_items(cls, hosts: List[dict]) -> List['InventoryHost']: devs = inventory.Devices.from_json return [cls(item[0], devs(item[1].data)) for item in hosts] - def __repr__(self): + def __repr__(self) -> str: return "({name})".format(name=self.name) @staticmethod def get_host_names(hosts: List['InventoryHost']) -> List[str]: return [host.name for host in hosts] - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: return self.name == other.name and self.devices == other.devices @@ -1645,7 +1658,8 @@ class OrchestratorEvent: ERROR = 'ERROR' regex_v1 = re.compile(r'^([^ ]+) ([^:]+):([^ ]+) \[([^\]]+)\] "((?:.|\n)*)"$', re.MULTILINE) - def __init__(self, created: Union[str, datetime.datetime], kind, subject, level, message): + def __init__(self, created: Union[str, datetime.datetime], kind: str, + subject: str, level: str, message: str) -> None: if isinstance(created, str): created = str_to_datetime(created) self.created: datetime.datetime = created @@ -1674,7 +1688,7 @@ class OrchestratorEvent: @classmethod @handle_type_error - def from_json(cls, data) -> "OrchestratorEvent": + def from_json(cls, data: str) -> "OrchestratorEvent": """ >>> OrchestratorEvent.from_json('''2020-06-10T10:20:25.691255 daemon:crash.ubuntu [INFO] "Deployed crash.ubuntu on host 'ubuntu'"''').to_json() '2020-06-10T10:20:25.691255Z daemon:crash.ubuntu [INFO] "Deployed crash.ubuntu on host \\'ubuntu\\'"' @@ -1687,7 +1701,7 @@ class OrchestratorEvent: return cls(*match.groups()) raise ValueError(f'Unable to match: "{data}"') - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if not isinstance(other, OrchestratorEvent): return False @@ -1695,11 +1709,11 @@ class OrchestratorEvent: and self.subject == other.subject and self.message == other.message -def _mk_orch_methods(cls): +def _mk_orch_methods(cls: Any) -> Any: # Needs to be defined outside of for. # Otherwise meth is always bound to last key - def shim(method_name): - def inner(self, *args, **kwargs): + def shim(method_name: str) -> Callable: + def inner(self: Any, *args: Any, **kwargs: Any) -> Any: completion = self._oremote(method_name, args, kwargs) return completion return inner @@ -1745,13 +1759,13 @@ class OrchestratorClientMixin(Orchestrator): self.__mgr = mgr # Make sure we're not overwriting any other `mgr` properties - def __get_mgr(self): + def __get_mgr(self) -> Any: try: return self.__mgr except AttributeError: return self - def _oremote(self, meth, args, kwargs): + def _oremote(self, meth: Any, args: Any, kwargs: Any) -> Any: """ Helper for invoking `remote` on whichever orchestrator is enabled diff --git a/src/pybind/mgr/orchestrator/module.py b/src/pybind/mgr/orchestrator/module.py index 9f97fe9499e16..ee56eea4dc65a 100644 --- a/src/pybind/mgr/orchestrator/module.py +++ b/src/pybind/mgr/orchestrator/module.py @@ -3,7 +3,7 @@ import errno import json from typing import List, Set, Optional, Iterator, cast, Dict, Any, Union import re -import ast +import datetime import yaml from prettytable import PrettyTable @@ -23,7 +23,7 @@ from ._interface import OrchestratorClientMixin, DeviceLightLoc, _cli_read_comma ServiceDescription, DaemonDescription, IscsiServiceSpec, json_to_generic_spec, GenericSpec -def nice_delta(now, t, suffix=''): +def nice_delta(now: datetime.datetime, t: Optional[datetime.datetime], suffix: str = '') -> str: if t: return to_pretty_timedelta(now - t) + suffix else: @@ -68,13 +68,13 @@ class DaemonAction(enum.Enum): reconfig = 'reconfig' -def to_format(what, format: Format, many: bool, cls): - def to_json_1(obj): +def to_format(what: Any, format: Format, many: bool, cls: Any) -> Any: + def to_json_1(obj: Any) -> Any: if hasattr(obj, 'to_json'): return obj.to_json() return obj - def to_json_n(objs): + def to_json_n(objs: List) -> List: return [to_json_1(o) for o in objs] to_json = to_json_n if many else to_json_1 @@ -92,12 +92,12 @@ def to_format(what, format: Format, many: bool, cls): else: copy = what - def to_yaml_1(obj): + def to_yaml_1(obj: Any) -> Any: if hasattr(obj, 'yaml_representer'): return obj return to_json_1(obj) - def to_yaml_n(objs): + def to_yaml_n(objs: list) -> list: return [to_yaml_1(o) for o in objs] to_yaml = to_yaml_n if many else to_yaml_1 @@ -109,7 +109,7 @@ def to_format(what, format: Format, many: bool, cls): raise OrchestratorError(f'unsupported format type: {format}') -def generate_preview_tables(data, osd_only=False): +def generate_preview_tables(data: Any, osd_only: bool = False) -> str: error = [x.get('error') for x in data if x.get('error')] if error: return json.dumps(error) @@ -144,7 +144,7 @@ OSDSPEC PREVIEWS return tables -def preview_table_osd(data): +def preview_table_osd(data: List) -> str: table = PrettyTable(header_style='upper', title='OSDSPEC PREVIEWS', border=True) table.field_names = "service name host data db wal".split() table.align = 'l' @@ -168,7 +168,7 @@ def preview_table_osd(data): return table.get_string() -def preview_table_services(data): +def preview_table_services(data: List) -> str: table = PrettyTable(header_style='upper', title="SERVICESPEC PREVIEW", border=True) table.field_names = 'SERVICE NAME ADD_TO REMOVE_FROM'.split() table.align = 'l' @@ -197,14 +197,14 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, ] NATIVE_OPTIONS = [] # type: List[dict] - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super(OrchestratorCli, self).__init__(*args, **kwargs) self.ident = set() # type: Set[str] self.fault = set() # type: Set[str] self._load() self._refresh_health() - def _load(self): + def _load(self) -> None: active = self.get_store('active_devices') if active: decoded = json.loads(active) @@ -212,14 +212,14 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, self.fault = set(decoded.get('fault', [])) self.log.debug('ident {}, fault {}'.format(self.ident, self.fault)) - def _save(self): + def _save(self) -> None: encoded = json.dumps({ 'ident': list(self.ident), 'fault': list(self.fault), }) self.set_store('active_devices', encoded) - def _refresh_health(self): + def _refresh_health(self) -> None: h = {} if self.ident: h['DEVICE_IDENT_ON'] = { @@ -243,7 +243,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, return [DeviceLightLoc(**l) for l in sum(locs, [])] @_cli_read_command(prefix='device ls-lights') - def _device_ls(self): + def _device_ls(self) -> HandleCommandResult: """List currently active device indicator lights""" return HandleCommandResult( stdout=json.dumps({ @@ -318,11 +318,11 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, else: return self.light_off(light_type.value, devid, force) - def _select_orchestrator(self): - return self.get_module_option("orchestrator") + def _select_orchestrator(self) -> str: + return cast(str, self.get_module_option("orchestrator")) @_cli_write_command('orch host add') - def _add_host(self, hostname: str, addr: Optional[str] = None, labels: Optional[List[str]] = None): + def _add_host(self, hostname: str, addr: Optional[str] = None, labels: Optional[List[str]] = None) -> HandleCommandResult: """Add a host""" s = HostSpec(hostname=hostname, addr=addr, labels=labels) completion = self.add_host(s) @@ -331,7 +331,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch host rm') - def _remove_host(self, hostname: str): + def _remove_host(self, hostname: str) -> HandleCommandResult: """Remove a host""" completion = self.remove_host(hostname) self._orchestrator_wait([completion]) @@ -339,7 +339,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch host set-addr') - def _update_set_addr(self, hostname: str, addr: str): + def _update_set_addr(self, hostname: str, addr: str) -> HandleCommandResult: """Update a host address""" completion = self.update_host_addr(hostname, addr) self._orchestrator_wait([completion]) @@ -347,7 +347,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, return HandleCommandResult(stdout=completion.result_str()) @_cli_read_command('orch host ls') - def _get_hosts(self, format: Format = Format.plain): + def _get_hosts(self, format: Format = Format.plain) -> HandleCommandResult: """List hosts""" completion = self.get_hosts() self._orchestrator_wait([completion]) @@ -368,7 +368,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, return HandleCommandResult(stdout=output) @_cli_write_command('orch host label add') - def _host_label_add(self, hostname: str, label: str): + def _host_label_add(self, hostname: str, label: str) -> HandleCommandResult: """Add a host label""" completion = self.add_host_label(hostname, label) self._orchestrator_wait([completion]) @@ -376,7 +376,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch host label rm') - def _host_label_rm(self, hostname: str, label: str): + def _host_label_rm(self, hostname: str, label: str) -> HandleCommandResult: """Remove a host label""" completion = self.remove_host_label(hostname, label) self._orchestrator_wait([completion]) @@ -384,7 +384,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch host ok-to-stop') - def _host_ok_to_stop(self, hostname: str): + def _host_ok_to_stop(self, hostname: str) -> HandleCommandResult: """Check if the specified host can be safely stopped without reducing availability""""" completion = self.host_ok_to_stop(hostname) self._orchestrator_wait([completion]) @@ -393,7 +393,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, @_cli_write_command( 'orch host maintenance enter') - def _host_maintenance_enter(self, hostname: str): + def _host_maintenance_enter(self, hostname: str) -> HandleCommandResult: """ Prepare a host for maintenance by shutting down and disabling all Ceph daemons (cephadm only) """ @@ -405,7 +405,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, @_cli_write_command( 'orch host maintenance exit') - def _host_maintenance_exit(self, hostname: str): + def _host_maintenance_exit(self, hostname: str) -> HandleCommandResult: """ Return a host from maintenance, restarting all Ceph daemons (cephadm only) """ @@ -518,7 +518,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, return HandleCommandResult(stdout='\n'.join(out)) @_cli_write_command('orch device zap') - def _zap_device(self, hostname: str, path: str, force: bool = False): + def _zap_device(self, hostname: str, path: str, force: bool = False) -> HandleCommandResult: """ Zap (erase!) a device so it can be re-used """ @@ -536,7 +536,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, service_name: Optional[str] = None, export: bool = False, format: Format = Format.plain, - refresh: bool = False): + refresh: bool = False) -> HandleCommandResult: """ List services known to orchestrator """ @@ -550,7 +550,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, raise_if_exception(completion) services: List[ServiceDescription] = completion.result - def ukn(s): + def ukn(s: Optional[str]) -> str: return '' if s is None else s # Sort the list for display @@ -607,7 +607,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, daemon_type: Optional[str] = None, daemon_id: Optional[str] = None, format: Format = Format.plain, - refresh: bool = False): + refresh: bool = False) -> HandleCommandResult: """ List daemons known to orchestrator """ @@ -620,7 +620,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, raise_if_exception(completion) daemons: List[DaemonDescription] = completion.result - def ukn(s): + def ukn(s: Optional[str]) -> str: return '' if s is None else s # Sort the list for display daemons.sort(key=lambda s: (ukn(s.daemon_type), ukn(s.hostname), ukn(s.daemon_id))) @@ -1018,7 +1018,7 @@ Usage: return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch') - def _service_action(self, action: ServiceAction, service_name: str): + def _service_action(self, action: ServiceAction, service_name: str) -> HandleCommandResult: """Start, stop, restart, redeploy, or reconfig an entire service (i.e. all daemons)""" completion = self.service_action(action.value, service_name) self._orchestrator_wait([completion]) @@ -1026,7 +1026,7 @@ Usage: return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch daemon') - def _daemon_action(self, action: DaemonAction, name: str): + def _daemon_action(self, action: DaemonAction, name: str) -> HandleCommandResult: """Start, stop, restart, (redeploy,) or reconfig a specific daemon""" if '.' not in name: raise OrchestratorError('%s is not a valid daemon name' % name) @@ -1048,7 +1048,7 @@ Usage: @_cli_write_command('orch daemon rm') def _daemon_rm(self, names: List[str], - force: Optional[bool] = False): + force: Optional[bool] = False) -> HandleCommandResult: """Remove specific daemon(s)""" for name in names: if '.' not in name: @@ -1065,7 +1065,7 @@ Usage: @_cli_write_command('orch rm') def _service_rm(self, service_name: str, - force: bool = False): + force: bool = False) -> HandleCommandResult: """Remove a service""" if service_name in ['mon', 'mgr'] and not force: raise OrchestratorError('The mon and mgr services cannot be removed') @@ -1274,7 +1274,7 @@ Usage: return HandleCommandResult(stdout=out) @_cli_write_command('orch set backend') - def _set_backend(self, module_name: Optional[str] = None): + def _set_backend(self, module_name: Optional[str] = None) -> HandleCommandResult: """ Select orchestrator module backend """ @@ -1321,19 +1321,19 @@ Usage: return HandleCommandResult(-errno.EINVAL, stderr="Module '{0}' not found".format(module_name)) @_cli_write_command('orch pause') - def _pause(self): + def _pause(self) -> HandleCommandResult: """Pause orchestrator background work""" self.pause() return HandleCommandResult() @_cli_write_command('orch resume') - def _resume(self): + def _resume(self) -> HandleCommandResult: """Resume orchestrator background work (if paused)""" self.resume() return HandleCommandResult() @_cli_write_command('orch cancel') - def _cancel(self): + def _cancel(self) -> HandleCommandResult: """ cancels ongoing operations @@ -1343,14 +1343,14 @@ Usage: return HandleCommandResult() @_cli_read_command('orch status') - def _status(self, format: Format = Format.plain): + def _status(self, format: Format = Format.plain) -> HandleCommandResult: """Report configured backend and its status""" o = self._select_orchestrator() if o is None: raise NoOrchestrator() avail, why = self.available() - result = { + result: Dict[str, Any] = { "backend": o } if avail is not None: @@ -1368,7 +1368,7 @@ Usage: output += ' ({0})'.format(result['reason']) return HandleCommandResult(stdout=output) - def self_test(self): + def self_test(self) -> None: old_orch = self._select_orchestrator() self._set_backend('') assert self._select_orchestrator() is None @@ -1392,7 +1392,7 @@ Usage: assert c.has_result @staticmethod - def _upgrade_check_image_name(image, ceph_version): + def _upgrade_check_image_name(image: Optional[str], ceph_version: Optional[str]) -> None: """ >>> OrchestratorCli._upgrade_check_image_name('v15.2.0', None) Traceback (most recent call last): @@ -1409,7 +1409,7 @@ Usage: @_cli_write_command('orch upgrade check') def _upgrade_check(self, image: Optional[str] = None, - ceph_version: Optional[str] = None): + ceph_version: Optional[str] = None) -> HandleCommandResult: """Check service versions vs available and target containers""" self._upgrade_check_image_name(image, ceph_version) completion = self.upgrade_check(image=image, version=ceph_version) @@ -1418,7 +1418,7 @@ Usage: return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch upgrade status') - def _upgrade_status(self): + def _upgrade_status(self) -> HandleCommandResult: """Check service versions vs available and target containers""" completion = self.upgrade_status() self._orchestrator_wait([completion]) @@ -1435,7 +1435,7 @@ Usage: @_cli_write_command('orch upgrade start') def _upgrade_start(self, image: Optional[str] = None, - ceph_version: Optional[str] = None): + ceph_version: Optional[str] = None) -> HandleCommandResult: """Initiate upgrade""" self._upgrade_check_image_name(image, ceph_version) completion = self.upgrade_start(image, ceph_version) @@ -1444,7 +1444,7 @@ Usage: return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch upgrade pause') - def _upgrade_pause(self): + def _upgrade_pause(self) -> HandleCommandResult: """Pause an in-progress upgrade""" completion = self.upgrade_pause() self._orchestrator_wait([completion]) @@ -1452,7 +1452,7 @@ Usage: return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch upgrade resume') - def _upgrade_resume(self): + def _upgrade_resume(self) -> HandleCommandResult: """Resume paused upgrade""" completion = self.upgrade_resume() self._orchestrator_wait([completion]) @@ -1460,7 +1460,7 @@ Usage: return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch upgrade stop') - def _upgrade_stop(self): + def _upgrade_stop(self) -> HandleCommandResult: """Stop an in-progress upgrade""" completion = self.upgrade_stop() self._orchestrator_wait([completion]) diff --git a/src/pybind/mgr/test_orchestrator/module.py b/src/pybind/mgr/test_orchestrator/module.py index c8a422d6c5728..56f13388443c9 100644 --- a/src/pybind/mgr/test_orchestrator/module.py +++ b/src/pybind/mgr/test_orchestrator/module.py @@ -208,7 +208,7 @@ class TestOrchestrator(MgrModule, orchestrator.Orchestrator): daemon_size = len(list(daemons)) services.append(orchestrator.ServiceDescription( spec=ServiceSpec( - service_type=daemon_type, + service_type=daemon_type, # type: ignore ), size=daemon_size, running=daemon_size)) -- 2.39.5