Addressing adking review comments

Signed-off-by: Redouane Kachach <rkachach@redhat.com>
This commit is contained in:
Redouane Kachach 2022-10-11 10:10:16 +02:00
parent 0bd530db3f
commit b94c2b685a
No known key found for this signature in database
GPG Key ID: 843EE0E7D13F5049
5 changed files with 97 additions and 73 deletions

View File

@ -77,9 +77,9 @@ the `ceph rgw realm tokens` output:
}
]
User can use the token to create and synchronize a secondary zones
on another cluster with the master zone by using `ceph rgw zone create`
command and proving the corresponding token.
User can use the token to pull a realm to create secondary zone on a
different cluster that syncs with the master zone on the primary cluster
by using `ceph rgw zone create` command and providing the corresponding token.
Following is an example of zone spec file:
@ -101,9 +101,9 @@ Following is an example of zone spec file:
ceph rgw zone create -i zone-spec.yaml
.. note:: The spec file used by RGW has the same format as the one used by cephadm. Thus,
the user can provide any cephadm rgw supported parameter as any other advanced
configuration items such as SSL certificates etc.
.. note:: The spec file used by RGW has the same format as the one used by the orchestrator. Thus,
the user can provide any orchestration supported rgw parameters including advanced
configuration features such as SSL certificates etc.
Commands
--------

View File

