|
6 | 6 | import functools |
7 | 7 | import sys |
8 | 8 |
|
9 | | -from mgr_module import MgrModule, CLICommand, HandleCommandResult, Option |
| 9 | +from mgr_module import ( |
| 10 | + MgrModule, |
| 11 | + CLICommand, |
| 12 | + HandleCommandResult, |
| 13 | + Option, |
| 14 | + MonCommandFailed, |
| 15 | +) |
10 | 16 | import orchestrator |
11 | 17 |
|
12 | 18 | from ceph.deployment.service_spec import RGWSpec, PlacementSpec, SpecValidationError |
@@ -49,6 +55,10 @@ class RGWSpecParsingError(Exception): |
49 | 55 | pass |
50 | 56 |
|
51 | 57 |
|
| 58 | +class PoolCreationError(Exception): |
| 59 | + pass |
| 60 | + |
| 61 | + |
52 | 62 | class OrchestratorAPI(OrchestratorClientMixin): |
53 | 63 | def __init__(self, mgr: MgrModule): |
54 | 64 | super(OrchestratorAPI, self).__init__() |
@@ -196,10 +206,14 @@ def _cmd_rgw_realm_bootstrap(self, |
196 | 206 |
|
197 | 207 | try: |
198 | 208 | for spec in rgw_specs: |
| 209 | + self.create_pools(spec) |
199 | 210 | RGWAM(self.env).realm_bootstrap(spec, start_radosgw) |
200 | 211 | except RGWAMException as e: |
201 | 212 | self.log.error('cmd run exception: (%d) %s' % (e.retcode, e.message)) |
202 | 213 | return HandleCommandResult(retval=e.retcode, stdout=e.stdout, stderr=e.stderr) |
| 214 | + except PoolCreationError as e: |
| 215 | + self.log.error(f'Pool creation failure: {str(e)}') |
| 216 | + return HandleCommandResult(retval=-errno.EINVAL, stderr=str(e)) |
203 | 217 |
|
204 | 218 | return HandleCommandResult(retval=0, stdout="Realm(s) created correctly. Please, use 'ceph rgw realm tokens' to get the token.", stderr='') |
205 | 219 |
|
@@ -229,6 +243,85 @@ def _parse_rgw_specs(self, inbuf: str) -> List[RGWSpec]: |
229 | 243 |
|
230 | 244 | return rgw_specs |
231 | 245 |
|
| 246 | + def create_pools(self, spec: RGWSpec) -> None: |
| 247 | + def _pool_create_command( |
| 248 | + pool_name: str, |
| 249 | + pool_type: str, |
| 250 | + pool_attrs: Optional[Dict[str, Union[str, List[str]]]] = None |
| 251 | + ) -> None: |
| 252 | + try: |
| 253 | + cmd_dict: Dict[str, Union[str, List[str]]] = { |
| 254 | + 'prefix': 'osd pool create', |
| 255 | + 'pool': pool_name, |
| 256 | + 'pool_type': pool_type, |
| 257 | + } |
| 258 | + if pool_attrs: |
| 259 | + for k, v in pool_attrs.items(): |
| 260 | + cmd_dict[k] = v |
| 261 | + self.check_mon_command(cmd_dict) |
| 262 | + except MonCommandFailed as e: |
| 263 | + raise PoolCreationError(f'RGW module failed to create pool {pool_name} ' |
| 264 | + f'of type {pool_type} with attrs [{pool_attrs}]: {str(e)}') |
| 265 | + # enable the rgw application on the pool |
| 266 | + try: |
| 267 | + self.check_mon_command({ |
| 268 | + 'prefix': 'osd pool application enable', |
| 269 | + 'pool': pool_name, |
| 270 | + 'app': 'rgw', |
| 271 | + }) |
| 272 | + except MonCommandFailed as e: |
| 273 | + raise PoolCreationError(f'Failed enabling application "rgw" on pool {pool_name}: {str(e)}') |
| 274 | + |
| 275 | + zone_name = spec.rgw_zone |
| 276 | + for pool_suffix in [ |
| 277 | + 'buckets.index', |
| 278 | + 'meta', |
| 279 | + 'log', |
| 280 | + 'control' |
| 281 | + ]: |
| 282 | + # TODO: add size? |
| 283 | + non_data_pool_attrs: Dict[str, Union[str, List[str]]] = { |
| 284 | + 'pg-num': '16' if 'index' in pool_suffix else '8', |
| 285 | + } |
| 286 | + _pool_create_command(f'{zone_name}.rgw.{pool_suffix}', 'replicated', non_data_pool_attrs) |
| 287 | + |
| 288 | + if spec.data_pool_attributes: |
| 289 | + if spec.data_pool_attributes.get('type', 'ec') == 'ec': |
| 290 | + # we need to create ec profile |
| 291 | + assert zone_name is not None |
| 292 | + profile_name = self.create_zone_ec_profile(zone_name, spec.data_pool_attributes) |
| 293 | + # now we can pass the ec profile into the pool create command |
| 294 | + data_pool_attrs: Dict[str, Union[str, List[str]]] = { |
| 295 | + 'erasure_code_profile': profile_name |
| 296 | + } |
| 297 | + if 'pg_num' in spec.data_pool_attributes: |
| 298 | + data_pool_attrs['pg_num'] = spec.data_pool_attributes['pg_num'] |
| 299 | + _pool_create_command(f'{zone_name}.rgw.buckets.data', 'erasure', data_pool_attrs) |
| 300 | + else: |
| 301 | + # replicated pool |
| 302 | + data_pool_attrs = {k: v for k, v in spec.data_pool_attributes.items() if k != 'type'} |
| 303 | + _pool_create_command(f'{zone_name}.rgw.buckets.data', 'replicated', data_pool_attrs) |
| 304 | + |
| 305 | + def create_zone_ec_profile(self, zone_name: str, pool_attributes: Optional[Dict[str, str]]) -> str: |
| 306 | + # creates ec profile and returns profile name |
| 307 | + ec_pool_kv_pairs = {} |
| 308 | + if pool_attributes is not None: |
| 309 | + ec_pool_kv_pairs = {k: v for k, v in pool_attributes.items() if k not in ['type', 'pg_num']} |
| 310 | + profile_name = f'{zone_name}_zone_data_pool_ec_profile' |
| 311 | + profile_attrs = [f'{k}={v}' for k, v in ec_pool_kv_pairs.items()] |
| 312 | + cmd_dict: Dict[str, Union[str, List[str]]] = { |
| 313 | + 'prefix': 'osd erasure-code-profile set', |
| 314 | + 'name': profile_name, |
| 315 | + } |
| 316 | + if profile_attrs: |
| 317 | + cmd_dict['profile'] = profile_attrs |
| 318 | + try: |
| 319 | + self.check_mon_command(cmd_dict) |
| 320 | + except MonCommandFailed as e: |
| 321 | + raise PoolCreationError(f'RGW module failed to create ec profile {profile_name} ' |
| 322 | + f'with attrs {profile_attrs}: {str(e)}') |
| 323 | + return profile_name |
| 324 | + |
232 | 325 | @CLICommand('rgw realm zone-creds create', perm='rw') |
233 | 326 | def _cmd_rgw_realm_new_zone_creds(self, |
234 | 327 | realm_name: Optional[str] = None, |
|
0 commit comments