]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
pybind/mgr/orchestrator: use enum parameter when appropriate
authorKefu Chai <kchai@redhat.com>
Sun, 27 Dec 2020 14:45:24 +0000 (22:45 +0800)
committerKefu Chai <kchai@redhat.com>
Fri, 15 Jan 2021 09:25:06 +0000 (17:25 +0800)
and move desc as docstring.

Signed-off-by: Kefu Chai <kchai@redhat.com>
src/pybind/mgr/orchestrator/module.py
src/pybind/mgr/orchestrator/tests/test_orchestrator.py

index c2af2f3a2eaa107df16da9ed6041711bc77d7f8a..9f97fe9499e16a2d66b146e02fa11884b4d95c1b 100644 (file)
@@ -1,3 +1,4 @@
+import enum
 import errno
 import json
 from typing import List, Set, Optional, Iterator, cast, Dict, Any, Union
@@ -29,7 +30,45 @@ def nice_delta(now, t, suffix=''):
         return '-'
 
 
-def to_format(what, format: str, many: bool, cls):
+class Format(enum.Enum):
+    plain = 'plain'
+    json = 'json'
+    json_pretty = 'json-pretty'
+    yaml = 'yaml'
+
+
+class ServiceType(enum.Enum):
+    mon = 'mon'
+    mgr = 'mgr'
+    rbd_mirror = 'rbd-mirror'
+    crash = 'crash'
+    alertmanager = 'alertmanager'
+    grafana = 'grafana'
+    node_exporter = 'node-exporter'
+    prometheus = 'prometheus'
+    mds = 'mds'
+    rgw = 'rgw'
+    nfs = 'nfs'
+    iscsi = 'iscsi'
+    cephadm_exporter = 'cephadm-exporter'
+
+
+class ServiceAction(enum.Enum):
+    start = 'start'
+    stop = 'stop'
+    restart = 'restart'
+    redeploy = 'redeploy'
+    reconfig = 'reconfig'
+
+
+class DaemonAction(enum.Enum):
+    start = 'start'
+    stop = 'stop'
+    restart = 'restart'
+    reconfig = 'reconfig'
+
+
+def to_format(what, format: Format, many: bool, cls):
     def to_json_1(obj):
         if hasattr(obj, 'to_json'):
             return obj.to_json()
@@ -40,11 +79,11 @@ def to_format(what, format: str, many: bool, cls):
 
     to_json = to_json_n if many else to_json_1
 
-    if format == 'json':
+    if format == Format.json:
         return json.dumps(to_json(what), sort_keys=True)
-    elif format == 'json-pretty':
+    elif format == Format.json_pretty:
         return json.dumps(to_json(what), indent=2, sort_keys=True)
-    elif format == 'yaml':
+    elif format == Format.yaml:
         # fun with subinterpreters again. pyyaml depends on object identity.
         # as what originates from a different subinterpreter we have to copy things here.
         if cls:
@@ -203,10 +242,9 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
         locs = [d['location'] for d in self.get('devices')['devices'] if d['devid'] == dev_id]
         return [DeviceLightLoc(**l) for l in sum(locs, [])]
 
-    @_cli_read_command(
-        prefix='device ls-lights',
-        desc='List currently active device indicator lights')
+    @_cli_read_command(prefix='device ls-lights')
     def _device_ls(self):