@ -561,6 +561,35 @@ class CephadmServe:
self.mgr.set_health_warning('CEPHADM_FAILED_SET_OPTION', f'Failed to set {len(options_failed_to_set)} option(s)', len(
options_failed_to_set), options_failed_to_set)
def _update_rgw_endpoints(self, rgw_spec: RGWSpec) -> None:
if not rgw_spec.update_endpoints or rgw_spec.rgw_realm_token is None:
return
ep = []
protocol = 'https' if rgw_spec.ssl else 'http'
for s in self.mgr.cache.get_daemons_by_service(rgw_spec.service_name()):
if s.ports:
for p in s.ports:
ep.append(f'{protocol}://{s.hostname}:{p}')
zone_update_cmd = {
'prefix': 'rgw zone update',
'realm_name': rgw_spec.rgw_realm,
'zonegroup_name': rgw_spec.rgw_zonegroup,
'zone_name': rgw_spec.rgw_zone,
'realm_token': rgw_spec.rgw_realm_token,
'endpoints': ep,
}
self.log.debug(f'rgw cmd: {zone_update_cmd}')
rc, out, err = self.mgr.mon_command(zone_update_cmd)
rgw_spec.update_endpoints = (rc != 0) # keep trying on failure
if rc != 0:
self.log.error(f'Error when trying to update rgw zone {err}')
self.mgr.set_health_warning('CEPHADM_RGW', 'Cannot update rgw endpoints', 1,
[f'Cannot update rgw endpoints for daemon {rgw_spec.service_name()}'])
else:
self.mgr.remove_health_warning('CEPHADM_RGW')
def _apply_service(self, spec: ServiceSpec) -> bool:
"""
Schedule a service. Deploy new daemons or remove old ones, depending
@ -815,26 +844,7 @@ class CephadmServe:
self.mgr._schedule_daemon_action(active_mgr.name(), 'restart')
if service_type == 'rgw':
rgw_spec = cast(RGWSpec, spec)
if rgw_spec.update_endpoints and rgw_spec.rgw_realm_token is not None:
ep = []
for s in self.mgr.cache.get_daemons_by_service(rgw_spec.service_name()):
if s.ports:
for p in s.ports:
ep.append(f'http://{s.hostname}:{p}')
zone_update_cmd = {
'prefix': 'rgw zone update',
'realm_name': rgw_spec.rgw_realm,
'zonegroup_name': rgw_spec.rgw_zonegroup,
'zone_name': rgw_spec.rgw_zone,
'realm_token': rgw_spec.rgw_realm_token,
'endpoints': ep,
}
self.log.debug(f'rgw cmd: {zone_update_cmd}')
rc, out, err = self.mgr.mon_command(zone_update_cmd)
rgw_spec.update_endpoints = (rc != 0) # keep trying on failure
if rc != 0:
self.log.error(f'Error when trying to update rgw zone {err}.. keep trying')
self._update_rgw_endpoints(cast(RGWSpec, spec))
# remove any?
def _ok_to_stop(remove_daemons: List[orchestrator.DaemonDescription]) -> bool:

View File

@ -128,29 +128,30 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
start_radosgw: Optional[bool] = True,
inbuf: Optional[str] = None):
"""Bootstrap new rgw realm, zonegroup, and zone"""
try:
if inbuf:
rgw_specs = self._parse_rgw_specs(inbuf)
elif (realm_name and zonegroup_name and zone_name):
placement_spec = PlacementSpec.from_string(placement) if placement else None
rgw_specs = [RGWSpec(rgw_realm=realm_name,
rgw_zonegroup=zonegroup_name,
rgw_zone=zone_name,
rgw_frontend_port=port,
placement=placement_spec)]
else:
return HandleCommandResult(retval=-errno.EINVAL, stdout='', stderr='Invalid arguments: -h or --help for usage')
if inbuf:
rgw_specs = self._parse_rgw_specs(inbuf)
elif (realm_name and zonegroup_name and zone_name):
placement_spec = PlacementSpec.from_string(placement) if placement else None
rgw_specs = [RGWSpec(rgw_realm=realm_name,
rgw_zonegroup=zonegroup_name,
rgw_zone=zone_name,
rgw_frontend_port=port,
placement=placement_spec)]
else:
err_msg = 'Invalid arguments: either pass a spec with -i or provide the realm, zonegroup and zone.'
return HandleCommandResult(retval=-errno.EINVAL, stdout='', stderr=err_msg)
try:
for spec in rgw_specs:
RGWAM(self.env).realm_bootstrap(spec, start_radosgw)
except RGWAMException as e:
self.log.error('cmd run exception: (%d) %s' % (e.retcode, e.message))
return (e.retcode, e.message, e.stderr)
return HandleCommandResult(retval=0, stdout="Realm(s) created correctly. Please, use 'ceph rgw realm tokens' to get the token.", stderr='')
def _parse_rgw_specs(self, inbuf: Optional[str] = None):
def _parse_rgw_specs(self, inbuf: Optional[str] = None) -> List[RGWSpec]:
"""Parse RGW specs from a YAML file."""
# YAML '---' document separator with no content generates
# None entries in the output. Let's skip them silently.
@ -158,7 +159,6 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
specs = [o for o in yaml_objs if o is not None]
rgw_specs = []
for spec in specs:
# TODO(rkachach): should we use a new spec instead of RGWSpec here!
rgw_spec = RGWSpec.from_json(spec)
rgw_spec.validate()
rgw_specs.append(rgw_spec)
@ -193,22 +193,26 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
@CLICommand('rgw realm tokens', perm='r')
def list_realm_tokens(self):
realms_info = []
for realm_info in RGWAM(self.env).get_realms_info():
if not realm_info['master_zone_id']:
realms_info.append({'realm': realm_info['realm_name'], 'token': 'realm has no master zone'})
elif not realm_info['endpoint']:
realms_info.append({'realm': realm_info['realm_name'], 'token': 'master zone has no endpoint'})
elif not (realm_info['access_key'] and realm_info['secret']):
realms_info.append({'realm': realm_info['realm_name'], 'token': 'master zone has no access/secret keys'})
else:
keys = ['realm_name', 'realm_id', 'is_primary', 'endpoint', 'access_key', 'secret']
realm_token = RealmToken(**{k: realm_info[k] for k in keys})
realm_token_b = realm_token.to_json().encode('utf-8')
realm_token_s = base64.b64encode(realm_token_b).decode('utf-8')
realms_info.append({'realm': realm_info['realm_name'], 'token': realm_token_s})
try:
realms_info = []
for realm_info in RGWAM(self.env).get_realms_info():
if not realm_info['master_zone_id']:
realms_info.append({'realm': realm_info['realm_name'], 'token': 'realm has no master zone'})
elif not realm_info['endpoint']:
realms_info.append({'realm': realm_info['realm_name'], 'token': 'master zone has no endpoint'})
elif not (realm_info['access_key'] and realm_info['secret']):
realms_info.append({'realm': realm_info['realm_name'], 'token': 'master zone has no access/secret keys'})
else:
keys = ['realm_name', 'realm_id', 'is_primary', 'endpoint', 'access_key', 'secret']
realm_token = RealmToken(**{k: realm_info[k] for k in keys})
realm_token_b = realm_token.to_json().encode('utf-8')
realm_token_s = base64.b64encode(realm_token_b).decode('utf-8')
realms_info.append({'realm': realm_info['realm_name'], 'token': realm_token_s})
except RGWAMException as e:
self.log.error(f'cmd run exception: ({e.retcode}) {e.message}')
return (e.retcode, e.message, e.stderr)
return HandleCommandResult(retval=0, stdout=json.dumps(realms_info), stderr='')
return HandleCommandResult(retval=0, stdout=json.dumps(realms_info, indent=4), stderr='')
@CLICommand('rgw zone update', perm='rw')
def update_zone_info(self, realm_name: str, zonegroup_name: str, zone_name: str, realm_token: str, endpoints: List[str]):
@ -233,23 +237,24 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
start_radosgw: Optional[bool] = True,
inbuf: Optional[str] = None):
"""Bootstrap new rgw zone that syncs with existing zone"""
try:
if inbuf:
rgw_specs = self._parse_rgw_specs(inbuf)
elif (zone_name and realm_token):
placement_spec = PlacementSpec.from_string(placement) if placement else None
rgw_specs = [RGWSpec(rgw_realm_token=realm_token,
rgw_zone=zone_name,
rgw_frontend_port=port,
placement=placement_spec)]
else:
return HandleCommandResult(retval=-errno.EINVAL, stdout='', stderr='Invalid arguments: -h or --help for usage')
if inbuf:
rgw_specs = self._parse_rgw_specs(inbuf)
elif (zone_name and realm_token):
placement_spec = PlacementSpec.from_string(placement) if placement else None
rgw_specs = [RGWSpec(rgw_realm_token=realm_token,
rgw_zone=zone_name,
rgw_frontend_port=port,
placement=placement_spec)]
else:
err_msg = 'Invalid arguments: either pass a spec with -i or provide the zone_name and realm_token.'
return HandleCommandResult(retval=-errno.EINVAL, stdout='', stderr=err_msg)
try:
for rgw_spec in rgw_specs:
retval, out, err = RGWAM(self.env).zone_create(rgw_spec, start_radosgw)
if retval != 0:
break
except RGWAMException as e:
self.log.error('cmd run exception: (%d) %s' % (e.retcode, e.message))
return (e.retcode, e.message, e.stderr)

View File

@ -890,10 +890,16 @@ class RGWSpec(ServiceSpec):
extra_container_args=extra_container_args, custom_configs=custom_configs)
#: The RGW realm associated with this service. Needs to be manually created
#: if the spec is being applied directly to cephdam. In case of rgw module
#: the realm is created automatically.
self.rgw_realm: Optional[str] = rgw_realm
#: The RGW zonegroup associated with this service. Needs to be manually created
#: if the spec is being applied directly to cephdam. In case of rgw module
#: the zonegroup is created automatically.
self.rgw_zonegroup: Optional[str] = rgw_zonegroup
#: The RGW zone associated with this service. Needs to be manually created
#: if the spec is being applied directly to cephdam. In case of rgw module
#: the zone is created automatically.
self.rgw_zone: Optional[str] = rgw_zone
#: Port of the RGW daemons
self.rgw_frontend_port: Optional[int] = rgw_frontend_port

View File

@ -195,7 +195,8 @@ class RealmOp:
params = ['realm', 'list']
output = RGWAdminJSONCmd(ze).run(params)
return output.get('realms') or []
except RGWAMException:
except RGWAMException as e:
logging.info(f'Exception while listing realms {e.message}')
# in case the realm list is empty an exception is raised
return []
@ -229,7 +230,8 @@ class ZonegroupOp:
params = ['zonegroup', 'list']
output = RGWAdminJSONCmd(ze).run(params)
return output.get('zonegroups') or []
except RGWAMException:
except RGWAMException as e:
logging.info(f'Exception while listing zonegroups {e.message}')
return []
def get(self, zonegroup: EntityKey = None):
@ -262,7 +264,8 @@ class ZoneOp:
params = ['zone', 'list']
output = RGWAdminJSONCmd(ze).run(params)
return output.get('zones') or []
except RGWAMException:
except RGWAMException as e:
logging.info(f'Exception while listing zones {e.message}')
return []
def get(self, zone: EntityKey):
@ -483,7 +486,7 @@ class RGWAM:
uid_prefix='user-sys',
is_system=True)
sys_user = RGWUser(sys_user_info)
logging.info('Created system user: %s' % sys_user.uid)
logging.info(f'Created system user: {sys_user.uid} on {realm.name}/{zonegroup.name}/{zone.name}')
access_key = sys_user.keys[0].access_key if sys_user and sys_user.keys else ''
secret_key = sys_user.keys[0].secret_key if sys_user and sys_user.keys else ''
sys_user.add_key(access_key, secret_key)
@ -495,7 +498,7 @@ class RGWAM:
try:
user_info = self.user_op().create(zone, zg, uid=uid, is_system=False)
user = RGWUser(user_info)
logging.info('Created regular user: %s' % user.uid)
logging.info('Created regular user {user.uid} on {realm.name}/{zonegroup.name}/{zone.name}')
return user
except RGWAMException as e:
raise RGWAMException('failed to create user', e)