From 5f7c6d58cc90ff139c1f8f8b950ef37d8597f92f Mon Sep 17 00:00:00 2001 From: Adam King Date: Mon, 25 Mar 2024 12:13:32 -0400 Subject: [PATCH] mgr/rgw: add support to modify zonegroup parameters This is being done with `radosgw-admin zonegroup set` rather than `radosgw-admin zonegroup modify` as I don't think the hostnames parameter (which is the primary focus for this specific change) can be set using the modify command. The nice bit about that is it should in theory make it easy to extend this to allow setting other parameters to be modified in the zonegroup in the future. Signed-off-by: Adam King --- src/pybind/mgr/rgw/module.py | 18 +++++-- .../ceph/deployment/service_spec.py | 4 +- src/python-common/ceph/rgw/rgwam_core.py | 52 +++++++++++++++++-- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/src/pybind/mgr/rgw/module.py b/src/pybind/mgr/rgw/module.py index f48e2e09fc3..1b589541932 100644 --- a/src/pybind/mgr/rgw/module.py +++ b/src/pybind/mgr/rgw/module.py @@ -28,7 +28,7 @@ if TYPE_CHECKING: from typing_extensions import Protocol class MgrModuleProtocol(Protocol): - def tool_exec(self, args: List[str]) -> Tuple[int, str, str]: + def tool_exec(self, args: List[str], timeout: int = 10, stdin: Optional[bytes] = None) -> Tuple[int, str, str]: ... def apply_rgw(self, spec: RGWSpec) -> OrchResult[str]: @@ -66,9 +66,9 @@ class RGWAMOrchMgr(RGWAMEnvMgr): def __init__(self, mgr: MgrModuleProtocol): self.mgr = mgr - def tool_exec(self, prog: str, args: List[str]) -> Tuple[List[str], int, str, str]: + def tool_exec(self, prog: str, args: List[str], stdin: Optional[bytes] = None) -> Tuple[List[str], int, str, str]: cmd = [prog] + args - rc, stdout, stderr = self.mgr.tool_exec(args=cmd) + rc, stdout, stderr = self.mgr.tool_exec(args=cmd, stdin=stdin) return cmd, rc, stdout, stderr def apply_rgw(self, spec: RGWSpec) -> None: @@ -286,6 +286,18 @@ 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') + 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, + zonegroup_name, + zone_name, + hostnames) + return HandleCommandResult(retval, 'Zonegroup updated successfully', '') + except RGWAMException as e: + 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') @check_orchestrator def _cmd_rgw_zone_create(self, diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py index 767e1e6f468..1917e004802 100644 --- a/src/python-common/ceph/deployment/service_spec.py +++ b/src/python-common/ceph/deployment/service_spec.py @@ -1174,10 +1174,11 @@ class RGWSpec(ServiceSpec): rgw_realm_token: Optional[str] = None, update_endpoints: Optional[bool] = False, zone_endpoints: Optional[str] = None, # comma separated endpoints list + zonegroup_hostnames: Optional[str] = None, rgw_user_counters_cache: Optional[bool] = False, rgw_user_counters_cache_size: Optional[int] = None, rgw_bucket_counters_cache: Optional[bool] = False, - rgw_bucket_counters_cache_size: Optional[int] = None + rgw_bucket_counters_cache_size: Optional[int] = None, ): assert service_type == 'rgw', service_type @@ -1217,6 +1218,7 @@ class RGWSpec(ServiceSpec): self.rgw_realm_token = rgw_realm_token self.update_endpoints = update_endpoints self.zone_endpoints = zone_endpoints + self.zonegroup_hostnames = zonegroup_hostnames #: To track op metrics by user config value rgw_user_counters_cache must be set to true self.rgw_user_counters_cache = rgw_user_counters_cache diff --git a/src/python-common/ceph/rgw/rgwam_core.py b/src/python-common/ceph/rgw/rgwam_core.py index 7041ea1544f..333f4901585 100644 --- a/src/python-common/ceph/rgw/rgwam_core.py +++ b/src/python-common/ceph/rgw/rgwam_core.py @@ -149,11 +149,12 @@ class RGWCmdBase: opt_arg(self.cmd_suffix, '--rgw-zone', zone_env.zone.name) opt_arg(self.cmd_suffix, '--zone-id', zone_env.zone.id) - def run(self, cmd): + def run(self, cmd, stdin=None): args = cmd + self.cmd_suffix - cmd, returncode, stdout, stderr = self.mgr.tool_exec(self.prog, args) + cmd, returncode, stdout, stderr = self.mgr.tool_exec(self.prog, args, stdin) log.debug('cmd=%s' % str(cmd)) + log.debug(f'stdin={stdin}') log.debug('stdout=%s' % stdout) if returncode != 0: @@ -174,8 +175,8 @@ class RGWAdminJSONCmd(RGWAdminCmd): def __init__(self, zone_env: ZoneEnv): super().__init__(zone_env) - def run(self, cmd): - stdout, _ = RGWAdminCmd.run(self, cmd) + def run(self, cmd, stdin=None): + stdout, _ = RGWAdminCmd.run(self, cmd, stdin) return json.loads(stdout) @@ -237,9 +238,13 @@ class ZonegroupOp: def get(self, zonegroup: EntityKey = None): ze = ZoneEnv(self.env) params = ['zonegroup', 'get'] - opt_arg(params, '--rgw-zonegroup', zonegroup) return RGWAdminJSONCmd(ze).run(params) + def set(self, zonegroup: EntityKey, zg_json: str): + ze = ZoneEnv(self.env) + params = ['zonegroup', 'set'] + return RGWAdminJSONCmd(ze).run(params, stdin=zg_json.encode('utf-8')) + def create(self, realm: EntityKey, zg: EntityKey = None, endpoints=None, is_master=True): ze = ZoneEnv(self.env, realm=realm).init_zg(zg, gen=True) @@ -724,6 +729,43 @@ class RGWAM: return (0, success_message, '') + def zonegroup_modify(self, realm_name, zonegroup_name, zone_name, hostnames): + if realm_name is None: + raise RGWAMException('Realm name is a mandatory parameter') + if zone_name is None: + raise RGWAMException('Zone name is a mandatory parameter') + if zonegroup_name is None: + raise RGWAMException('Zonegroup name is a mandatory parameter') + + realm = EntityName(realm_name) + zone = EntityName(zone_name) + period_info = self.period_op().get(realm) + period = RGWPeriod(period_info) + logging.info('Period: ' + period.id) + zonegroup = period.find_zonegroup_by_name(zonegroup_name) + if not zonegroup: + raise RGWAMException(f'zonegroup {zonegroup_name} not found') + zg = EntityName(zonegroup.name) + zg_json = self.zonegroup_op().get(zg) + + if hostnames: + zg_json['hostnames'] = hostnames + + try: + self.zonegroup_op().set(zg, json.dumps(zg_json)) + except RGWAMException as e: + raise RGWAMException('failed to set zonegroup', e) + + try: + period_info = self.period_op().update(realm, zg, zone, True) + except RGWAMException as e: + raise RGWAMException('failed to update period', e) + + period = RGWPeriod(period_info) + logging.debug(period.to_json()) + + return (0, f'Modified zonegroup {zonegroup_name} of realm {realm_name}', '') + def get_realms_info(self): realms_info = [] for realm_name in self.realm_op().list(): -- 2.39.5