+        """List currently active device indicator lights"""
         return HandleCommandResult(
             stdout=json.dumps({
                 'ident': list(self.ident),
@@ -257,69 +295,64 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
                 self._refresh_health()
             raise
 
-    @_cli_write_command(
-        prefix='device light',
-        cmd_args='name=enable,type=CephChoices,strings=on|off '
-                 'name=devid,type=CephString '
-                 'name=light_type,type=CephChoices,strings=ident|fault,req=false '
-                 'name=force,type=CephBool,req=false',
-        desc='Enable or disable the device light. Default type is `ident`\n'
-             'Usage: device light (on|off) <devid> [ident|fault] [--force]')
-    def _device_light(self, enable, devid, light_type=None, force=False):
-        # type: (str, str, Optional[str], bool) -> HandleCommandResult
-        light_type = light_type or 'ident'
-        on = enable == 'on'
-        if on:
-            return self.light_on(light_type, devid)
+    class DeviceLightEnable(enum.Enum):
+        on = 'on'
+        off = 'off'
+
+    class DeviceLightType(enum.Enum):
+        ident = 'ident'
+        fault = 'fault'
+
+    @_cli_write_command(prefix='device light')
+    def _device_light(self,
+                      enable: DeviceLightEnable,
+                      devid: str,
+                      light_type: DeviceLightType = DeviceLightType.ident,
+                      force: bool = False) -> HandleCommandResult:
+        """
+        Enable or disable the device light. Default type is `ident`
+        'Usage: device light (on|off) <devid> [ident|fault] [--force]'
+        """""
+        if enable == self.DeviceLightEnable.on:
+            return self.light_on(light_type.value, devid)
         else:
-            return self.light_off(light_type, devid, force)
+            return self.light_off(light_type.value, devid, force)
 
     def _select_orchestrator(self):
         return self.get_module_option("orchestrator")
 
-    @_cli_write_command(
-        'orch host add',
-        'name=hostname,type=CephString,req=true '
-        'name=addr,type=CephString,req=false '
-        'name=labels,type=CephString,n=N,req=false',
-        'Add a host')
+    @_cli_write_command('orch host add')
     def _add_host(self, hostname: str, addr: Optional[str] = None, labels: Optional[List[str]] = None):
+        """Add a host"""
         s = HostSpec(hostname=hostname, addr=addr, labels=labels)
         completion = self.add_host(s)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch host rm',
-        "name=hostname,type=CephString,req=true",
-        'Remove a host')
-    def _remove_host(self, hostname):
+    @_cli_write_command('orch host rm')
+    def _remove_host(self, hostname: str):
+        """Remove a host"""
         completion = self.remove_host(hostname)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch host set-addr',
-        'name=hostname,type=CephString '
-        'name=addr,type=CephString',
-        'Update a host address')
-    def _update_set_addr(self, hostname, addr):
+    @_cli_write_command('orch host set-addr')
+    def _update_set_addr(self, hostname: str, addr: str):
+        """Update a host address"""
         completion = self.update_host_addr(hostname, addr)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_read_command(
-        'orch host ls',
-        'name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false',
-        'List hosts')
-    def _get_hosts(self, format='plain'):
+    @_cli_read_command('orch host ls')
+    def _get_hosts(self, format: Format = Format.plain):
+        """List hosts"""
         completion = self.get_hosts()
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
-        if format != 'plain':
+        if format != Format.plain:
             output = to_format(completion.result, format, many=True, cls=HostSpec)
         else:
             table = PrettyTable(
@@ -334,43 +367,36 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
             output = table.get_string()
         return HandleCommandResult(stdout=output)
 
-    @_cli_write_command(
-        'orch host label add',
-        'name=hostname,type=CephString '
-        'name=label,type=CephString',
-        'Add a host label')
-    def _host_label_add(self, hostname, label):
+    @_cli_write_command('orch host label add')
+    def _host_label_add(self, hostname: str, label: str):
+        """Add a host label"""
         completion = self.add_host_label(hostname, label)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch host label rm',
-        'name=hostname,type=CephString '
-        'name=label,type=CephString',
-        'Remove a host label')
-    def _host_label_rm(self, hostname, label):
+    @_cli_write_command('orch host label rm')
+    def _host_label_rm(self, hostname: str, label: str):
+        """Remove a host label"""
         completion = self.remove_host_label(hostname, label)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch host ok-to-stop',
-        'name=hostname,type=CephString',
-        desc='Check if the specified host can be safely stopped without reducing availability')
+    @_cli_write_command('orch host ok-to-stop')
     def _host_ok_to_stop(self, hostname: str):
+        """Check if the specified host can be safely stopped without reducing availability"""""
         completion = self.host_ok_to_stop(hostname)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
     @_cli_write_command(
-        'orch host maintenance enter',
-        'name=hostname,type=CephString',
-        desc='Prepare a host for maintenance by shutting down and disabling all Ceph daemons (cephadm only)')
+        'orch host maintenance enter')
     def _host_maintenance_enter(self, hostname: str):
+        """
+        Prepare a host for maintenance by shutting down and disabling all Ceph daemons (cephadm only)
+        """
         completion = self.enter_host_maintenance(hostname)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
@@ -378,32 +404,31 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
         return HandleCommandResult(stdout=completion.result_str())
 
     @_cli_write_command(
-        'orch host maintenance exit',
-        'name=hostname,type=CephString',
-        desc='Return a host from maintenance, restarting all Ceph daemons (cephadm only)')
+        'orch host maintenance exit')
     def _host_maintenance_exit(self, hostname: str):
+        """
+        Return a host from maintenance, restarting all Ceph daemons (cephadm only)
+        """
         completion = self.exit_host_maintenance(hostname)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
 
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_read_command(
-        'orch device ls',
-        "name=hostname,type=CephString,n=N,req=false "
-        "name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false "
-        "name=refresh,type=CephBool,req=false "
-        "name=wide,type=CephBool,req=false",
-        'List devices on a host')
-    def _list_devices(self, hostname=None, format='plain', refresh=False, wide=False):
-        # type: (Optional[List[str]], str, bool, bool) -> HandleCommandResult
+    @_cli_read_command('orch device ls')
+    def _list_devices(self,
+                      hostname: Optional[List[str]] = None,
+                      format: Format = Format.plain,
+                      refresh: bool = False,
+                      wide: bool = False) -> HandleCommandResult:
         """
-        Provide information about storage devices present in cluster hosts
-
-        Note: this does not have to be completely synchronous. Slightly out of
-        date hardware inventory is fine as long as hardware ultimately appears
-        in the output of this command.
+        List devices on a host
         """
+        # Provide information about storage devices present in cluster hosts
+        #
+        # Note: this does not have to be completely synchronous. Slightly out of
+        # date hardware inventory is fine as long as hardware ultimately appears
+        # in the output of this command.
         nf = InventoryFilter(hosts=hostname) if hostname else None
 
         completion = self.get_inventory(host_filter=nf, refresh=refresh)
