From 4aa9e246f05663ec334f67d8e7f1ce817c1cbf2d Mon Sep 17 00:00:00 2001 From: Samuel Just Date: Wed, 26 Nov 2025 22:51:54 +0000 Subject: [PATCH] pybind/mgr: add per-module CLICommand instances to remaining modules Signed-off-by: Samuel Just --- src/pybind/mgr/alerts/cli.py | 3 ++ src/pybind/mgr/alerts/module.py | 5 ++- src/pybind/mgr/balancer/cli.py | 3 ++ src/pybind/mgr/balancer/module.py | 37 ++++++++++-------- src/pybind/mgr/cephadm/cli.py | 2 +- src/pybind/mgr/cli_api/cli.py | 3 ++ src/pybind/mgr/cli_api/module.py | 10 +++-- src/pybind/mgr/crash/cli.py | 3 ++ src/pybind/mgr/crash/module.py | 23 ++++++----- src/pybind/mgr/devicehealth/cli.py | 3 ++ src/pybind/mgr/devicehealth/module.py | 19 +++++---- src/pybind/mgr/diskprediction_local/cli.py | 3 ++ src/pybind/mgr/diskprediction_local/module.py | 3 ++ src/pybind/mgr/feedback/cli.py | 3 ++ src/pybind/mgr/feedback/module.py | 13 ++++--- src/pybind/mgr/hello/cli.py | 3 ++ src/pybind/mgr/hello/module.py | 7 +++- src/pybind/mgr/influx/cli.py | 3 ++ src/pybind/mgr/influx/module.py | 9 +++-- src/pybind/mgr/insights/cli.py | 3 ++ src/pybind/mgr/insights/module.py | 7 +++- src/pybind/mgr/iostat/cli.py | 3 ++ src/pybind/mgr/iostat/module.py | 6 ++- src/pybind/mgr/k8sevents/cli.py | 3 ++ src/pybind/mgr/k8sevents/module.py | 3 ++ src/pybind/mgr/localpool/cli.py | 3 ++ src/pybind/mgr/localpool/module.py | 3 ++ src/pybind/mgr/mds_autoscaler/cli.py | 3 ++ src/pybind/mgr/mds_autoscaler/module.py | 3 ++ src/pybind/mgr/mirroring/cli.py | 3 ++ src/pybind/mgr/mirroring/module.py | 29 +++++++------- src/pybind/mgr/nfs/cli.py | 3 ++ src/pybind/mgr/nfs/module.py | 35 +++++++++-------- src/pybind/mgr/osd_perf_query/cli.py | 3 ++ src/pybind/mgr/osd_perf_query/module.py | 3 ++ src/pybind/mgr/osd_support/cli.py | 3 ++ src/pybind/mgr/osd_support/module.py | 3 ++ src/pybind/mgr/pg_autoscaler/cli.py | 3 ++ src/pybind/mgr/pg_autoscaler/module.py | 15 ++++--- src/pybind/mgr/progress/cli.py | 3 ++ src/pybind/mgr/progress/module.py | 3 ++ src/pybind/mgr/prometheus/cli.py | 3 ++ src/pybind/mgr/prometheus/module.py | 9 +++-- src/pybind/mgr/rbd_support/cli.py | 3 ++ src/pybind/mgr/rbd_support/module.py | 39 ++++++++++--------- src/pybind/mgr/rgw/cli.py | 3 ++ src/pybind/mgr/rgw/module.py | 21 +++++----- src/pybind/mgr/rook/cli.py | 3 ++ src/pybind/mgr/rook/module.py | 3 ++ src/pybind/mgr/selftest/cli.py | 3 ++ src/pybind/mgr/selftest/module.py | 29 +++++++------- src/pybind/mgr/snap_schedule/cli.py | 3 ++ src/pybind/mgr/snap_schedule/module.py | 19 +++++---- src/pybind/mgr/stats/cli.py | 3 ++ src/pybind/mgr/stats/module.py | 3 ++ src/pybind/mgr/status/cli.py | 3 ++ src/pybind/mgr/status/module.py | 7 +++- src/pybind/mgr/telegraf/cli.py | 3 ++ src/pybind/mgr/telegraf/module.py | 9 +++-- src/pybind/mgr/telemetry/cli.py | 3 ++ src/pybind/mgr/telemetry/module.py | 37 ++++++++++-------- src/pybind/mgr/test_orchestrator/cli.py | 3 ++ src/pybind/mgr/test_orchestrator/module.py | 5 ++- src/pybind/mgr/volumes/cli.py | 3 ++ src/pybind/mgr/volumes/module.py | 3 ++ 65 files changed, 356 insertions(+), 162 deletions(-) create mode 100644 src/pybind/mgr/alerts/cli.py create mode 100644 src/pybind/mgr/balancer/cli.py create mode 100644 src/pybind/mgr/cli_api/cli.py create mode 100644 src/pybind/mgr/crash/cli.py create mode 100644 src/pybind/mgr/devicehealth/cli.py create mode 100644 src/pybind/mgr/diskprediction_local/cli.py create mode 100644 src/pybind/mgr/feedback/cli.py create mode 100644 src/pybind/mgr/hello/cli.py create mode 100644 src/pybind/mgr/influx/cli.py create mode 100644 src/pybind/mgr/insights/cli.py create mode 100644 src/pybind/mgr/iostat/cli.py create mode 100644 src/pybind/mgr/k8sevents/cli.py create mode 100644 src/pybind/mgr/localpool/cli.py create mode 100644 src/pybind/mgr/mds_autoscaler/cli.py create mode 100644 src/pybind/mgr/mirroring/cli.py create mode 100644 src/pybind/mgr/nfs/cli.py create mode 100644 src/pybind/mgr/osd_perf_query/cli.py create mode 100644 src/pybind/mgr/osd_support/cli.py create mode 100644 src/pybind/mgr/pg_autoscaler/cli.py create mode 100644 src/pybind/mgr/progress/cli.py create mode 100644 src/pybind/mgr/prometheus/cli.py create mode 100644 src/pybind/mgr/rbd_support/cli.py create mode 100644 src/pybind/mgr/rgw/cli.py create mode 100644 src/pybind/mgr/rook/cli.py create mode 100644 src/pybind/mgr/selftest/cli.py create mode 100644 src/pybind/mgr/snap_schedule/cli.py create mode 100644 src/pybind/mgr/stats/cli.py create mode 100644 src/pybind/mgr/status/cli.py create mode 100644 src/pybind/mgr/telegraf/cli.py create mode 100644 src/pybind/mgr/telemetry/cli.py create mode 100644 src/pybind/mgr/test_orchestrator/cli.py create mode 100644 src/pybind/mgr/volumes/cli.py diff --git a/src/pybind/mgr/alerts/cli.py b/src/pybind/mgr/alerts/cli.py new file mode 100644 index 00000000000..aa85c0c7880 --- /dev/null +++ b/src/pybind/mgr/alerts/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +AlertsCLICommand = CLICommandBase.make_registry_subtype("AlertsCLICommand") diff --git a/src/pybind/mgr/alerts/module.py b/src/pybind/mgr/alerts/module.py index 5bd627471ea..bfc9676a122 100644 --- a/src/pybind/mgr/alerts/module.py +++ b/src/pybind/mgr/alerts/module.py @@ -11,8 +11,11 @@ import json import smtplib import ssl +from .cli import AlertsCLICommand + class Alerts(MgrModule): + CLICommand = AlertsCLICommand MODULE_OPTIONS = [ Option( name='interval', @@ -112,7 +115,7 @@ class Alerts(MgrModule): self.get_ceph_option(opt)) self.log.debug(' native option %s = %s', opt, getattr(self, opt)) - @CLIReadCommand('alerts send') + @AlertsCLICommand.Read('alerts send') def send(self) -> HandleCommandResult: """ (re)send alerts immediately diff --git a/src/pybind/mgr/balancer/cli.py b/src/pybind/mgr/balancer/cli.py new file mode 100644 index 00000000000..cbc6d63a5bc --- /dev/null +++ b/src/pybind/mgr/balancer/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +BalancerCLICommand = CLICommandBase.make_registry_subtype("BalancerCLICommand") diff --git a/src/pybind/mgr/balancer/module.py b/src/pybind/mgr/balancer/module.py index 78dfa011ef2..39e7802b755 100644 --- a/src/pybind/mgr/balancer/module.py +++ b/src/pybind/mgr/balancer/module.py @@ -15,6 +15,8 @@ from typing import cast, Any, Dict, List, Optional, Sequence, Tuple, Union from mgr_module import CRUSHMap import datetime +from .cli import BalancerCLICommand + TIME_FORMAT = '%Y-%m-%d_%H:%M:%S' @@ -239,6 +241,7 @@ class Eval: class Module(MgrModule): + CLICommand = BalancerCLICommand MODULE_OPTIONS = [ Option(name='active', type='bool', @@ -353,7 +356,7 @@ class Module(MgrModule): super(Module, self).__init__(*args, **kwargs) self.event = Event() - @CLIReadCommand('balancer status') + @BalancerCLICommand.Read('balancer status') def show_status(self) -> Tuple[int, str, str]: """ Show balancer status @@ -369,7 +372,7 @@ class Module(MgrModule): } return (0, json.dumps(s, indent=4, sort_keys=True), '') - @CLIReadCommand('balancer status detail') + @BalancerCLICommand.Read('balancer status detail') def show_status_detail(self) -> Tuple[int, str, str]: """ Show balancer status (detailed) @@ -394,7 +397,7 @@ class Module(MgrModule): } return (0, json.dumps(s, indent=4, sort_keys=True), '') - @CLICommand('balancer mode') + @BalancerCLICommand('balancer mode') def set_mode(self, mode: Mode) -> Tuple[int, str, str]: """ Set balancer mode @@ -435,7 +438,7 @@ class Module(MgrModule): self.set_module_option('mode', mode.value) return (0, '', '') - @CLICommand('balancer on') + @BalancerCLICommand('balancer on') def on(self) -> Tuple[int, str, str]: """ Enable automatic balancing @@ -446,7 +449,7 @@ class Module(MgrModule): self.event.set() return (0, '', '') - @CLICommand('balancer off') + @BalancerCLICommand('balancer off') def off(self) -> Tuple[int, str, str]: """ Disable automatic balancing @@ -457,7 +460,7 @@ class Module(MgrModule): self.event.set() return (0, '', '') - @CLIReadCommand('balancer pool ls') + @BalancerCLICommand.Read('balancer pool ls') def pool_ls(self) -> Tuple[int, str, str]: """ List automatic balancing pools @@ -484,7 +487,7 @@ class Module(MgrModule): self.set_module_option('pool_ids', ','.join(str(p) for p in final_ids)) return (0, json.dumps(sorted(final_names), indent=4, sort_keys=True), '') - @CLICommand('balancer pool add') + @BalancerCLICommand('balancer pool add') def pool_add(self, pools: Sequence[str]) -> Tuple[int, str, str]: """ Enable automatic balancing for specific pools @@ -502,7 +505,7 @@ class Module(MgrModule): self.set_module_option('pool_ids', ','.join(final)) return (0, '', '') - @CLICommand('balancer pool rm') + @BalancerCLICommand('balancer pool rm') def pool_rm(self, pools: Sequence[str]) -> Tuple[int, str, str]: """ Disable automatic balancing for specific pools @@ -558,7 +561,7 @@ class Module(MgrModule): f'pool "{option}"') return ms, pools - @CLIReadCommand('balancer eval-verbose') + @BalancerCLICommand.Read('balancer eval-verbose') def plan_eval_verbose(self, option: Optional[str] = None): """ Evaluate data distribution for the current cluster or specific pool or specific @@ -570,7 +573,7 @@ class Module(MgrModule): except ValueError as e: return (-errno.EINVAL, '', str(e)) - @CLIReadCommand('balancer eval') + @BalancerCLICommand.Read('balancer eval') def plan_eval_brief(self, option: Optional[str] = None): """ Evaluate data distribution for the current cluster or specific pool or specific plan @@ -581,7 +584,7 @@ class Module(MgrModule): except ValueError as e: return (-errno.EINVAL, '', str(e)) - @CLIReadCommand('balancer optimize') + @BalancerCLICommand.Read('balancer optimize') def plan_optimize(self, plan: str, pools: List[str] = []) -> Tuple[int, str, str]: """ Run optimizer to create a new plan @@ -614,7 +617,7 @@ class Module(MgrModule): self.optimize_result = detail return (r, '', detail) - @CLIReadCommand('balancer show') + @BalancerCLICommand.Read('balancer show') def plan_show(self, plan: str) -> Tuple[int, str, str]: """ Show details of an optimization plan @@ -624,7 +627,7 @@ class Module(MgrModule): return (-errno.ENOENT, '', f'plan {plan} not found') return (0, plan_.show(), '') - @CLICommand('balancer rm') + @BalancerCLICommand('balancer rm') def plan_rm(self, plan: str) -> Tuple[int, str, str]: """ Discard an optimization plan @@ -633,7 +636,7 @@ class Module(MgrModule): del self.plans[plan] return (0, '', '') - @CLICommand('balancer reset') + @BalancerCLICommand('balancer reset') def plan_reset(self) -> Tuple[int, str, str]: """ Discard all optimization plans @@ -641,7 +644,7 @@ class Module(MgrModule): self.plans = {} return (0, '', '') - @CLIReadCommand('balancer dump') + @BalancerCLICommand.Read('balancer dump') def plan_dump(self, plan: str) -> Tuple[int, str, str]: """ Show an optimization plan @@ -652,14 +655,14 @@ class Module(MgrModule): else: return (0, plan_.dump(), '') - @CLIReadCommand('balancer ls') + @BalancerCLICommand.Read('balancer ls') def plan_ls(self) -> Tuple[int, str, str]: """ List all plans """ return (0, json.dumps([p for p in self.plans], indent=4, sort_keys=True), '') - @CLIReadCommand('balancer execute') + @BalancerCLICommand.Read('balancer execute') def plan_execute(self, plan: str) -> Tuple[int, str, str]: """ Execute an optimization plan diff --git a/src/pybind/mgr/cephadm/cli.py b/src/pybind/mgr/cephadm/cli.py index 0fc671fd08b..40022af72ad 100644 --- a/src/pybind/mgr/cephadm/cli.py +++ b/src/pybind/mgr/cephadm/cli.py @@ -1,4 +1,4 @@ -from ..orchestrator.cli import OrchestratorCLICommandBase +from orchestrator.cli import OrchestratorCLICommandBase CephadmCLICommand = OrchestratorCLICommandBase.make_registry_subtype( diff --git a/src/pybind/mgr/cli_api/cli.py b/src/pybind/mgr/cli_api/cli.py new file mode 100644 index 00000000000..ffad43e6d59 --- /dev/null +++ b/src/pybind/mgr/cli_api/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +CLIAPICLICommand = CLICommandBase.make_registry_subtype("CLIAPICLICommand") diff --git a/src/pybind/mgr/cli_api/module.py b/src/pybind/mgr/cli_api/module.py index 79b042eb0e9..08dac550a30 100755 --- a/src/pybind/mgr/cli_api/module.py +++ b/src/pybind/mgr/cli_api/module.py @@ -6,7 +6,9 @@ import time import errno from typing import Any, Callable, Dict, List -from mgr_module import MgrModule, HandleCommandResult, CLICommand, API +from .cli import CLIAPICLICommand + +from mgr_module import MgrModule, HandleCommandResult, API logger = logging.getLogger() get_time = time.perf_counter @@ -53,7 +55,7 @@ class MgrAPIReflector(type): # save functions to klass._cli_{n}() methods. This # can help on unit testing wrapper = cls.func_wrapper(func) - command = CLICommand(**CephCommander(func).to_ceph_signature())( # type: ignore + command = CLIAPICLICommand(**CephCommander(func).to_ceph_signature())( # type: ignore wrapper) setattr( klass, @@ -84,7 +86,9 @@ class MgrAPIReflector(type): class CLI(MgrModule, metaclass=MgrAPIReflector): - @CLICommand('mgr cli_benchmark') + CLICommand = CLIAPICLICommand + + @CLIAPICLICommand('mgr cli_benchmark') def benchmark(self, iterations: int, threads: int, func_name: str, func_args: List[str] = None) -> HandleCommandResult: # type: ignore func_args = () if func_args is None else func_args diff --git a/src/pybind/mgr/crash/cli.py b/src/pybind/mgr/crash/cli.py new file mode 100644 index 00000000000..ad6ab2be166 --- /dev/null +++ b/src/pybind/mgr/crash/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +CrashCLICommand = CLICommandBase.make_registry_subtype("CrashCLICommand") diff --git a/src/pybind/mgr/crash/module.py b/src/pybind/mgr/crash/module.py index ed1cb63cdb4..9aae5c9306f 100644 --- a/src/pybind/mgr/crash/module.py +++ b/src/pybind/mgr/crash/module.py @@ -12,6 +12,8 @@ from threading import Event, Lock from typing import cast, Any, Callable, DefaultDict, Dict, Iterable, List, Optional, Tuple, TypeVar, \ Union, TYPE_CHECKING +from .cli import CrashCLICommand + DATEFMT = '%Y-%m-%dT%H:%M:%S.%f' OLD_DATEFMT = '%Y-%m-%d %H:%M:%S.%f' @@ -38,6 +40,7 @@ CrashT = Dict[str, Union[str, List[str]]] class Module(MgrModule): + CLICommand = CrashCLICommand MODULE_OPTIONS = [ Option( name='warn_recent_interval', @@ -215,7 +218,7 @@ class Module(MgrModule): # command handlers - @CLIReadCommand('crash info') + @CrashCLICommand.Read('crash info') @with_crashes def do_info(self, id: str) -> Tuple[int, str, str]: """ @@ -229,7 +232,7 @@ class Module(MgrModule): val = json.dumps(crash, indent=4, sort_keys=True) return 0, val, '' - @CLICommand('crash post') + @CrashCLICommand('crash post') def do_post(self, inbuf: str) -> Tuple[int, str, str]: """ Add a crash dump (use -i ) @@ -273,7 +276,7 @@ class Module(MgrModule): '' if 'archived' in c else '*']) return 0, table.get_string(), '' - @CLIReadCommand('crash ls') + @CrashCLICommand.Read('crash ls') @with_crashes def do_ls_all(self, format: Optional[str] = None) -> Tuple[int, str, str]: """ @@ -282,7 +285,7 @@ class Module(MgrModule): assert self.crashes is not None return self._do_ls(self.crashes.values(), format) - @CLIReadCommand('crash ls-new') + @CrashCLICommand.Read('crash ls-new') @with_crashes def do_ls_new(self, format: Optional[str] = None) -> Tuple[int, str, str]: """ @@ -293,7 +296,7 @@ class Module(MgrModule): if 'archived' not in crash] return self._do_ls(t, format) - @CLICommand('crash rm') + @CrashCLICommand('crash rm') @with_crashes def do_rm(self, id: str) -> Tuple[int, str, str]: """ @@ -308,7 +311,7 @@ class Module(MgrModule): self._refresh_health_checks() return 0, '', '' - @CLICommand('crash prune') + @CrashCLICommand('crash prune') @with_crashes def do_prune(self, keep: int) -> Tuple[int, str, str]: """ @@ -332,7 +335,7 @@ class Module(MgrModule): if removed_any: self._refresh_health_checks() - @CLIWriteCommand('crash archive') + @CrashCLICommand.Write('crash archive') @with_crashes def do_archive(self, id: str) -> Tuple[int, str, str]: """ @@ -351,7 +354,7 @@ class Module(MgrModule): self._refresh_health_checks() return 0, '', '' - @CLIWriteCommand('crash archive-all') + @CrashCLICommand.Write('crash archive-all') @with_crashes def do_archive_all(self) -> Tuple[int, str, str]: """ @@ -367,7 +370,7 @@ class Module(MgrModule): self._refresh_health_checks() return 0, '', '' - @CLIReadCommand('crash stat') + @CrashCLICommand.Read('crash stat') @with_crashes def do_stat(self) -> Tuple[int, str, str]: """ @@ -418,7 +421,7 @@ class Module(MgrModule): retlines.append(binstr(bindict)) return 0, '\n'.join(retlines), '' - @CLIReadCommand('crash json_report') + @CrashCLICommand.Read('crash json_report') @with_crashes def do_json_report(self, hours: int) -> Tuple[int, str, str]: """ diff --git a/src/pybind/mgr/devicehealth/cli.py b/src/pybind/mgr/devicehealth/cli.py new file mode 100644 index 00000000000..f9b4db9293c --- /dev/null +++ b/src/pybind/mgr/devicehealth/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +DevicehealthCLICommand = CLICommandBase.make_registry_subtype("DevicehealthCLICommand") diff --git a/src/pybind/mgr/devicehealth/module.py b/src/pybind/mgr/devicehealth/module.py index 0a72e720d09..9854cc78c43 100644 --- a/src/pybind/mgr/devicehealth/module.py +++ b/src/pybind/mgr/devicehealth/module.py @@ -12,6 +12,8 @@ from threading import Event from datetime import datetime, timedelta, timezone from typing import cast, Any, Dict, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union +from .cli import DevicehealthCLICommand + TIME_FORMAT = '%Y%m%d-%H%M%S' DEVICE_HEALTH = 'DEVICE_HEALTH' @@ -48,6 +50,7 @@ def get_nvme_wear_level(data: Dict[Any, Any]) -> Optional[float]: class Module(MgrModule): + CLICommand = DevicehealthCLICommand # latest (if db does not exist) SCHEMA = [ @@ -173,7 +176,7 @@ class Module(MgrModule): return False return parts[0] in ('osd', 'mon') - @CLIReadCommand('device query-daemon-health-metrics') + @DevicehealthCLICommand.Read('device query-daemon-health-metrics') def do_query_daemon_health_metrics(self, who: str) -> Tuple[int, str, str]: ''' Get device health metrics for a given daemon @@ -189,7 +192,7 @@ class Module(MgrModule): return result.wait() @CLIRequiresDB - @CLIReadCommand('device scrape-daemon-health-metrics') + @DevicehealthCLICommand.Read('device scrape-daemon-health-metrics') @MgrModuleRecoverDB def do_scrape_daemon_health_metrics(self, who: str) -> Tuple[int, str, str]: ''' @@ -201,7 +204,7 @@ class Module(MgrModule): return self.scrape_daemon(daemon_type, daemon_id) @CLIRequiresDB - @CLIReadCommand('device scrape-health-metrics') + @DevicehealthCLICommand.Read('device scrape-health-metrics') @MgrModuleRecoverDB def do_scrape_health_metrics(self, devid: Optional[str] = None) -> Tuple[int, str, str]: ''' @@ -213,7 +216,7 @@ class Module(MgrModule): return self.scrape_device(devid) @CLIRequiresDB - @CLIReadCommand('device get-health-metrics') + @DevicehealthCLICommand.Read('device get-health-metrics') @MgrModuleRecoverDB def do_get_health_metrics(self, devid: str, sample: Optional[str] = None) -> Tuple[int, str, str]: ''' @@ -222,7 +225,7 @@ class Module(MgrModule): return self.show_device_metrics(devid, sample) @CLIRequiresDB - @CLICommand('device check-health') + @DevicehealthCLICommand('device check-health') @MgrModuleRecoverDB def do_check_health(self) -> Tuple[int, str, str]: ''' @@ -230,7 +233,7 @@ class Module(MgrModule): ''' return self.check_health() - @CLICommand('device monitoring on') + @DevicehealthCLICommand('device monitoring on') def do_monitoring_on(self) -> Tuple[int, str, str]: ''' Enable device health monitoring @@ -239,7 +242,7 @@ class Module(MgrModule): self.event.set() return 0, '', '' - @CLICommand('device monitoring off') + @DevicehealthCLICommand('device monitoring off') def do_monitoring_off(self) -> Tuple[int, str, str]: ''' Disable device health monitoring @@ -249,7 +252,7 @@ class Module(MgrModule): return 0, '', '' @CLIRequiresDB - @CLIReadCommand('device predict-life-expectancy') + @DevicehealthCLICommand.Read('device predict-life-expectancy') @MgrModuleRecoverDB def do_predict_life_expectancy(self, devid: str) -> Tuple[int, str, str]: ''' diff --git a/src/pybind/mgr/diskprediction_local/cli.py b/src/pybind/mgr/diskprediction_local/cli.py new file mode 100644 index 00000000000..f4b5242b5ea --- /dev/null +++ b/src/pybind/mgr/diskprediction_local/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +DiskpredictionLocalCLICommand = CLICommandBase.make_registry_subtype("DiskpredictionLocalCLICommand") diff --git a/src/pybind/mgr/diskprediction_local/module.py b/src/pybind/mgr/diskprediction_local/module.py index 450dc9c8359..bd8c350d52a 100644 --- a/src/pybind/mgr/diskprediction_local/module.py +++ b/src/pybind/mgr/diskprediction_local/module.py @@ -7,6 +7,8 @@ from threading import Event import time from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING from mgr_module import CommandResult, MgrModule, Option + +from .cli import DiskpredictionLocalCLICommand # Importing scipy early appears to avoid a future deadlock when # we try to do # @@ -23,6 +25,7 @@ TIME_WEEK = TIME_DAYS * 7 class Module(MgrModule): + CLICommand = DiskpredictionLocalCLICommand MODULE_OPTIONS = [ Option(name='sleep_interval', default=600), diff --git a/src/pybind/mgr/feedback/cli.py b/src/pybind/mgr/feedback/cli.py new file mode 100644 index 00000000000..c3b8851e711 --- /dev/null +++ b/src/pybind/mgr/feedback/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +FeedbackCLICommand = CLICommandBase.make_registry_subtype("FeedbackCLICommand") diff --git a/src/pybind/mgr/feedback/module.py b/src/pybind/mgr/feedback/module.py index 59bd7a75674..6c76e0d5a92 100644 --- a/src/pybind/mgr/feedback/module.py +++ b/src/pybind/mgr/feedback/module.py @@ -7,6 +7,8 @@ See doc/mgr/feedback.rst for more info. from requests.exceptions import RequestException +from .cli import FeedbackCLICommand + from mgr_module import HandleCommandResult, MgrModule import errno @@ -15,9 +17,10 @@ from .model import Feedback class FeedbackModule(MgrModule): + CLICommand = FeedbackCLICommand # there are CLI commands we implement - @CLIReadCommand('feedback set api-key') + @FeedbackCLICommand.Read('feedback set api-key') def _cmd_feedback_set_api_key(self, key: str) -> HandleCommandResult: """ Set Ceph Issue Tracker API key @@ -28,7 +31,7 @@ class FeedbackModule(MgrModule): return HandleCommandResult(stderr=f'Exception in setting API key : {error}') return HandleCommandResult(stdout="Successfully updated API key") - @CLIReadCommand('feedback delete api-key') + @FeedbackCLICommand.Read('feedback delete api-key') def _cmd_feedback_delete_api_key(self) -> HandleCommandResult: """ Delete Ceph Issue Tracker API key @@ -39,7 +42,7 @@ class FeedbackModule(MgrModule): return HandleCommandResult(stderr=f'Exception in deleting API key : {error}') return HandleCommandResult(stdout="Successfully deleted key") - @CLIReadCommand('feedback get api-key') + @FeedbackCLICommand.Read('feedback get api-key') def _cmd_feedback_get_api_key(self) -> HandleCommandResult: """ Get Ceph Issue Tracker API key @@ -52,7 +55,7 @@ class FeedbackModule(MgrModule): return HandleCommandResult(stderr=f'Error in retreiving issue tracker API key: {error}') return HandleCommandResult(stdout=f'Your key: {key}') - @CLIReadCommand('feedback issue list') + @FeedbackCLICommand.Read('feedback issue list') def _cmd_feedback_issue_list(self) -> HandleCommandResult: """ Fetch issue list @@ -64,7 +67,7 @@ class FeedbackModule(MgrModule): return HandleCommandResult(stderr="Error occurred. Try again later") return HandleCommandResult(stdout=str(response)) - @CLIReadCommand('feedback issue report') + @FeedbackCLICommand.Read('feedback issue report') def _cmd_feedback_issue_report(self, project: str, tracker: str, subject: str, description: str) -> HandleCommandResult: """ Create an issue diff --git a/src/pybind/mgr/hello/cli.py b/src/pybind/mgr/hello/cli.py new file mode 100644 index 00000000000..9f6dcd34500 --- /dev/null +++ b/src/pybind/mgr/hello/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +HelloCLICommand = CLICommandBase.make_registry_subtype("HelloCLICommand") diff --git a/src/pybind/mgr/hello/module.py b/src/pybind/mgr/hello/module.py index e7a7fc710b6..f6f309ade35 100644 --- a/src/pybind/mgr/hello/module.py +++ b/src/pybind/mgr/hello/module.py @@ -10,8 +10,11 @@ from threading import Event from typing import cast, Any, Optional, TYPE_CHECKING import errno +from .cli import HelloCLICommand + class Hello(MgrModule): + CLICommand = HelloCLICommand # These are module options we understand. These can be set with # # ceph config set global mgr/hello/ @@ -77,7 +80,7 @@ class Hello(MgrModule): self.log.debug(' native option %s = %s', opt, getattr(self, opt)) # there are CLI commands we implement - @CLIReadCommand('hello') + @HelloCLICommand.Read('hello') def hello(self, person_name: Optional[str] = None) -> HandleCommandResult: """ Say hello @@ -89,7 +92,7 @@ class Hello(MgrModule): fin = '!' if self.get_module_option('emphatic') else '' return HandleCommandResult(stdout=f'Hello, {who}{fin}') - @CLIReadCommand('count') + @HelloCLICommand.Read('count') def count(self, num: int) -> HandleCommandResult: """ Do some counting diff --git a/src/pybind/mgr/influx/cli.py b/src/pybind/mgr/influx/cli.py new file mode 100644 index 00000000000..ab8cb847aa1 --- /dev/null +++ b/src/pybind/mgr/influx/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +InfluxCLICommand = CLICommandBase.make_registry_subtype("InfluxCLICommand") diff --git a/src/pybind/mgr/influx/module.py b/src/pybind/mgr/influx/module.py index 9735b71c658..b22ffc8f028 100644 --- a/src/pybind/mgr/influx/module.py +++ b/src/pybind/mgr/influx/module.py @@ -8,6 +8,8 @@ import errno import time from typing import cast, Any, Dict, Iterator, List, Optional, Tuple, Union +from .cli import InfluxCLICommand + from mgr_module import MgrModule, Option, OptionValue try: @@ -19,6 +21,7 @@ except ImportError: class Module(MgrModule): + CLICommand = InfluxCLICommand MODULE_OPTIONS = [ Option(name='hostname', default=None, @@ -425,14 +428,14 @@ class Module(MgrModule): return json.dumps(result, indent=2, sort_keys=True) - @CLIReadCommand('influx config-show') + @InfluxCLICommand.Read('influx config-show') def config_show(self) -> Tuple[int, str, str]: """ Show current configuration """ return 0, json.dumps(self.config, sort_keys=True), '' - @CLIWriteCommand('influx config-set') + @InfluxCLICommand.Write('influx config-set') def config_set(self, key: str, value: str) -> Tuple[int, str, str]: if not value: return -errno.EINVAL, '', 'Value should not be empty' @@ -445,7 +448,7 @@ class Module(MgrModule): except ValueError as e: return -errno.EINVAL, '', str(e) - @CLICommand('influx send') + @InfluxCLICommand('influx send') def send(self) -> Tuple[int, str, str]: """ Force sending data to Influx diff --git a/src/pybind/mgr/insights/cli.py b/src/pybind/mgr/insights/cli.py new file mode 100644 index 00000000000..a948d9235fd --- /dev/null +++ b/src/pybind/mgr/insights/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +InsightsCLICommand = CLICommandBase.make_registry_subtype("InsightsCLICommand") diff --git a/src/pybind/mgr/insights/module.py b/src/pybind/mgr/insights/module.py index 1e82f3dc0ad..6e8c6aa7d12 100644 --- a/src/pybind/mgr/insights/module.py +++ b/src/pybind/mgr/insights/module.py @@ -3,6 +3,8 @@ import json import re import threading +from .cli import InsightsCLICommand + from mgr_module import HandleCommandResult from mgr_module import MgrModule, CommandResult, NotifyType from . import health as health_util @@ -20,6 +22,7 @@ ON_DISK_VERSION = 1 class Module(MgrModule): + CLICommand = InsightsCLICommand NOTIFY_TYPES = [NotifyType.health] @@ -244,7 +247,7 @@ class Module(MgrModule): ret={}, outs=\"{}\"".format(ret, outs)) return [], ["Failed to read monitor config dump"] - @CLIReadCommand('insights') + @InsightsCLICommand.Read('insights') def do_report(self): ''' Retrieve insights report @@ -302,7 +305,7 @@ class Module(MgrModule): result = json.dumps(report, indent=2, cls=health_util.HealthEncoder) return HandleCommandResult(stdout=result) - @CLICommand('insights prune-health') + @InsightsCLICommand('insights prune-health') def do_prune_health(self, hours: int): ''' Remove health history older than hours diff --git a/src/pybind/mgr/iostat/cli.py b/src/pybind/mgr/iostat/cli.py new file mode 100644 index 00000000000..e8e0001fcf0 --- /dev/null +++ b/src/pybind/mgr/iostat/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +IostatCLICommand = CLICommandBase.make_registry_subtype("IostatCLICommand") diff --git a/src/pybind/mgr/iostat/module.py b/src/pybind/mgr/iostat/module.py index 4ef8dbbb044..7d12e9e8ef1 100644 --- a/src/pybind/mgr/iostat/module.py +++ b/src/pybind/mgr/iostat/module.py @@ -2,8 +2,12 @@ from typing import Any from mgr_module import HandleCommandResult, MgrModule +from .cli import IostatCLICommand + class Module(MgrModule): + CLICommand = IostatCLICommand + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -17,7 +21,7 @@ class Module(MgrModule): assert 'num_write' in r['pg_stats_delta']['stat_sum'] assert 'num_read' in r['pg_stats_delta']['stat_sum'] - @CLIReadCommand('iostat', poll=True) + @IostatCLICommand.Read('iostat', poll=True) def iostat(self, width: int = 80, print_header: bool = False) -> HandleCommandResult: """ Get IO rates diff --git a/src/pybind/mgr/k8sevents/cli.py b/src/pybind/mgr/k8sevents/cli.py new file mode 100644 index 00000000000..d004ab6f8ca --- /dev/null +++ b/src/pybind/mgr/k8sevents/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +K8SeventsCLICommand = CLICommandBase.make_registry_subtype("K8SeventsCLICommand") diff --git a/src/pybind/mgr/k8sevents/module.py b/src/pybind/mgr/k8sevents/module.py index 5855ba42e1b..8d4304f38a9 100644 --- a/src/pybind/mgr/k8sevents/module.py +++ b/src/pybind/mgr/k8sevents/module.py @@ -33,6 +33,8 @@ import logging import tempfile import threading +from .cli import K8SeventsCLICommand + from urllib.parse import urlparse from datetime import tzinfo, datetime, timedelta @@ -1012,6 +1014,7 @@ class CephConfigWatcher(BaseThread): class Module(MgrModule): + CLICommand = K8SeventsCLICommand COMMANDS = [ { "cmd": "k8sevents status", diff --git a/src/pybind/mgr/localpool/cli.py b/src/pybind/mgr/localpool/cli.py new file mode 100644 index 00000000000..cd4343b7810 --- /dev/null +++ b/src/pybind/mgr/localpool/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +LocalpoolCLICommand = CLICommandBase.make_registry_subtype("LocalpoolCLICommand") diff --git a/src/pybind/mgr/localpool/module.py b/src/pybind/mgr/localpool/module.py index 0706dff6519..de309b8274a 100644 --- a/src/pybind/mgr/localpool/module.py +++ b/src/pybind/mgr/localpool/module.py @@ -3,8 +3,11 @@ import json import threading from typing import cast, Any +from .cli import LocalpoolCLICommand + class Module(MgrModule): + CLICommand = LocalpoolCLICommand MODULE_OPTIONS = [ Option( diff --git a/src/pybind/mgr/mds_autoscaler/cli.py b/src/pybind/mgr/mds_autoscaler/cli.py new file mode 100644 index 00000000000..c4460f4f9f9 --- /dev/null +++ b/src/pybind/mgr/mds_autoscaler/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +MDSAutoscalerCLICommand = CLICommandBase.make_registry_subtype("MDSAutoscalerCLICommand") diff --git a/src/pybind/mgr/mds_autoscaler/module.py b/src/pybind/mgr/mds_autoscaler/module.py index 2f780059c0b..b5a418e5ac1 100644 --- a/src/pybind/mgr/mds_autoscaler/module.py +++ b/src/pybind/mgr/mds_autoscaler/module.py @@ -9,10 +9,13 @@ from orchestrator._interface import MDSSpec, ServiceSpec import orchestrator import copy +from .cli import MDSAutoscalerCLICommand + log = logging.getLogger(__name__) class MDSAutoscaler(orchestrator.OrchestratorClientMixin, MgrModule): + CLICommand = MDSAutoscalerCLICommand """ MDS autoscaler. """ diff --git a/src/pybind/mgr/mirroring/cli.py b/src/pybind/mgr/mirroring/cli.py new file mode 100644 index 00000000000..11bd581549b --- /dev/null +++ b/src/pybind/mgr/mirroring/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +MirroringCLICommand = CLICommandBase.make_registry_subtype("MirroringCLICommand") diff --git a/src/pybind/mgr/mirroring/module.py b/src/pybind/mgr/mirroring/module.py index 01dfe301253..10201bac1bc 100644 --- a/src/pybind/mgr/mirroring/module.py +++ b/src/pybind/mgr/mirroring/module.py @@ -1,10 +1,13 @@ from typing import List, Optional +from .cli import MirroringCLICommand + from mgr_module import MgrModule, Option, NotifyType from .fs.snapshot_mirror import FSSnapshotMirror class Module(MgrModule): + CLICommand = MirroringCLICommand MODULE_OPTIONS: List[Option] = [] NOTIFY_TYPES = [NotifyType.fs_map] @@ -15,19 +18,19 @@ class Module(MgrModule): def notify(self, notify_type: NotifyType, notify_id): self.fs_snapshot_mirror.notify(notify_type) - @CLIWriteCommand('fs snapshot mirror enable') + @MirroringCLICommand.Write('fs snapshot mirror enable') def snapshot_mirror_enable(self, fs_name: str): """Enable snapshot mirroring for a filesystem""" return self.fs_snapshot_mirror.enable_mirror(fs_name) - @CLIWriteCommand('fs snapshot mirror disable') + @MirroringCLICommand.Write('fs snapshot mirror disable') def snapshot_mirror_disable(self, fs_name: str): """Disable snapshot mirroring for a filesystem""" return self.fs_snapshot_mirror.disable_mirror(fs_name) - @CLIWriteCommand('fs snapshot mirror peer_add') + @MirroringCLICommand.Write('fs snapshot mirror peer_add') def snapshot_mirorr_peer_add(self, fs_name: str, remote_cluster_spec: str, @@ -46,20 +49,20 @@ class Module(MgrModule): "future release. Use 'peer_bootstrap' instead.\n") return r, out, err - @CLIReadCommand('fs snapshot mirror peer_list') + @MirroringCLICommand.Read('fs snapshot mirror peer_list') def snapshot_mirror_peer_list(self, fs_name: str): """List configured peers for a file system""" return self.fs_snapshot_mirror.peer_list(fs_name) - @CLIWriteCommand('fs snapshot mirror peer_remove') + @MirroringCLICommand.Write('fs snapshot mirror peer_remove') def snapshot_mirror_peer_remove(self, fs_name: str, peer_uuid: str): """Remove a filesystem peer""" return self.fs_snapshot_mirror.peer_remove(fs_name, peer_uuid) - @CLIWriteCommand('fs snapshot mirror peer_bootstrap create') + @MirroringCLICommand.Write('fs snapshot mirror peer_bootstrap create') def snapshot_mirror_peer_bootstrap_create(self, fs_name: str, client_name: str, @@ -67,47 +70,47 @@ class Module(MgrModule): """Bootstrap a filesystem peer""" return self.fs_snapshot_mirror.peer_bootstrap_create(fs_name, client_name, site_name) - @CLIWriteCommand('fs snapshot mirror peer_bootstrap import') + @MirroringCLICommand.Write('fs snapshot mirror peer_bootstrap import') def snapshot_mirror_peer_bootstrap_import(self, fs_name: str, token: str): """Import a bootstrap token""" return self.fs_snapshot_mirror.peer_bootstrap_import(fs_name, token) - @CLIWriteCommand('fs snapshot mirror add') + @MirroringCLICommand.Write('fs snapshot mirror add') def snapshot_mirror_add_dir(self, fs_name: str, path: str): """Add a directory for snapshot mirroring""" return self.fs_snapshot_mirror.add_dir(fs_name, path) - @CLIWriteCommand('fs snapshot mirror remove') + @MirroringCLICommand.Write('fs snapshot mirror remove') def snapshot_mirror_remove_dir(self, fs_name: str, path: str): """Remove a snapshot mirrored directory""" return self.fs_snapshot_mirror.remove_dir(fs_name, path) - @CLIReadCommand('fs snapshot mirror ls') + @MirroringCLICommand.Read('fs snapshot mirror ls') def snapshot_mirror_ls(self, fs_name: str): """List the snapshot mirrored directories""" return self.fs_snapshot_mirror.list_dirs(fs_name) - @CLIReadCommand('fs snapshot mirror dirmap') + @MirroringCLICommand.Read('fs snapshot mirror dirmap') def snapshot_mirror_dirmap(self, fs_name: str, path: str): """Get current mirror instance map for a directory""" return self.fs_snapshot_mirror.status(fs_name, path) - @CLIReadCommand('fs snapshot mirror show distribution') + @MirroringCLICommand.Read('fs snapshot mirror show distribution') def snapshot_mirror_distribution(self, fs_name: str): """Get current instance to directory map for a filesystem""" return self.fs_snapshot_mirror.show_distribution(fs_name) - @CLIReadCommand('fs snapshot mirror daemon status') + @MirroringCLICommand.Read('fs snapshot mirror daemon status') def snapshot_mirror_daemon_status(self): """Get mirror daemon status""" return self.fs_snapshot_mirror.daemon_status() diff --git a/src/pybind/mgr/nfs/cli.py b/src/pybind/mgr/nfs/cli.py new file mode 100644 index 00000000000..8c846302812 --- /dev/null +++ b/src/pybind/mgr/nfs/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +NFSCLICommand = CLICommandBase.make_registry_subtype("NFSCLICommand") diff --git a/src/pybind/mgr/nfs/module.py b/src/pybind/mgr/nfs/module.py index 0b18988c181..716ee205e69 100644 --- a/src/pybind/mgr/nfs/module.py +++ b/src/pybind/mgr/nfs/module.py @@ -3,6 +3,8 @@ import threading from typing import Tuple, Optional, List, Dict, Any import yaml +from .cli import NFSCLICommand + from mgr_module import MgrModule, Option, CLICheckNonemptyFileInput import object_format import orchestrator @@ -17,6 +19,7 @@ log = logging.getLogger(__name__) class Module(orchestrator.OrchestratorClientMixin, MgrModule): + CLICommand = NFSCLICommand MODULE_OPTIONS: List[Option] = [] def __init__(self, *args: str, **kwargs: Any) -> None: @@ -28,7 +31,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): self.nfs = NFSCluster(self) self.inited = True - @CLICommand('nfs export create cephfs', perm='rw') + @NFSCLICommand('nfs export create cephfs', perm='rw') @object_format.Responder() def _cmd_nfs_export_create_cephfs( self, @@ -58,7 +61,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): earmark_resolver=earmark_resolver ) - @CLICommand('nfs export create rgw', perm='rw') + @NFSCLICommand('nfs export create rgw', perm='rw') @object_format.Responder() def _cmd_nfs_export_create_rgw( self, @@ -84,37 +87,37 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): sectype=sectype, ) - @CLICommand('nfs export rm', perm='rw') + @NFSCLICommand('nfs export rm', perm='rw') @object_format.EmptyResponder() def _cmd_nfs_export_rm(self, cluster_id: str, pseudo_path: str) -> None: """Remove a cephfs export""" return self.export_mgr.delete_export(cluster_id=cluster_id, pseudo_path=pseudo_path) - @CLICommand('nfs export delete', perm='rw') + @NFSCLICommand('nfs export delete', perm='rw') @object_format.EmptyResponder() def _cmd_nfs_export_delete(self, cluster_id: str, pseudo_path: str) -> None: """Delete a cephfs export (DEPRECATED)""" return self.export_mgr.delete_export(cluster_id=cluster_id, pseudo_path=pseudo_path) - @CLICommand('nfs export ls', perm='r') + @NFSCLICommand('nfs export ls', perm='r') @object_format.Responder() def _cmd_nfs_export_ls(self, cluster_id: str, detailed: bool = False) -> List[Any]: """List exports of a NFS cluster""" return self.export_mgr.list_exports(cluster_id=cluster_id, detailed=detailed) - @CLICommand('nfs export info', perm='r') + @NFSCLICommand('nfs export info', perm='r') @object_format.Responder() def _cmd_nfs_export_info(self, cluster_id: str, pseudo_path: str) -> Dict[str, Any]: """Fetch a export of a NFS cluster given the pseudo path/binding""" return self.export_mgr.get_export(cluster_id=cluster_id, pseudo_path=pseudo_path) - @CLICommand('nfs export get', perm='r') + @NFSCLICommand('nfs export get', perm='r') @object_format.Responder() def _cmd_nfs_export_get(self, cluster_id: str, pseudo_path: str) -> Dict[str, Any]: """Fetch a export of a NFS cluster given the pseudo path/binding (DEPRECATED)""" return self.export_mgr.get_export(cluster_id=cluster_id, pseudo_path=pseudo_path) - @CLICommand('nfs export apply', perm='rw') + @NFSCLICommand('nfs export apply', perm='rw') @CLICheckNonemptyFileInput(desc='Export JSON or Ganesha EXPORT specification') @object_format.Responder() def _cmd_nfs_export_apply(self, cluster_id: str, inbuf: str) -> AppliedExportResults: @@ -123,7 +126,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): return self.export_mgr.apply_export(cluster_id, export_config=inbuf, earmark_resolver=earmark_resolver) - @CLICommand('nfs cluster create', perm='rw') + @NFSCLICommand('nfs cluster create', perm='rw') @object_format.EmptyResponder() def _cmd_nfs_cluster_create(self, cluster_id: str, @@ -159,45 +162,45 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): tls_min_version=tls_min_version, tls_ciphers=tls_ciphers) - @CLICommand('nfs cluster rm', perm='rw') + @NFSCLICommand('nfs cluster rm', perm='rw') @object_format.EmptyResponder() def _cmd_nfs_cluster_rm(self, cluster_id: str) -> None: """Removes an NFS Cluster""" return self.nfs.delete_nfs_cluster(cluster_id=cluster_id) - @CLICommand('nfs cluster delete', perm='rw') + @NFSCLICommand('nfs cluster delete', perm='rw') @object_format.EmptyResponder() def _cmd_nfs_cluster_delete(self, cluster_id: str) -> None: """Removes an NFS Cluster (DEPRECATED)""" return self.nfs.delete_nfs_cluster(cluster_id=cluster_id) - @CLICommand('nfs cluster ls', perm='r') + @NFSCLICommand('nfs cluster ls', perm='r') @object_format.Responder() def _cmd_nfs_cluster_ls(self) -> List[str]: """List NFS Clusters""" return self.nfs.list_nfs_cluster() - @CLICommand('nfs cluster info', perm='r') + @NFSCLICommand('nfs cluster info', perm='r') @object_format.Responder() def _cmd_nfs_cluster_info(self, cluster_id: Optional[str] = None) -> Dict[str, Any]: """Displays NFS Cluster info""" return self.nfs.show_nfs_cluster_info(cluster_id=cluster_id) - @CLICommand('nfs cluster config get', perm='r') + @NFSCLICommand('nfs cluster config get', perm='r') @object_format.ErrorResponseHandler() def _cmd_nfs_cluster_config_get(self, cluster_id: str) -> Tuple[int, str, str]: """Fetch NFS-Ganesha config""" conf = self.nfs.get_nfs_cluster_config(cluster_id=cluster_id) return 0, conf, "" - @CLICommand('nfs cluster config set', perm='rw') + @NFSCLICommand('nfs cluster config set', perm='rw') @CLICheckNonemptyFileInput(desc='NFS-Ganesha Configuration') @object_format.EmptyResponder() def _cmd_nfs_cluster_config_set(self, cluster_id: str, inbuf: str) -> None: """Set NFS-Ganesha config by `-i `""" return self.nfs.set_nfs_cluster_config(cluster_id=cluster_id, nfs_config=inbuf) - @CLICommand('nfs cluster config reset', perm='rw') + @NFSCLICommand('nfs cluster config reset', perm='rw') @object_format.EmptyResponder() def _cmd_nfs_cluster_config_reset(self, cluster_id: str) -> None: """Reset NFS-Ganesha Config to default""" diff --git a/src/pybind/mgr/osd_perf_query/cli.py b/src/pybind/mgr/osd_perf_query/cli.py new file mode 100644 index 00000000000..24e7935e516 --- /dev/null +++ b/src/pybind/mgr/osd_perf_query/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +OSDPerfQueryCLICommand = CLICommandBase.make_registry_subtype("OSDPerfQueryCLICommand") diff --git a/src/pybind/mgr/osd_perf_query/module.py b/src/pybind/mgr/osd_perf_query/module.py index ac32f804282..2bfbe9c8afd 100644 --- a/src/pybind/mgr/osd_perf_query/module.py +++ b/src/pybind/mgr/osd_perf_query/module.py @@ -8,6 +8,8 @@ from time import time import errno import prettytable +from .cli import OSDPerfQueryCLICommand + from mgr_module import MgrModule def get_human_readable(bytes, precision=2): @@ -21,6 +23,7 @@ def get_human_readable(bytes, precision=2): return '%.*f%s' % (precision, bytes, suffixes[suffix_index]) class OSDPerfQuery(MgrModule): + CLICommand = OSDPerfQueryCLICommand COMMANDS = [ { "cmd": "osd perf query add " diff --git a/src/pybind/mgr/osd_support/cli.py b/src/pybind/mgr/osd_support/cli.py new file mode 100644 index 00000000000..4e5088c8439 --- /dev/null +++ b/src/pybind/mgr/osd_support/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +OSDSupportCLICommand = CLICommandBase.make_registry_subtype("OSDSupportCLICommand") diff --git a/src/pybind/mgr/osd_support/module.py b/src/pybind/mgr/osd_support/module.py index 1f3e137bbc2..ff961d59d00 100644 --- a/src/pybind/mgr/osd_support/module.py +++ b/src/pybind/mgr/osd_support/module.py @@ -1,7 +1,10 @@ from mgr_module import MgrModule +from .cli import OSDSupportCLICommand + class OSDSupport(MgrModule): + CLICommand = OSDSupportCLICommand # Kept to keep upgrades from older point releases working. # This module can be removed as soon as we no longer # support upgrades from old octopus point releases. diff --git a/src/pybind/mgr/pg_autoscaler/cli.py b/src/pybind/mgr/pg_autoscaler/cli.py new file mode 100644 index 00000000000..dbe25410ce0 --- /dev/null +++ b/src/pybind/mgr/pg_autoscaler/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +PGAutoscalerCLICommand = CLICommandBase.make_registry_subtype("PGAutoscalerCLICommand") diff --git a/src/pybind/mgr/pg_autoscaler/module.py b/src/pybind/mgr/pg_autoscaler/module.py index ad5ebc14d4e..eb96766dd1a 100644 --- a/src/pybind/mgr/pg_autoscaler/module.py +++ b/src/pybind/mgr/pg_autoscaler/module.py @@ -10,6 +10,8 @@ import uuid from prettytable import PrettyTable from mgr_module import HealthChecksT, CRUSHMap, MgrModule, Option, OSDMap +from .cli import PGAutoscalerCLICommand + """ Some terminology is made up for the purposes of this module: @@ -113,6 +115,7 @@ class CrushSubtreeResourceStatus: class PgAutoscaler(MgrModule): + CLICommand = PGAutoscalerCLICommand """ PG autoscaler. """ @@ -163,7 +166,7 @@ class PgAutoscaler(MgrModule): self.log.debug(' mgr option %s = %s', opt['name'], getattr(self, opt['name'])) - @CLIReadCommand('osd pool autoscale-status') + @PGAutoscalerCLICommand.Read('osd pool autoscale-status') def _command_autoscale_status(self, format: str = 'plain') -> Tuple[int, str, str]: """ report on pool pg_num sizing recommendation and intent @@ -236,7 +239,7 @@ class PgAutoscaler(MgrModule): ]) return 0, table.get_string(), '' - @CLIWriteCommand("osd pool set threshold") + @PGAutoscalerCLICommand.Write("osd pool set threshold") def set_scaling_threshold(self, num: float) -> Tuple[int, str, str]: """ set the autoscaler threshold @@ -248,7 +251,7 @@ class PgAutoscaler(MgrModule): self.set_module_option("threshold", num) return 0, "threshold updated", "" - @CLIReadCommand("osd pool get threshold") + @PGAutoscalerCLICommand.Read("osd pool get threshold") def get_scaling_threshold(self) -> Tuple[int, str, str]: """ return the autoscaler threshold value @@ -277,7 +280,7 @@ class PgAutoscaler(MgrModule): else: return False - @CLIWriteCommand("osd pool get noautoscale") + @PGAutoscalerCLICommand.Write("osd pool get noautoscale") def get_noautoscale(self) -> Tuple[int, str, str]: """ Get the noautoscale flag to see if all pools @@ -289,7 +292,7 @@ class PgAutoscaler(MgrModule): else: return 0, "", "noautoscale is off" - @CLIWriteCommand("osd pool unset noautoscale") + @PGAutoscalerCLICommand.Write("osd pool unset noautoscale") def unset_noautoscale(self) -> Tuple[int, str, str]: """ Unset the noautoscale flag so all pools will @@ -311,7 +314,7 @@ class PgAutoscaler(MgrModule): }) return 0, "", "noautoscale is unset, all pools now back to its previous mode" - @CLIWriteCommand("osd pool set noautoscale") + @PGAutoscalerCLICommand.Write("osd pool set noautoscale") def set_noautoscale(self) -> Tuple[int, str, str]: """ set the noautoscale for all pools (including diff --git a/src/pybind/mgr/progress/cli.py b/src/pybind/mgr/progress/cli.py new file mode 100644 index 00000000000..0d497812faf --- /dev/null +++ b/src/pybind/mgr/progress/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +ProgressCLICommand = CLICommandBase.make_registry_subtype("ProgressCLICommand") diff --git a/src/pybind/mgr/progress/module.py b/src/pybind/mgr/progress/module.py index 6339cc860cb..1a82d4e79f3 100644 --- a/src/pybind/mgr/progress/module.py +++ b/src/pybind/mgr/progress/module.py @@ -15,6 +15,8 @@ import time import logging import json +from .cli import ProgressCLICommand + ENCODING_VERSION = 2 @@ -415,6 +417,7 @@ class PgId(object): class Module(MgrModule): + CLICommand = ProgressCLICommand COMMANDS = [ {"cmd": "progress", "desc": "Show progress of recovery operations", diff --git a/src/pybind/mgr/prometheus/cli.py b/src/pybind/mgr/prometheus/cli.py new file mode 100644 index 00000000000..d918cf0ae07 --- /dev/null +++ b/src/pybind/mgr/prometheus/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +PrometheusCLICommand = CLICommandBase.make_registry_subtype("PrometheusCLICommand") diff --git a/src/pybind/mgr/prometheus/module.py b/src/pybind/mgr/prometheus/module.py index 4e363bf10a9..26b0da41d6d 100644 --- a/src/pybind/mgr/prometheus/module.py +++ b/src/pybind/mgr/prometheus/module.py @@ -11,6 +11,8 @@ from collections import namedtuple from collections import OrderedDict from tempfile import NamedTemporaryFile +from .cli import PrometheusCLICommand + from mgr_module import MgrModule, MgrStandbyModule, PG_STATES, Option, ServiceInfoT, HandleCommandResult from mgr_util import get_default_addr, profile_method, build_url, test_port_allocation, PortAlreadyInUse from orchestrator import OrchestratorClientMixin, raise_if_exception, OrchestratorError @@ -597,6 +599,7 @@ class MetricCollectionThread(threading.Thread): class Module(MgrModule, OrchestratorClientMixin): + CLICommand = PrometheusCLICommand MODULE_OPTIONS = [ Option( 'server_addr', @@ -1932,7 +1935,7 @@ class Module(MgrModule, OrchestratorClientMixin): return ''.join(_metrics) + '\n' - @CLIReadCommand('prometheus file_sd_config') + @PrometheusCLICommand.Read('prometheus file_sd_config') def get_file_sd_config(self) -> Tuple[int, str, str]: ''' Return file_sd compatible prometheus config for mgr cluster @@ -2205,7 +2208,7 @@ class Module(MgrModule, OrchestratorClientMixin): self.log.info('Stopping engine...') self.shutdown_event.set() - @CLIReadCommand('healthcheck history ls') + @PrometheusCLICommand.Read('healthcheck history ls') def _list_healthchecks(self, format: Format = Format.plain) -> HandleCommandResult: """List all the healthchecks being tracked @@ -2230,7 +2233,7 @@ class Module(MgrModule, OrchestratorClientMixin): return HandleCommandResult(retval=0, stdout=out) - @CLIWriteCommand('healthcheck history clear') + @PrometheusCLICommand.Write('healthcheck history clear') def _clear_healthchecks(self) -> HandleCommandResult: """Clear the healthcheck history""" self.health_history.reset() diff --git a/src/pybind/mgr/rbd_support/cli.py b/src/pybind/mgr/rbd_support/cli.py new file mode 100644 index 00000000000..c654913c9c1 --- /dev/null +++ b/src/pybind/mgr/rbd_support/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +RBDSupportCLICommand = CLICommandBase.make_registry_subtype("RBDSupportCLICommand") diff --git a/src/pybind/mgr/rbd_support/module.py b/src/pybind/mgr/rbd_support/module.py index 6934905d813..d8294eb15b6 100644 --- a/src/pybind/mgr/rbd_support/module.py +++ b/src/pybind/mgr/rbd_support/module.py @@ -11,6 +11,8 @@ import rbd import traceback from typing import cast, Any, Callable, Optional, Tuple, TypeVar +from .cli import RBDSupportCLICommand + from mgr_module import MgrModule, Option from threading import Thread, Event @@ -73,6 +75,7 @@ def with_latest_osdmap(func: FuncT) -> FuncT: class Module(MgrModule): + CLICommand = RBDSupportCLICommand MODULE_OPTIONS = [ Option(name=MirrorSnapshotScheduleHandler.MODULE_OPTION_NAME), Option(name=MirrorSnapshotScheduleHandler.MODULE_OPTION_NAME_MAX_CONCURRENT_SNAP_CREATE, @@ -136,7 +139,7 @@ class Module(MgrModule): # shut down client and deregister it from MgrMap super().shutdown() - @CLIWriteCommand('rbd mirror snapshot schedule add') + @RBDSupportCLICommand.Write('rbd mirror snapshot schedule add') @with_latest_osdmap def mirror_snapshot_schedule_add(self, level_spec: str, @@ -148,7 +151,7 @@ class Module(MgrModule): spec = LevelSpec.from_name(self, level_spec, namespace_validator, image_validator) return self.mirror_snapshot_schedule.add_schedule(spec, interval, start_time) - @CLIWriteCommand('rbd mirror snapshot schedule remove') + @RBDSupportCLICommand.Write('rbd mirror snapshot schedule remove') @with_latest_osdmap def mirror_snapshot_schedule_remove(self, level_spec: str, @@ -160,7 +163,7 @@ class Module(MgrModule): spec = LevelSpec.from_name(self, level_spec, namespace_validator, image_validator) return self.mirror_snapshot_schedule.remove_schedule(spec, interval, start_time) - @CLIReadCommand('rbd mirror snapshot schedule list') + @RBDSupportCLICommand.Read('rbd mirror snapshot schedule list') @with_latest_osdmap def mirror_snapshot_schedule_list(self, level_spec: str = '') -> Tuple[int, str, str]: @@ -170,7 +173,7 @@ class Module(MgrModule): spec = LevelSpec.from_name(self, level_spec, namespace_validator, image_validator) return self.mirror_snapshot_schedule.list(spec) - @CLIReadCommand('rbd mirror snapshot schedule status') + @RBDSupportCLICommand.Read('rbd mirror snapshot schedule status') @with_latest_osdmap def mirror_snapshot_schedule_status(self, level_spec: str = '') -> Tuple[int, str, str]: @@ -180,7 +183,7 @@ class Module(MgrModule): spec = LevelSpec.from_name(self, level_spec, namespace_validator, image_validator) return self.mirror_snapshot_schedule.status(spec) - @CLIReadCommand('rbd perf image stats') + @RBDSupportCLICommand.Read('rbd perf image stats') @with_latest_osdmap def perf_image_stats(self, pool_spec: Optional[str] = None, @@ -192,7 +195,7 @@ class Module(MgrModule): sort_by_name = sort_by.name if sort_by else OSD_PERF_QUERY_COUNTERS[0] return self.perf.get_perf_stats(pool_spec, sort_by_name) - @CLIReadCommand('rbd perf image counters') + @RBDSupportCLICommand.Read('rbd perf image counters') @with_latest_osdmap def perf_image_counters(self, pool_spec: Optional[str] = None, @@ -204,7 +207,7 @@ class Module(MgrModule): sort_by_name = sort_by.name if sort_by else OSD_PERF_QUERY_COUNTERS[0] return self.perf.get_perf_counters(pool_spec, sort_by_name) - @CLIWriteCommand('rbd task add flatten') + @RBDSupportCLICommand.Write('rbd task add flatten') @with_latest_osdmap def task_add_flatten(self, image_spec: str) -> Tuple[int, str, str]: """ @@ -213,7 +216,7 @@ class Module(MgrModule): with self.task.lock: return self.task.queue_flatten(image_spec) - @CLIWriteCommand('rbd task add remove') + @RBDSupportCLICommand.Write('rbd task add remove') @with_latest_osdmap def task_add_remove(self, image_spec: str) -> Tuple[int, str, str]: """ @@ -222,7 +225,7 @@ class Module(MgrModule): with self.task.lock: return self.task.queue_remove(image_spec) - @CLIWriteCommand('rbd task add trash remove') + @RBDSupportCLICommand.Write('rbd task add trash remove') @with_latest_osdmap def task_add_trash_remove(self, image_id_spec: str) -> Tuple[int, str, str]: """ @@ -231,7 +234,7 @@ class Module(MgrModule): with self.task.lock: return self.task.queue_trash_remove(image_id_spec) - @CLIWriteCommand('rbd task add migration execute') + @RBDSupportCLICommand.Write('rbd task add migration execute') @with_latest_osdmap def task_add_migration_execute(self, image_spec: str) -> Tuple[int, str, str]: """ @@ -240,7 +243,7 @@ class Module(MgrModule): with self.task.lock: return self.task.queue_migration_execute(image_spec) - @CLIWriteCommand('rbd task add migration commit') + @RBDSupportCLICommand.Write('rbd task add migration commit') @with_latest_osdmap def task_add_migration_commit(self, image_spec: str) -> Tuple[int, str, str]: """ @@ -249,7 +252,7 @@ class Module(MgrModule): with self.task.lock: return self.task.queue_migration_commit(image_spec) - @CLIWriteCommand('rbd task add migration abort') + @RBDSupportCLICommand.Write('rbd task add migration abort') @with_latest_osdmap def task_add_migration_abort(self, image_spec: str) -> Tuple[int, str, str]: """ @@ -258,7 +261,7 @@ class Module(MgrModule): with self.task.lock: return self.task.queue_migration_abort(image_spec) - @CLIWriteCommand('rbd task cancel') + @RBDSupportCLICommand.Write('rbd task cancel') @with_latest_osdmap def task_cancel(self, task_id: str) -> Tuple[int, str, str]: """ @@ -267,7 +270,7 @@ class Module(MgrModule): with self.task.lock: return self.task.task_cancel(task_id) - @CLIReadCommand('rbd task list') + @RBDSupportCLICommand.Read('rbd task list') @with_latest_osdmap def task_list(self, task_id: Optional[str] = None) -> Tuple[int, str, str]: """ @@ -276,7 +279,7 @@ class Module(MgrModule): with self.task.lock: return self.task.task_list(task_id) - @CLIWriteCommand('rbd trash purge schedule add') + @RBDSupportCLICommand.Write('rbd trash purge schedule add') @with_latest_osdmap def trash_purge_schedule_add(self, level_spec: str, @@ -288,7 +291,7 @@ class Module(MgrModule): spec = LevelSpec.from_name(self, level_spec, allow_image_level=False) return self.trash_purge_schedule.add_schedule(spec, interval, start_time) - @CLIWriteCommand('rbd trash purge schedule remove') + @RBDSupportCLICommand.Write('rbd trash purge schedule remove') @with_latest_osdmap def trash_purge_schedule_remove(self, level_spec: str, @@ -300,7 +303,7 @@ class Module(MgrModule): spec = LevelSpec.from_name(self, level_spec, allow_image_level=False) return self.trash_purge_schedule.remove_schedule(spec, interval, start_time) - @CLIReadCommand('rbd trash purge schedule list') + @RBDSupportCLICommand.Read('rbd trash purge schedule list') @with_latest_osdmap def trash_purge_schedule_list(self, level_spec: str = '') -> Tuple[int, str, str]: @@ -310,7 +313,7 @@ class Module(MgrModule): spec = LevelSpec.from_name(self, level_spec, allow_image_level=False) return self.trash_purge_schedule.list(spec) - @CLIReadCommand('rbd trash purge schedule status') + @RBDSupportCLICommand.Read('rbd trash purge schedule status') @with_latest_osdmap def trash_purge_schedule_status(self, level_spec: str = '') -> Tuple[int, str, str]: diff --git a/src/pybind/mgr/rgw/cli.py b/src/pybind/mgr/rgw/cli.py new file mode 100644 index 00000000000..48dd43b7c39 --- /dev/null +++ b/src/pybind/mgr/rgw/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +RGWCLICommand = CLICommandBase.make_registry_subtype("RGWCLICommand") diff --git a/src/pybind/mgr/rgw/module.py b/src/pybind/mgr/rgw/module.py index d77eed2362c..f7749a26dd5 100644 --- a/src/pybind/mgr/rgw/module.py +++ b/src/pybind/mgr/rgw/module.py @@ -6,6 +6,8 @@ import base64 import functools import sys +from .cli import RGWCLICommand + from mgr_module import ( MgrModule, HandleCommandResult, @@ -110,6 +112,7 @@ def check_orchestrator(func: FuncT) -> FuncT: class Module(orchestrator.OrchestratorClientMixin, MgrModule): + CLICommand = RGWCLICommand MODULE_OPTIONS: List[Option] = [ Option( 'secondary_zone_period_retry_limit', @@ -162,7 +165,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): self.get_ceph_option(opt)) self.log.debug(' native option %s = %s', opt, getattr(self, opt)) # type: ignore - @CLICommand('rgw admin', perm='rw') + @RGWCLICommand('rgw admin', perm='rw') def _cmd_rgw_admin(self, params: Sequence[str]) -> HandleCommandResult: """rgw admin""" cmd, returncode, out, err = self.env.mgr.tool_exec('radosgw-admin', params or []) @@ -173,7 +176,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): return HandleCommandResult(retval=returncode, stdout=out, stderr=err) - @CLICommand('rgw realm bootstrap', perm='rw') + @RGWCLICommand('rgw realm bootstrap', perm='rw') @check_orchestrator def _cmd_rgw_realm_bootstrap(self, realm_name: Optional[str] = None, @@ -354,7 +357,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): f'with attrs {profile_attrs}: {str(e)}') return profile_name - @CLICommand('rgw realm zone-creds create', perm='rw') + @RGWCLICommand('rgw realm zone-creds create', perm='rw') def _cmd_rgw_realm_new_zone_creds(self, realm_name: Optional[str] = None, endpoints: Optional[str] = None, @@ -369,7 +372,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): return HandleCommandResult(retval=retval, stdout=out, stderr=err) - @CLICommand('rgw realm zone-creds remove', perm='rw') + @RGWCLICommand('rgw realm zone-creds remove', perm='rw') def _cmd_rgw_realm_rm_zone_creds(self, realm_token: Optional[str] = None) -> HandleCommandResult: """Create credentials for new zone creation""" @@ -381,7 +384,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): return HandleCommandResult(retval=retval, stdout=out, stderr=err) - @CLICommand('rgw realm tokens', perm='r') + @RGWCLICommand('rgw realm tokens', perm='r') def list_realm_tokens(self) -> HandleCommandResult: try: realms_info = self.get_realm_tokens() @@ -408,7 +411,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): realms_info.append({'realm': realm_info['realm_name'], 'token': realm_token_s}) return realms_info - @CLICommand('rgw zone modify', perm='rw') + @RGWCLICommand('rgw zone modify', perm='rw') def update_zone_info(self, realm_name: str, zonegroup_name: str, zone_name: str, realm_token: str, zone_endpoints: List[str]) -> HandleCommandResult: try: retval, out, err = RGWAM(self.env).zone_modify(realm_name, @@ -421,7 +424,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): self.log.error('cmd run exception: (%d) %s' % (e.retcode, e.message)) return HandleCommandResult(retval=e.retcode, stdout=e.stdout, stderr=e.stderr) - @CLICommand('rgw zonegroup modify', perm='rw') + @RGWCLICommand('rgw zonegroup modify', perm='rw') def update_zonegroup_info(self, realm_name: str, zonegroup_name: str, zone_name: str, hostnames: List[str]) -> HandleCommandResult: try: retval, out, err = RGWAM(self.env).zonegroup_modify(realm_name, @@ -433,7 +436,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): self.log.error('cmd run exception: (%d) %s' % (e.retcode, e.message)) return HandleCommandResult(retval=e.retcode, stdout=e.stdout, stderr=e.stderr) - @CLICommand('rgw zone create', perm='rw') + @RGWCLICommand('rgw zone create', perm='rw') @check_orchestrator def _cmd_rgw_zone_create(self, zone_name: Optional[str] = None, @@ -496,7 +499,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): raise e return created_zones - @CLICommand('rgw realm reconcile', perm='rw') + @RGWCLICommand('rgw realm reconcile', perm='rw') def _cmd_rgw_realm_reconcile(self, realm_name: Optional[str] = None, zonegroup_name: Optional[str] = None, diff --git a/src/pybind/mgr/rook/cli.py b/src/pybind/mgr/rook/cli.py new file mode 100644 index 00000000000..f7cfe117cb1 --- /dev/null +++ b/src/pybind/mgr/rook/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +RookCLICommand = CLICommandBase.make_registry_subtype("RookCLICommand") diff --git a/src/pybind/mgr/rook/module.py b/src/pybind/mgr/rook/module.py index 177c75c4a17..82d2e8beced 100644 --- a/src/pybind/mgr/rook/module.py +++ b/src/pybind/mgr/rook/module.py @@ -10,6 +10,8 @@ import time from typing import Optional, Dict, Union, Tuple, Type, Optional from functools import wraps +from .cli import RookCLICommand + from ceph.deployment import inventory from ceph.deployment.service_spec import ServiceSpec, NFSServiceSpec, RGWSpec, PlacementSpec from ceph.utils import datetime_now @@ -100,6 +102,7 @@ class RookEnv(object): class RookOrchestrator(MgrModule, orchestrator.Orchestrator): + CLICommand = RookCLICommand """ Writes are a two-phase thing, firstly sending the write to the k8s API (fast) and then waiting diff --git a/src/pybind/mgr/selftest/cli.py b/src/pybind/mgr/selftest/cli.py new file mode 100644 index 00000000000..f75a33e3fd5 --- /dev/null +++ b/src/pybind/mgr/selftest/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +SelftestCLICommand = CLICommandBase.make_registry_subtype("SelftestCLICommand") diff --git a/src/pybind/mgr/selftest/module.py b/src/pybind/mgr/selftest/module.py index a4e8f65476f..65b4c6cdf5e 100644 --- a/src/pybind/mgr/selftest/module.py +++ b/src/pybind/mgr/selftest/module.py @@ -10,6 +10,8 @@ from contextlib import redirect_stderr, redirect_stdout from io import StringIO from typing import Any, Dict, List, Optional, Tuple +from .cli import SelftestCLICommand + # These workloads are things that can be requested to run inside the # serve() function @@ -20,6 +22,7 @@ class Workload(enum.Enum): class Module(MgrModule): + CLICommand = SelftestCLICommand """ This module is for testing the ceph-mgr python interface from within a running ceph-mgr daemon. @@ -65,7 +68,7 @@ class Module(MgrModule): self._health: Dict[str, Dict[str, Any]] = {} self._repl = InteractiveInterpreter(dict(mgr=self)) - @CLICommand('mgr self-test python-version', perm='r') + @SelftestCLICommand('mgr self-test python-version', perm='r') def python_version(self) -> Tuple[int, str, str]: ''' Query the version of the embedded Python runtime @@ -75,7 +78,7 @@ class Module(MgrModule): micro = sys.version_info.micro return 0, f'{major}.{minor}.{micro}', '' - @CLICommand('mgr self-test run') + @SelftestCLICommand('mgr self-test run') def run(self) -> Tuple[int, str, str]: ''' Run mgr python interface tests @@ -83,7 +86,7 @@ class Module(MgrModule): self._self_test() return 0, '', 'Self-test succeeded' - @CLICommand('mgr self-test background start') + @SelftestCLICommand('mgr self-test background start') def backgroun_start(self, workload: Workload) -> Tuple[int, str, str]: ''' Activate a background workload (one of command_spam, throw_exception) @@ -92,7 +95,7 @@ class Module(MgrModule): self._event.set() return 0, '', 'Running `{0}` in background'.format(self._workload) - @CLICommand('mgr self-test background stop') + @SelftestCLICommand('mgr self-test background stop') def background_stop(self) -> Tuple[int, str, str]: ''' Stop background workload if any is running @@ -106,21 +109,21 @@ class Module(MgrModule): else: return 0, '', 'No background workload was running' - @CLICommand('mgr self-test config get') + @SelftestCLICommand('mgr self-test config get') def config_get(self, key: str) -> Tuple[int, str, str]: ''' Peek at a configuration value ''' return 0, str(self.get_module_option(key)), '' - @CLICommand('mgr self-test config get_localized') + @SelftestCLICommand('mgr self-test config get_localized') def config_get_localized(self, key: str) -> Tuple[int, str, str]: ''' Peek at a configuration value (localized variant) ''' return 0, str(self.get_localized_module_option(key)), '' - @CLICommand('mgr self-test remote') + @SelftestCLICommand('mgr self-test remote') def test_remote(self) -> Tuple[int, str, str]: ''' Test inter-module calls @@ -128,7 +131,7 @@ class Module(MgrModule): self._test_remote_calls() return 0, '', 'Successfully called' - @CLICommand('mgr self-test module') + @SelftestCLICommand('mgr self-test module') def module(self, module: str) -> Tuple[int, str, str]: ''' Run another module's self_test() method @@ -140,7 +143,7 @@ class Module(MgrModule): else: return 0, str(r), "Self-test OK" - @CLICommand('mgr self-test cluster-log') + @SelftestCLICommand('mgr self-test cluster-log') def do_cluster_log(self, channel: str, priority: str, @@ -159,7 +162,7 @@ class Module(MgrModule): message) return 0, '', 'Successfully called' - @CLICommand('mgr self-test health set') + @SelftestCLICommand('mgr self-test health set') def health_set(self, checks: str) -> Tuple[int, str, str]: ''' Set a health check from a JSON-formatted description. @@ -183,7 +186,7 @@ class Module(MgrModule): self.set_health_checks(self._health) return 0, "", "" - @CLICommand('mgr self-test health clear') + @SelftestCLICommand('mgr self-test health clear') def health_clear(self, checks: Optional[List[str]] = None) -> Tuple[int, str, str]: ''' Clear health checks by name. If no names provided, clear all. @@ -198,7 +201,7 @@ class Module(MgrModule): self.set_health_checks(self._health) return 0, "", "" - @CLICommand('mgr self-test insights_set_now_offset') + @SelftestCLICommand('mgr self-test insights_set_now_offset') def insights_set_now_offset(self, hours: int) -> Tuple[int, str, str]: ''' Set the now time for the insights module. @@ -468,7 +471,7 @@ class Module(MgrModule): self._event.clear() self.log.info("Ended command_spam workload...") - @CLICommand('mgr self-test eval') + @SelftestCLICommand('mgr self-test eval') def eval(self, s: Optional[str] = None, inbuf: Optional[str] = None) -> HandleCommandResult: diff --git a/src/pybind/mgr/snap_schedule/cli.py b/src/pybind/mgr/snap_schedule/cli.py new file mode 100644 index 00000000000..8545d9406ae --- /dev/null +++ b/src/pybind/mgr/snap_schedule/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +SnapScheduleCLICommand = CLICommandBase.make_registry_subtype("SnapScheduleCLICommand") diff --git a/src/pybind/mgr/snap_schedule/module.py b/src/pybind/mgr/snap_schedule/module.py index 7749af7b79b..b639556db6a 100644 --- a/src/pybind/mgr/snap_schedule/module.py +++ b/src/pybind/mgr/snap_schedule/module.py @@ -12,8 +12,11 @@ from mgr_module import MgrModule, Option, NotifyType from mgr_util import CephfsConnectionException from threading import Event +from .cli import SnapScheduleCLICommand + class Module(MgrModule): + CLICommand = SnapScheduleCLICommand NOTIFY_TYPES = [NotifyType.fs_map] MODULE_OPTIONS = [ @@ -122,7 +125,7 @@ class Module(MgrModule): self._initialized.wait() return -errno.EINVAL, "", "Unknown command" - @CLIReadCommand('fs snap-schedule status') + @SnapScheduleCLICommand.Read('fs snap-schedule status') def snap_schedule_get(self, path: str = '/', fs: Optional[str] = None, @@ -151,7 +154,7 @@ class Module(MgrModule): self.log.info(errstr) return 0, '\n===\n'.join([ret_sched.report() for ret_sched in ret_scheds]), '' - @CLIReadCommand('fs snap-schedule list') + @SnapScheduleCLICommand.Read('fs snap-schedule list') def snap_schedule_list(self, path: str, recursive: bool = False, fs: Optional[str] = None, @@ -188,7 +191,7 @@ class Module(MgrModule): return 0, json.dumps(out), '' return 0, '\n'.join([str(sched) for sched in scheds]), '' - @CLIWriteCommand('fs snap-schedule add') + @SnapScheduleCLICommand.Write('fs snap-schedule add') def snap_schedule_add(self, path: str, snap_schedule: str, @@ -226,7 +229,7 @@ class Module(MgrModule): return -errno.EIO, '', str(e) return 0, suc_msg, '' - @CLIWriteCommand('fs snap-schedule remove') + @SnapScheduleCLICommand.Write('fs snap-schedule remove') def snap_schedule_rm(self, path: str, repeat: Optional[str] = None, @@ -254,7 +257,7 @@ class Module(MgrModule): return -errno.EIO, '', str(e) return 0, 'Schedule removed for path {}'.format(abs_path), '' - @CLIWriteCommand('fs snap-schedule retention add') + @SnapScheduleCLICommand.Write('fs snap-schedule retention add') def snap_schedule_retention_add(self, path: str, retention_spec_or_period: str, @@ -284,7 +287,7 @@ class Module(MgrModule): return -errno.EIO, '', str(e) return 0, 'Retention added to path {}'.format(abs_path), '' - @CLIWriteCommand('fs snap-schedule retention remove') + @SnapScheduleCLICommand.Write('fs snap-schedule retention remove') def snap_schedule_retention_rm(self, path: str, retention_spec_or_period: str, @@ -314,7 +317,7 @@ class Module(MgrModule): return -errno.EIO, '', str(e) return 0, 'Retention removed from path {}'.format(abs_path), '' - @CLIWriteCommand('fs snap-schedule activate') + @SnapScheduleCLICommand.Write('fs snap-schedule activate') def snap_schedule_activate(self, path: str, repeat: Optional[str] = None, @@ -342,7 +345,7 @@ class Module(MgrModule): return -errno.EIO, '', str(e) return 0, 'Schedule activated for path {}'.format(abs_path), '' - @CLIWriteCommand('fs snap-schedule deactivate') + @SnapScheduleCLICommand.Write('fs snap-schedule deactivate') def snap_schedule_deactivate(self, path: str, repeat: Optional[str] = None, diff --git a/src/pybind/mgr/stats/cli.py b/src/pybind/mgr/stats/cli.py new file mode 100644 index 00000000000..de7296945af --- /dev/null +++ b/src/pybind/mgr/stats/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +StatsCLICommand = CLICommandBase.make_registry_subtype("StatsCLICommand") diff --git a/src/pybind/mgr/stats/module.py b/src/pybind/mgr/stats/module.py index fcc1bce97b5..e3380ee414e 100644 --- a/src/pybind/mgr/stats/module.py +++ b/src/pybind/mgr/stats/module.py @@ -5,11 +5,14 @@ performance stats for ceph filesystem (for now...) import json from typing import List, Dict +from .cli import StatsCLICommand + from mgr_module import MgrModule, Option, NotifyType from .fs.perf_stats import FSPerfStats class Module(MgrModule): + CLICommand = StatsCLICommand COMMANDS = [ { "cmd": "fs perf stats " diff --git a/src/pybind/mgr/status/cli.py b/src/pybind/mgr/status/cli.py new file mode 100644 index 00000000000..bfd26bcfcd8 --- /dev/null +++ b/src/pybind/mgr/status/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +StatusCLICommand = CLICommandBase.make_registry_subtype("StatusCLICommand") diff --git a/src/pybind/mgr/status/module.py b/src/pybind/mgr/status/module.py index 02cc5db4b0c..7e7264a762f 100644 --- a/src/pybind/mgr/status/module.py +++ b/src/pybind/mgr/status/module.py @@ -11,10 +11,13 @@ import fnmatch import mgr_util import json +from .cli import StatusCLICommand + from mgr_module import MgrModule, HandleCommandResult class Module(MgrModule): + CLICommand = StatusCLICommand def get_unlabeled_counter_latest(self, daemon_type: str, daemon_name: str, stat: str) -> int: data = self.get_unlabeled_counter(daemon_type, daemon_name, stat)[stat] if data: @@ -29,7 +32,7 @@ class Module(MgrModule): else: return 0 - @CLIReadCommand("fs status") + @StatusCLICommand.Read("fs status") def handle_fs_status(self, fs: Optional[str] = None, format: str = 'plain') -> Tuple[int, str, str]: @@ -278,7 +281,7 @@ class Module(MgrModule): else: return HandleCommandResult(stdout=output) - @CLIReadCommand("osd status") + @StatusCLICommand.Read("osd status") def handle_osd_status(self, bucket: Optional[str] = None, format: str = 'plain') -> Tuple[int, str, str]: """ Show the status of OSDs within a bucket, or all diff --git a/src/pybind/mgr/telegraf/cli.py b/src/pybind/mgr/telegraf/cli.py new file mode 100644 index 00000000000..303230fb545 --- /dev/null +++ b/src/pybind/mgr/telegraf/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +TelegrafCLICommand = CLICommandBase.make_registry_subtype("TelegrafCLICommand") diff --git a/src/pybind/mgr/telegraf/module.py b/src/pybind/mgr/telegraf/module.py index 6467f8f8118..f62c6ba7bbb 100644 --- a/src/pybind/mgr/telegraf/module.py +++ b/src/pybind/mgr/telegraf/module.py @@ -5,6 +5,8 @@ import socket import time from threading import Event +from .cli import TelegrafCLICommand + from telegraf.basesocket import BaseSocket from telegraf.protocol import Line from mgr_module import MgrModule, Option, OptionValue, PG_STATES @@ -14,6 +16,7 @@ from urllib.parse import urlparse class Module(MgrModule): + CLICommand = TelegrafCLICommand MODULE_OPTIONS = [ Option(name='address', default='unixgram:///tmp/telegraf.sock'), @@ -234,14 +237,14 @@ class Module(MgrModule): self.run = False self.event.set() - @CLIReadCommand('telegraf config-show') + @TelegrafCLICommand.Read('telegraf config-show') def config_show(self) -> Tuple[int, str, str]: """ Show current configuration """ return 0, json.dumps(self.config), '' - @CLICommand('telegraf config-set') + @TelegrafCLICommand('telegraf config-set') def config_set(self, key: str, value: str) -> Tuple[int, str, str]: """ Set a configuration value @@ -253,7 +256,7 @@ class Module(MgrModule): self.set_module_option(key, value) return 0, 'Configuration option {0} updated'.format(key), '' - @CLICommand('telegraf send') + @TelegrafCLICommand('telegraf send') def send(self) -> Tuple[int, str, str]: """ Force sending data to Telegraf diff --git a/src/pybind/mgr/telemetry/cli.py b/src/pybind/mgr/telemetry/cli.py new file mode 100644 index 00000000000..aa483bfa81a --- /dev/null +++ b/src/pybind/mgr/telemetry/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +TelemetryCLICommand = CLICommandBase.make_registry_subtype("TelemetryCLICommand") diff --git a/src/pybind/mgr/telemetry/module.py b/src/pybind/mgr/telemetry/module.py index 148681ca022..1c604ad3ab5 100644 --- a/src/pybind/mgr/telemetry/module.py +++ b/src/pybind/mgr/telemetry/module.py @@ -20,6 +20,8 @@ from threading import Event, Lock from collections import defaultdict from typing import cast, Any, DefaultDict, Dict, List, Optional, Tuple, TypeVar, TYPE_CHECKING, Union +from .cli import TelemetryCLICommand + from mgr_module import MgrModule, Option, OptionValue, ServiceInfoT @@ -185,6 +187,7 @@ ROOK_KEYS_BY_COLLECTION : List[Tuple[str, Collection]] = [ ] class Module(MgrModule): + CLICommand = TelemetryCLICommand metadata_keys = [ "arch", "ceph_version", @@ -1667,7 +1670,7 @@ class Module(MgrModule): return 0, msg, '' - @CLIReadCommand('telemetry status') + @TelemetryCLICommand.Read('telemetry status') def status(self) -> Tuple[int, str, str]: ''' Show current configuration @@ -1679,7 +1682,7 @@ class Module(MgrModule): if self.last_upload else self.last_upload) return 0, json.dumps(r, indent=4, sort_keys=True), '' - @CLIReadCommand('telemetry diff') + @TelemetryCLICommand.Read('telemetry diff') def diff(self) -> Tuple[int, str, str]: ''' Show the diff between opted-in collection and available collection @@ -1699,7 +1702,7 @@ class Module(MgrModule): return 0, r, '' - @CLICommand('telemetry on') + @TelemetryCLICommand('telemetry on') def on(self, license: Optional[str] = None) -> Tuple[int, str, str]: ''' Enable telemetry reports from this cluster @@ -1734,7 +1737,7 @@ To enable, add '--license {LICENSE}' to the 'ceph telemetry on' command.''' return 0, msg, '' - @CLICommand('telemetry off') + @TelemetryCLICommand('telemetry off') def off(self) -> Tuple[int, str, str]: ''' Disable telemetry reports from this cluster @@ -1761,35 +1764,35 @@ To enable, add '--license {LICENSE}' to the 'ceph telemetry on' command.''' msg = 'Telemetry is now disabled.' return 0, msg, '' - @CLIReadCommand('telemetry enable channel all') + @TelemetryCLICommand.Read('telemetry enable channel all') def enable_channel_all(self, channels: List[str] = ALL_CHANNELS) -> Tuple[int, str, str]: ''' Enable all channels ''' return self.toggle_channel('enable', channels) - @CLIReadCommand('telemetry enable channel') + @TelemetryCLICommand.Read('telemetry enable channel') def enable_channel(self, channels: Optional[List[str]] = None) -> Tuple[int, str, str]: ''' Enable a list of channels ''' return self.toggle_channel('enable', channels) - @CLIReadCommand('telemetry disable channel all') + @TelemetryCLICommand.Read('telemetry disable channel all') def disable_channel_all(self, channels: List[str] = ALL_CHANNELS) -> Tuple[int, str, str]: ''' Disable all channels ''' return self.toggle_channel('disable', channels) - @CLIReadCommand('telemetry disable channel') + @TelemetryCLICommand.Read('telemetry disable channel') def disable_channel(self, channels: Optional[List[str]] = None) -> Tuple[int, str, str]: ''' Disable a list of channels ''' return self.toggle_channel('disable', channels) - @CLIReadCommand('telemetry channel ls') + @TelemetryCLICommand.Read('telemetry channel ls') def channel_ls(self) -> Tuple[int, str, str]: ''' List all channels @@ -1822,7 +1825,7 @@ To enable, add '--license {LICENSE}' to the 'ceph telemetry on' command.''' return 0, table.get_string(sortby="NAME"), '' - @CLIReadCommand('telemetry collection ls') + @TelemetryCLICommand.Read('telemetry collection ls') def collection_ls(self) -> Tuple[int, str, str]: ''' List all collections @@ -1879,7 +1882,7 @@ To enable, add '--license {LICENSE}' to the 'ceph telemetry on' command.''' return 0, f'{msg}{table.get_string(sortby="NAME")}', '' - @CLICommand('telemetry send') + @TelemetryCLICommand('telemetry send') def do_send(self, endpoint: Optional[List[EndPoint]] = None, license: Optional[str] = None) -> Tuple[int, str, str]: @@ -1896,7 +1899,7 @@ Please consider enabling the telemetry module with 'ceph telemetry on'.''' self.last_report = self.compile_report() return self.send(self.last_report, endpoint) - @CLIReadCommand('telemetry show') + @TelemetryCLICommand.Read('telemetry show') def show(self, channels: Optional[List[str]] = None) -> Tuple[int, str, str]: ''' Show a sample report of opted-in collections (except for 'device') @@ -1916,7 +1919,7 @@ Please consider enabling the telemetry module with 'ceph telemetry on'.''' return 0, report, '' - @CLIReadCommand('telemetry preview') + @TelemetryCLICommand.Read('telemetry preview') def preview(self, channels: Optional[List[str]] = None) -> Tuple[int, str, str]: ''' Preview a sample report of the most recent collections available (except for 'device') @@ -1953,7 +1956,7 @@ Please consider enabling the telemetry module with 'ceph telemetry on'.''' return 0, report, '' - @CLIReadCommand('telemetry show-device') + @TelemetryCLICommand.Read('telemetry show-device') def show_device(self) -> Tuple[int, str, str]: ''' Show a sample device report @@ -1972,7 +1975,7 @@ Please consider enabling the telemetry module with 'ceph telemetry on'.''' return 0, json.dumps(self.get_report_locked('device'), indent=4, sort_keys=True), '' - @CLIReadCommand('telemetry preview-device') + @TelemetryCLICommand.Read('telemetry preview-device') def preview_device(self) -> Tuple[int, str, str]: ''' Preview a sample device report of the most recent device collection @@ -2002,7 +2005,7 @@ Please consider enabling the telemetry module with 'ceph telemetry on'.''' report = json.dumps(report, indent=4, sort_keys=True) return 0, report, '' - @CLIReadCommand('telemetry show-all') + @TelemetryCLICommand.Read('telemetry show-all') def show_all(self) -> Tuple[int, str, str]: ''' Show a sample report of all enabled channels (including 'device' channel) @@ -2023,7 +2026,7 @@ Please consider enabling the telemetry module with 'ceph telemetry on'.''' self.format_perf_histogram(report) return 0, json.dumps(report, indent=4, sort_keys=True), '' - @CLIReadCommand('telemetry preview-all') + @TelemetryCLICommand.Read('telemetry preview-all') def preview_all(self) -> Tuple[int, str, str]: ''' Preview a sample report of the most recent collections available of all channels (including 'device') diff --git a/src/pybind/mgr/test_orchestrator/cli.py b/src/pybind/mgr/test_orchestrator/cli.py new file mode 100644 index 00000000000..f8c327e922e --- /dev/null +++ b/src/pybind/mgr/test_orchestrator/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +TestOrchestratorCLICommand = CLICommandBase.make_registry_subtype("TestOrchestratorCLICommand") diff --git a/src/pybind/mgr/test_orchestrator/module.py b/src/pybind/mgr/test_orchestrator/module.py index 701a6b9f90c..74d23f0aedc 100644 --- a/src/pybind/mgr/test_orchestrator/module.py +++ b/src/pybind/mgr/test_orchestrator/module.py @@ -7,6 +7,8 @@ import functools import itertools from subprocess import check_output, CalledProcessError +from .cli import TestOrchestratorCLICommand + from ceph.deployment.service_spec import ServiceSpec, NFSServiceSpec, IscsiServiceSpec try: @@ -24,6 +26,7 @@ from orchestrator import handle_orch_error, raise_if_exception class TestOrchestrator(MgrModule, orchestrator.Orchestrator): + CLICommand = TestOrchestratorCLICommand """ This is an orchestrator implementation used for internal testing. It's meant for development environments and integration testing. @@ -33,7 +36,7 @@ class TestOrchestrator(MgrModule, orchestrator.Orchestrator): The implementation is similar to the Rook orchestrator, but simpler. """ - @CLICommand('test_orchestrator load_data', perm='w') + @TestOrchestratorCLICommand('test_orchestrator load_data', perm='w') def _load_data(self, inbuf): """ load dummy data into test orchestrator diff --git a/src/pybind/mgr/volumes/cli.py b/src/pybind/mgr/volumes/cli.py new file mode 100644 index 00000000000..849db305d18 --- /dev/null +++ b/src/pybind/mgr/volumes/cli.py @@ -0,0 +1,3 @@ +from mgr_module import CLICommandBase + +VolumesCLICommand = CLICommandBase.make_registry_subtype("VolumesCLICommand") diff --git a/src/pybind/mgr/volumes/module.py b/src/pybind/mgr/volumes/module.py index 03d582b4440..3b53aad9e34 100644 --- a/src/pybind/mgr/volumes/module.py +++ b/src/pybind/mgr/volumes/module.py @@ -3,6 +3,8 @@ import logging import traceback import threading +from .cli import VolumesCLICommand + from mgr_module import MgrModule, Option import orchestrator @@ -40,6 +42,7 @@ def mgr_cmd_wrap(f): class Module(orchestrator.OrchestratorClientMixin, MgrModule): + CLICommand = VolumesCLICommand COMMANDS = [ { 'cmd': 'fs volume ls', -- 2.47.3