@@ -411,8 +436,11 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
 
-        if format != 'plain':
-            return HandleCommandResult(stdout=to_format(completion.result, format, many=True, cls=InventoryHost))
+        if format != Format.plain:
+            return HandleCommandResult(stdout=to_format(completion.result,
+                                                        format,
+                                                        many=True,
+                                                        cls=InventoryHost))
         else:
             display_map = {
                 "Unsupported": "N/A",
@@ -489,13 +517,11 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
             out.append(table.get_string())
             return HandleCommandResult(stdout='\n'.join(out))
 
-    @_cli_write_command(
-        'orch device zap',
-        'name=hostname,type=CephString '
-        'name=path,type=CephString '
-        'name=force,type=CephBool,req=false',
-        'Zap (erase!) a device so it can be re-used')
-    def _zap_device(self, hostname, path, force=False):
+    @_cli_write_command('orch device zap')
+    def _zap_device(self, hostname: str, path: str, force: bool = False):
+        """
+        Zap (erase!) a device so it can be re-used
+        """
         if not force:
             raise OrchestratorError('must pass --force to PERMANENTLY ERASE DEVICE DATA')
         completion = self.zap_device(hostname, path)
@@ -503,18 +529,19 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_read_command(
-        'orch ls',
-        "name=service_type,type=CephString,req=false "
-        "name=service_name,type=CephString,req=false "
-        "name=export,type=CephBool,req=false "
-        "name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false "
-        "name=refresh,type=CephBool,req=false",
-        'List services known to orchestrator')
-    def _list_services(self, host=None, service_type=None, service_name=None, export=False, format='plain', refresh=False):
-
-        if export and format == 'plain':
-            format = 'yaml'
+    @_cli_read_command('orch ls')
+    def _list_services(self,
+                       host: Optional[str] = None,
+                       service_type: Optional[str] = None,
+                       service_name: Optional[str] = None,
+                       export: bool = False,
+                       format: Format = Format.plain,
+                       refresh: bool = False):
+        """
+        List services known to orchestrator
+        """
+        if export and format == Format.plain:
+            format = Format.yaml
 
         completion = self.describe_service(service_type,
                                            service_name,
@@ -531,7 +558,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
 
         if len(services) == 0:
             return HandleCommandResult(stdout="No services reported")
-        elif format != 'plain':
+        elif format != Format.plain:
             if export:
                 data = [s.spec for s in services]
                 return HandleCommandResult(stdout=to_format(data, format, many=True, cls=ServiceSpec))
@@ -573,16 +600,17 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
 
             return HandleCommandResult(stdout=table.get_string())
 
-    @_cli_read_command(
-        'orch ps',
-        "name=hostname,type=CephString,req=false "
-        "name=service_name,type=CephString,req=false "
-        "name=daemon_type,type=CephString,req=false "
-        "name=daemon_id,type=CephString,req=false "
-        "name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false "
-        "name=refresh,type=CephBool,req=false",
-        'List daemons known to orchestrator')
-    def _list_daemons(self, hostname=None, service_name=None, daemon_type=None, daemon_id=None, format='plain', refresh=False):
+    @_cli_read_command('orch ps')
+    def _list_daemons(self,
+                      hostname: Optional[str] = None,
+                      service_name: Optional[str] = None,
+                      daemon_type: Optional[str] = None,
+                      daemon_id: Optional[str] = None,
+                      format: Format = Format.plain,
+                      refresh: bool = False):
+        """
+        List daemons known to orchestrator
+        """
         completion = self.list_daemons(service_name,
                                        daemon_type,
                                        daemon_id=daemon_id,
@@ -597,7 +625,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
         # Sort the list for display
         daemons.sort(key=lambda s: (ukn(s.daemon_type), ukn(s.hostname), ukn(s.daemon_id)))
 
-        if format != 'plain':
+        if format != Format.plain:
             return HandleCommandResult(stdout=to_format(daemons, format, many=True, cls=DaemonDescription))
         else:
             if len(daemons) == 0:
@@ -649,20 +677,17 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
 
             return HandleCommandResult(stdout=table.get_string())
 
-    @_cli_write_command(
-        'orch apply osd',
-        'name=all_available_devices,type=CephBool,req=false '
-        'name=dry_run,type=CephBool,req=false '
-        'name=unmanaged,type=CephBool,req=false '
-        "name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false",
-        'Create OSD daemon(s) using a drive group spec')
+    @_cli_write_command('orch apply osd')
     def _apply_osd(self,
                    all_available_devices: bool = False,
-                   format: str = 'plain',
-                   unmanaged=None,
-                   dry_run=None,
+                   format: Format = Format.plain,
+                   unmanaged: Optional[bool] = None,
+                   dry_run: bool = False,
                    inbuf: Optional[str] = None) -> HandleCommandResult:
-        """Apply DriveGroupSpecs to create OSDs"""
+        """
+        Create OSD daemon(s) using a drive group spec
+        """
+        # Apply DriveGroupSpecs to create OSDs
         usage = """
 usage:
   ceph orch apply osd -i <json_file/yaml_file> [--dry-run]
@@ -737,7 +762,7 @@ Examples:
                 self._orchestrator_wait([completion])
                 raise_if_exception(completion)
                 data = completion.result
-                if format == 'plain':
+                if format == Format.plain:
                     out = generate_preview_tables(data, True)
                 else:
                     out = to_format(data, format, many=True, cls=None)
@@ -764,7 +789,7 @@ Examples:
                 completion = self.plan(dg_specs)
                 self._orchestrator_wait([completion])
                 data = completion.result
-                if format == 'plain':
+                if format == Format.plain:
                     out = generate_preview_tables(data, True)
                 else:
                     out = to_format(data, format, many=True, cls=None)
@@ -772,13 +797,10 @@ Examples:
 
         return HandleCommandResult(-errno.EINVAL, stderr=usage)
 
-    @_cli_write_command(
-        'orch daemon add osd',
-        "name=svc_arg,type=CephString,req=false",
-        'Create an OSD service. Either --svc_arg=host:drives')
-    def _daemon_add_osd(self, svc_arg=None):
-        # type: (Optional[str]) -> HandleCommandResult
-        """Create one or more OSDs"""
+    @_cli_write_command('orch daemon add osd')
+    def _daemon_add_osd(self, svc_arg: Optional[str] = None) -> HandleCommandResult:
+        """Create an OSD service. Either --svc_arg=host:drives"""
+        # Create one or more OSDs"""
 
         usage = """
 Usage:
@@ -801,36 +823,28 @@ Usage:
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch osd rm',
-        "name=svc_id,type=CephString,n=N "
-        "name=replace,type=CephBool,req=false "
-        "name=force,type=CephBool,req=false",
-        'Remove OSD services')
+    @_cli_write_command('orch osd rm')
     def _osd_rm_start(self,
                       svc_id: List[str],
                       replace: bool = False,
                       force: bool = False) -> HandleCommandResult:
+        """Remove OSD services"""
         completion = self.remove_osds(svc_id, replace=replace, force=force)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch osd rm stop',
-        "name=svc_id,type=CephString,n=N",
-        'Remove OSD services')
+    @_cli_write_command('orch osd rm stop')
     def _osd_rm_stop(self, svc_id: List[str]) -> HandleCommandResult:
+        """Remove OSD services"""
         completion = self.stop_remove_osds(svc_id)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch osd rm status',
-        "name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false",
-        desc='status of OSD removal operation')
-    def _osd_rm_status(self, format='plain') -> HandleCommandResult:
+    @_cli_write_command('orch osd rm status')
+    def _osd_rm_status(self, format: Format = Format.plain) -> HandleCommandResult:
+        """status of OSD removal operation"""
         completion = self.remove_osds_status()
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
@@ -839,7 +853,7 @@ Usage:
         if not report:
             return HandleCommandResult(stdout="No OSD remove/replace operations reported")
 
-        if format != 'plain':
+        if format != Format.plain:
             out = to_format(report, format, many=True, cls=None)
         else:
             table = PrettyTable(
@@ -855,15 +869,12 @@ Usage:
 
         return HandleCommandResult(stdout=out)
 
-    @_cli_write_command(
-        'orch daemon add',
-        'name=daemon_type,type=CephChoices,strings=mon|mgr|rbd-mirror|crash|alertmanager|grafana|node-exporter|prometheus|cephadm-exporter,req=false '
-        'name=placement,type=CephString,req=false',
-        'Add daemon(s)')
+    @_cli_write_command('orch daemon add')
     def _daemon_add_misc(self,
-                         daemon_type: Optional[str] = None,
+                         daemon_type: Optional[ServiceType] = None,
                          placement: Optional[str] = None,
                          inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Add daemon(s)"""
         usage = f"""Usage:
     ceph orch daemon add -i <json_file>
     ceph orch daemon add {daemon_type or '<daemon_type>'} <placement>"""
@@ -874,52 +885,48 @@ Usage:
         else:
             spec = PlacementSpec.from_string(placement)
             assert daemon_type
-            spec = ServiceSpec(daemon_type, placement=spec)
-
-        daemon_type = spec.service_type
+            spec = ServiceSpec(daemon_type.value, placement=spec)
 
-        if daemon_type == 'mon':
+        if daemon_type == ServiceType.mon:
             completion = self.add_mon(spec)
-        elif daemon_type == 'mgr':
+        elif daemon_type == ServiceType.mgr:
             completion = self.add_mgr(spec)
-        elif daemon_type == 'rbd-mirror':
+        elif daemon_type == ServiceType.rbd_mirror:
             completion = self.add_rbd_mirror(spec)
-        elif daemon_type == 'crash':
+        elif daemon_type == ServiceType.crash:
             completion = self.add_crash(spec)
-        elif daemon_type == 'alertmanager':
+        elif daemon_type == ServiceType.alertmanager:
             completion = self.add_alertmanager(spec)
-        elif daemon_type == 'grafana':
+        elif daemon_type == ServiceType.grafana:
             completion = self.add_grafana(spec)
-        elif daemon_type == 'node-exporter':
+        elif daemon_type == ServiceType.node_exporter:
             completion = self.add_node_exporter(spec)
-        elif daemon_type == 'prometheus':
+        elif daemon_type == ServiceType.prometheus:
             completion = self.add_prometheus(spec)
-        elif daemon_type == 'mds':
+        elif daemon_type == ServiceType.mds:
             completion = self.add_mds(spec)
-        elif daemon_type == 'rgw':
+        elif daemon_type == ServiceType.rgw:
             completion = self.add_rgw(spec)
-        elif daemon_type == 'nfs':
+        elif daemon_type == ServiceType.nfs:
             completion = self.add_nfs(spec)
-        elif daemon_type == 'iscsi':
+        elif daemon_type == ServiceType.iscsi:
             completion = self.add_iscsi(spec)
-        elif daemon_type == 'cephadm-exporter':
+        elif daemon_type == ServiceType.cephadm_exporter:
             completion = self.add_cephadm_exporter(spec)
         else:
-            raise OrchestratorValidationError(f'unknown daemon type `{daemon_type}`')
+            tp = type(daemon_type)
+            raise OrchestratorValidationError(f'unknown daemon type `{tp}`')
 
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch daemon add mds',
-        'name=fs_name,type=CephString '
-        'name=placement,type=CephString,req=false',
-        'Start MDS daemon(s)')
+    @_cli_write_command('orch daemon add mds')
     def _mds_add(self,
                  fs_name: str,
                  placement: Optional[str] = None,
                  inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Start MDS daemon(s)"""
         if inbuf:
             raise OrchestratorValidationError('unrecognized command -i; -h or --help for usage')
 
@@ -934,15 +941,7 @@ Usage:
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch daemon add rgw',
-        'name=realm_name,type=CephString '
-        'name=zone_name,type=CephString '
-        'name=subcluster,type=CephString,req=false '
-        'name=port,type=CephInt,req=false '
-        'name=ssl,type=CephBool,req=false '
-        'name=placement,type=CephString,req=false',
-        'Start RGW daemon(s)')
+    @_cli_write_command('orch daemon add rgw')
     def _rgw_add(self,
                  realm_name: str,
                  zone_name: str,
@@ -951,6 +950,7 @@ Usage:
                  ssl: bool = False,
                  placement: Optional[str] = None,
                  inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Start RGW daemon(s)"""
         if inbuf:
             raise OrchestratorValidationError('unrecognized command -i; -h or --help for usage')
 
@@ -968,19 +968,14 @@ Usage:
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch daemon add nfs',
-        "name=svc_id,type=CephString "
-        "name=pool,type=CephString "
-        "name=namespace,type=CephString,req=false "
-        'name=placement,type=CephString,req=false',
-        'Start NFS daemon(s)')
+    @_cli_write_command('orch daemon add nfs')
     def _nfs_add(self,
                  svc_id: str,
                  pool: str,
                  namespace: Optional[str] = None,
                  placement: Optional[str] = None,
                  inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Start NFS daemon(s)"""
         if inbuf:
             raise OrchestratorValidationError('unrecognized command -i; -h or --help for usage')
 
@@ -996,14 +991,7 @@ Usage:
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch daemon add iscsi',
-        'name=pool,type=CephString '
-        'name=api_user,type=CephString '
-        'name=api_password,type=CephString '
-        'name=trusted_ip_list,type=CephString,req=false '
-        'name=placement,type=CephString,req=false',
-        'Start iscsi daemon(s)')
+    @_cli_write_command('orch daemon add iscsi')
     def _iscsi_add(self,
                    pool: str,
                    api_user: str,
@@ -1011,6 +999,7 @@ Usage:
                    trusted_ip_list: Optional[str] = None,
                    placement: Optional[str] = None,
                    inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Start iscsi daemon(s)"""
         if inbuf:
             raise OrchestratorValidationError('unrecognized command -i; -h or --help for usage')
 
@@ -1028,36 +1017,27 @@ Usage:
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch',
-        "name=action,type=CephChoices,strings=start|stop|restart|redeploy|reconfig "
-        "name=service_name,type=CephString",
-        'Start, stop, restart, redeploy, or reconfig an entire service (i.e. all daemons)')
-    def _service_action(self, action, service_name):
-        completion = self.service_action(action, service_name)
+    @_cli_write_command('orch')
+    def _service_action(self, action: ServiceAction, service_name: str):
+        """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])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch daemon',
-        "name=action,type=CephChoices,strings=start|stop|restart|reconfig "
-        "name=name,type=CephString",
-        'Start, stop, restart, (redeploy,) or reconfig a specific daemon')
-    def _daemon_action(self, action, name):
+    @_cli_write_command('orch daemon')
+    def _daemon_action(self, action: DaemonAction, name: str):
+        """Start, stop, restart, (redeploy,) or reconfig a specific daemon"""
         if '.' not in name:
             raise OrchestratorError('%s is not a valid daemon name' % name)
-        completion = self.daemon_action(action, name)
+        completion = self.daemon_action(action.value, name)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch daemon redeploy',
-        "name=name,type=CephString "
-        "name=image,type=CephString,req=false",
-        'Redeploy a daemon (with a specifc image)')
+    @_cli_write_command('orch daemon redeploy')
     def _daemon_action_redeploy(self, name: str, image: Optional[str] = None) -> HandleCommandResult:
+        """Redeploy a daemon (with a specifc image)"""
         if '.' not in name:
             raise OrchestratorError('%s is not a valid daemon name' % name)
         completion = self.daemon_action("redeploy", name, image=image)
@@ -1065,12 +1045,11 @@ Usage:
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch daemon rm',
-        "name=names,type=CephString,n=N "
-        'name=force,type=CephBool,req=false',
-        'Remove specific daemon(s)')
-    def _daemon_rm(self, names, force=False):
+    @_cli_write_command('orch daemon rm')
+    def _daemon_rm(self,
+                   names: List[str],
+                   force: Optional[bool] = False):
+        """Remove specific daemon(s)"""
         for name in names:
             if '.' not in name:
                 raise OrchestratorError('%s is not a valid daemon name' % name)
@@ -1083,12 +1062,11 @@ Usage:
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch rm',
-        'name=service_name,type=CephString '
-        'name=force,type=CephBool,req=false',
-        'Remove a service')
-    def _service_rm(self, service_name, force=False):
+    @_cli_write_command('orch rm')
+    def _service_rm(self,
+                    service_name: str,
+                    force: bool = False):
+        """Remove a service"""
         if service_name in ['mon', 'mgr'] and not force:
             raise OrchestratorError('The mon and mgr services cannot be removed')
         completion = self.remove_service(service_name)
@@ -1096,21 +1074,15 @@ Usage:
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch apply',
-        'name=service_type,type=CephChoices,strings=mon|mgr|rbd-mirror|crash|alertmanager|grafana|node-exporter|prometheus|cephadm-exporter,req=false '
-        'name=placement,type=CephString,req=false '
-        'name=dry_run,type=CephBool,req=false '
-        'name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false '
-        'name=unmanaged,type=CephBool,req=false',
-        'Update the size or placement for a service or apply a large yaml spec')
+    @_cli_write_command('orch apply')
     def _apply_misc(self,
-                    service_type: Optional[str] = None,
+                    service_type: Optional[ServiceType] = None,
                     placement: Optional[str] = None,
                     dry_run: bool = False,
-                    format: str = 'plain',
+                    format: Format = Format.plain,
                     unmanaged: bool = False,
                     inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Update the size or placement for a service or apply a large yaml spec"""
         usage = """Usage:
   ceph orch apply -i <yaml spec> [--dry-run]
   ceph orch apply <service_type> [--placement=<placement_string>] [--unmanaged]
@@ -1128,7 +1100,7 @@ Usage:
         else:
             placementspec = PlacementSpec.from_string(placement)
             assert service_type
-            specs = [ServiceSpec(service_type, placement=placementspec,
+            specs = [ServiceSpec(service_type.value, placement=placementspec,
                                  unmanaged=unmanaged, preview_only=dry_run)]
 
         completion = self.apply(specs)
@@ -1140,27 +1112,21 @@ Usage:
             self._orchestrator_wait([completion])
             raise_if_exception(completion)
             data = completion.result
-            if format == 'plain':
+            if format == Format.plain:
                 out = generate_preview_tables(data)
             else:
                 out = to_format(data, format, many=True, cls=None)
         return HandleCommandResult(stdout=out)
 
-    @_cli_write_command(
-        'orch apply mds',
-        'name=fs_name,type=CephString '
-        'name=placement,type=CephString,req=false '
-        'name=dry_run,type=CephBool,req=false '
-        'name=unmanaged,type=CephBool,req=false '
-        'name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false',
-        'Update the number of MDS instances for the given fs_name')
+    @_cli_write_command('orch apply mds')
     def _apply_mds(self,
                    fs_name: str,
                    placement: Optional[str] = None,
                    dry_run: bool = False,
                    unmanaged: bool = False,
-                   format: str = 'plain',
+                   format: Format = Format.plain,
                    inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Update the number of MDS instances for the given fs_name"""
         if inbuf:
             raise OrchestratorValidationError('unrecognized command -i; -h or --help for usage')
 
@@ -1180,24 +1146,13 @@ Usage:
             self._orchestrator_wait([completion_plan])
             raise_if_exception(completion_plan)
             data = completion_plan.result
-            if format == 'plain':
+            if format == Format.plain:
                 out = preview_table_services(data)
             else:
                 out = to_format(data, format, many=True, cls=None)
         return HandleCommandResult(stdout=out)
 
-    @_cli_write_command(
-        'orch apply rgw',
-        'name=realm_name,type=CephString '
-        'name=zone_name,type=CephString '
-        'name=subcluster,type=CephString,req=false '
-        'name=port,type=CephInt,req=false '
-        'name=ssl,type=CephBool,req=false '
-        'name=placement,type=CephString,req=false '
-        'name=dry_run,type=CephBool,req=false '
-        'name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false '
-        'name=unmanaged,type=CephBool,req=false',
-        'Update the number of RGW instances for the given zone')
+    @_cli_write_command('orch apply rgw')
     def _apply_rgw(self,
                    realm_name: str,
                    zone_name: str,
@@ -1206,9 +1161,10 @@ Usage:
                    ssl: bool = False,
                    placement: Optional[str] = None,
                    dry_run: bool = False,
-                   format: str = 'plain',
+                   format: Format = Format.plain,
                    unmanaged: bool = False,
                    inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Update the number of RGW instances for the given zone"""
         if inbuf:
             raise OrchestratorValidationError('unrecognized command -i; -h or --help for usage')
 
@@ -1232,31 +1188,23 @@ Usage:
             self._orchestrator_wait([completion_plan])
             raise_if_exception(completion_plan)
             data = completion_plan.result
-            if format == 'plain':
+            if format == Format.plain:
                 out = preview_table_services(data)
             else:
                 out = to_format(data, format, many=True, cls=None)
         return HandleCommandResult(stdout=out)
 
-    @_cli_write_command(
-        'orch apply nfs',
-        'name=svc_id,type=CephString '
-        'name=pool,type=CephString '
-        'name=namespace,type=CephString,req=false '
-        'name=placement,type=CephString,req=false '
-        'name=dry_run,type=CephBool,req=false '
-        'name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false '
-        'name=unmanaged,type=CephBool,req=false',
-        'Scale an NFS service')
+    @_cli_write_command('orch apply nfs')
     def _apply_nfs(self,
                    svc_id: str,
                    pool: str,
                    namespace: Optional[str] = None,
                    placement: Optional[str] = None,
-                   format: str = 'plain',
+                   format: Format = Format.plain,
                    dry_run: bool = False,
                    unmanaged: bool = False,
                    inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Scale an NFS service"""
         if inbuf:
             raise OrchestratorValidationError('unrecognized command -i; -h or --help for usage')
 
@@ -1278,23 +1226,13 @@ Usage:
             self._orchestrator_wait([completion_plan])
             raise_if_exception(completion_plan)
             data = completion_plan.result
-            if format == 'plain':
+            if format == Format.plain:
                 out = preview_table_services(data)
             else:
                 out = to_format(data, format, many=True, cls=None)
         return HandleCommandResult(stdout=out)
 
-    @_cli_write_command(
-        'orch apply iscsi',
-        'name=pool,type=CephString '
-        'name=api_user,type=CephString '
-        'name=api_password,type=CephString '
-        'name=trusted_ip_list,type=CephString,req=false '
-        'name=placement,type=CephString,req=false '
-        'name=dry_run,type=CephBool,req=false '
-        'name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false '
-        'name=unmanaged,type=CephBool,req=false',
-        'Scale an iSCSI service')
+    @_cli_write_command('orch apply iscsi')
     def _apply_iscsi(self,
                      pool: str,
                      api_user: str,
@@ -1303,8 +1241,9 @@ Usage:
                      placement: Optional[str] = None,
                      unmanaged: bool = False,
                      dry_run: bool = False,
-                     format: str = 'plain',
+                     format: Format = Format.plain,
                      inbuf: Optional[str] = None) -> HandleCommandResult:
+        """Scale an iSCSI service"""
         if inbuf:
             raise OrchestratorValidationError('unrecognized command -i; -h or --help for usage')
 
@@ -1328,25 +1267,23 @@ Usage:
             self._orchestrator_wait([completion_plan])
             raise_if_exception(completion_plan)
             data = completion_plan.result
-            if format == 'plain':
+            if format == Format.plain:
                 out = preview_table_services(data)
             else:
                 out = to_format(data, format, many=True, cls=None)
         return HandleCommandResult(stdout=out)
 
-    @_cli_write_command(
-        'orch set backend',
-        "name=module_name,type=CephString,req=true",
-        'Select orchestrator module backend')
-    def _set_backend(self, module_name):
+    @_cli_write_command('orch set backend')
+    def _set_backend(self, module_name: Optional[str] = None):
         """
-        We implement a setter command instead of just having the user
-        modify the setting directly, so that we can validate they're setting
-        it to a module that really exists and is enabled.
-
-        There isn't a mechanism for ensuring they don't *disable* the module
-        later, but this is better than nothing.
+        Select orchestrator module backend
         """
+        # We implement a setter command instead of just having the user
+        # modify the setting directly, so that we can validate they're setting
+        # it to a module that really exists and is enabled.
+
+        # There isn't a mechanism for ensuring they don't *disable* the module
+        # later, but this is better than nothing.
         mgr_map = self.get("mgr_map")
 
         if module_name is None or module_name == "":
@@ -1383,35 +1320,31 @@ Usage:
 
         return HandleCommandResult(-errno.EINVAL, stderr="Module '{0}' not found".format(module_name))
 
-    @_cli_write_command(
-        'orch pause',
-        desc='Pause orchestrator background work')
+    @_cli_write_command('orch pause')
     def _pause(self):
+        """Pause orchestrator background work"""
         self.pause()
         return HandleCommandResult()
 
-    @_cli_write_command(
-        'orch resume',
-        desc='Resume orchestrator background work (if paused)')
+    @_cli_write_command('orch resume')
     def _resume(self):
+        """Resume orchestrator background work (if paused)"""
         self.resume()
         return HandleCommandResult()
 
-    @_cli_write_command(
-        'orch cancel',
-        desc='cancels ongoing operations')
+    @_cli_write_command('orch cancel')
     def _cancel(self):
         """
+        cancels ongoing operations
+
         ProgressReferences might get stuck. Let's unstuck them.
         """
         self.cancel_completions()
         return HandleCommandResult()
 
-    @_cli_read_command(
-        'orch status',
-        'name=format,type=CephChoices,strings=plain|json|json-pretty|yaml,req=false',
-        desc='Report configured backend and its status')
-    def _status(self, format='plain'):
+    @_cli_read_command('orch status')
+    def _status(self, format: Format = Format.plain):
+        """Report configured backend and its status"""
         o = self._select_orchestrator()
         if o is None:
             raise NoOrchestrator()
@@ -1425,7 +1358,7 @@ Usage:
             if not avail:
                 result['reason'] = why
 
-        if format != 'plain':
+        if format != Format.plain:
             output = to_format(result, format, many=False, cls=None)
         else:
             output = "Backend: {0}".format(result['backend'])
@@ -1473,22 +1406,20 @@ Usage:
                 f"  Maybe you meant `--ceph-version {ver}`?"
             raise OrchestratorValidationError(s)
 
-    @_cli_write_command(
-        'orch upgrade check',
-        'name=image,type=CephString,req=false '
-        'name=ceph_version,type=CephString,req=false',
-        desc='Check service versions vs available and target containers')
-    def _upgrade_check(self, image=None, ceph_version=None):
+    @_cli_write_command('orch upgrade check')
+    def _upgrade_check(self,
+                       image: Optional[str] = None,
+                       ceph_version: Optional[str] = None):
+        """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)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch upgrade status',
-        desc='Check service versions vs available and target containers')
+    @_cli_write_command('orch upgrade status')
     def _upgrade_status(self):
+        """Check service versions vs available and target containers"""
         completion = self.upgrade_status()
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
@@ -1501,40 +1432,36 @@ Usage:
         out = json.dumps(r, indent=4)
         return HandleCommandResult(stdout=out)
 
-    @_cli_write_command(
-        'orch upgrade start',
-        'name=image,type=CephString,req=false '
-        'name=ceph_version,type=CephString,req=false',
-        desc='Initiate upgrade')
-    def _upgrade_start(self, image=None, ceph_version=None):
+    @_cli_write_command('orch upgrade start')
+    def _upgrade_start(self,
+                       image: Optional[str] = None,
+                       ceph_version: Optional[str] = None):
+        """Initiate upgrade"""
         self._upgrade_check_image_name(image, ceph_version)
         completion = self.upgrade_start(image, ceph_version)
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch upgrade pause',
-        desc='Pause an in-progress upgrade')
+    @_cli_write_command('orch upgrade pause')
     def _upgrade_pause(self):
+        """Pause an in-progress upgrade"""
         completion = self.upgrade_pause()
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch upgrade resume',
-        desc='Resume paused upgrade')
+    @_cli_write_command('orch upgrade resume')
     def _upgrade_resume(self):
+        """Resume paused upgrade"""
         completion = self.upgrade_resume()
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
         return HandleCommandResult(stdout=completion.result_str())
 
-    @_cli_write_command(
-        'orch upgrade stop',
-        desc='Stop an in-progress upgrade')
+    @_cli_write_command('orch upgrade stop')
     def _upgrade_stop(self):
+        """Stop an in-progress upgrade"""
         completion = self.upgrade_stop()
         self._orchestrator_wait([completion])
         raise_if_exception(completion)
index c5858aa9531250061ab62303bebb360f20cc777b..0f456d7c711fd284d58b22d4fe40923c2c5e1f85 100644 (file)
@@ -16,7 +16,7 @@ from tests import mock
 from orchestrator import raise_if_exception, Completion, ProgressReference
 from orchestrator import InventoryHost, DaemonDescription, ServiceDescription
 from orchestrator import OrchestratorValidationError
-from orchestrator.module import to_format, OrchestratorCli, preview_table_osd
+from orchestrator.module import to_format, Format, OrchestratorCli, preview_table_osd
 
 
 def _test_resource(data, resource_class, extra=None):
@@ -281,11 +281,11 @@ events:
         data = yaml.safe_load(y)
         object = cls.from_json(data)
 
-        assert to_format(object, 'yaml', False, cls) == y
-        assert to_format([object], 'yaml', True, cls) == y
+        assert to_format(object, Format.yaml, False, cls) == y
+        assert to_format([object], Format.yaml, True, cls) == y
 
-        j = json.loads(to_format(object, 'json', False, cls))
-        assert to_format(cls.from_json(j), 'yaml', False, cls) == y
+        j = json.loads(to_format(object, Format.json, False, cls))
+        assert to_format(cls.from_json(j), Format.yaml, False, cls) == y
 
 
 def test_event_multiline():