from ..security import Permission, Scope
from ..services.auth import AuthManager, JwtManager
from ..services.ceph_service import CephService
-from ..services.orchestrator import OrchClient
-from ..services.rgw_client import NoRgwDaemonsException, RgwClient
+from ..services.rgw_client import NoRgwDaemonsException, RgwClient, RgwMultisite
from ..tools import json_str_to_object, str_to_bool
from . import APIDoc, APIRouter, BaseController, CreatePermission, \
CRUDCollectionMethod, CRUDEndpoint, Endpoint, EndpointDoc, ReadPermission, \
- RESTController, UIRouter, allow_empty_body
-from ._crud import CRUDMeta, Form, FormField, FormTaskInfo, Icon, MethodType, TableAction, \
- Validator, VerticalContainer
+ RESTController, UIRouter, UpdatePermission, allow_empty_body
+from ._crud import CRUDMeta, Form, FormField, FormTaskInfo, Icon, MethodType, \
+ TableAction, Validator, VerticalContainer
from ._version import APIVersion
logger = logging.getLogger("controllers.rgw")
@UIRouter('/rgw/multisite')
-class RgwStatus(BaseController):
+class RgwMultisiteStatus(RESTController):
@Endpoint()
@ReadPermission
# pylint: disable=R0801
def status(self):
status = {'available': True, 'message': None}
- try:
- instance = RgwClient.admin_instance()
- is_multisite_configured = instance.get_multisite_status()
- if not is_multisite_configured:
- status['available'] = False
- status['message'] = 'Multi-site provides disaster recovery and may also \
- serve as a foundation for content delivery networks' # type: ignore
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ multisite_instance = RgwMultisite()
+ is_multisite_configured = multisite_instance.get_multisite_status()
+ if not is_multisite_configured:
+ status['available'] = False
+ status['message'] = 'Multi-site provides disaster recovery and may also \
+ serve as a foundation for content delivery networks' # type: ignore
return status
- @Endpoint()
- @ReadPermission
- # pylint: disable=R0801
- def sync_status(self):
- try:
- instance = RgwClient.admin_instance()
- result = instance.get_multisite_sync_status()
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw') # noqa: E501 pylint: disable=line-too-long
- return result
-
- @Endpoint(method='PUT')
- # pylint: disable=W0102
- def migrate(self, realm_name=None, zonegroup_name=None, zone_name=None,
- zonegroup_endpoints: List[str] = [], zone_endpoints: List[str] = [],
- user=None, daemon_name: Optional[str] = None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.migrate_to_multisite(realm_name, zonegroup_name, zone_name,
- zonegroup_endpoints, zone_endpoints, user)
- orch = OrchClient.instance()
- daemons = orch.services.list_daemons(service_name='rgw')
- for daemon in daemons:
- orch.daemons.action(action='reload', daemon_name=daemon.daemon_id)
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ @RESTController.Collection(method='PUT', path='/migrate')
+ @allow_empty_body
+ # pylint: disable=W0102,W0613
+ def migrate(self, daemon_name=None, realm_name=None, zonegroup_name=None, zone_name=None,
+ zonegroup_endpoints=None, zone_endpoints=None, access_key=None,
+ secret_key=None):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.migrate_to_multisite(realm_name, zonegroup_name,
+ zone_name, zonegroup_endpoints,
+ zone_endpoints, access_key,
+ secret_key)
return result
daemon['rgw_status'] = status
return daemon
+ @RESTController.Collection(method='PUT', path='/set_multisite_config')
+ @allow_empty_body
+ def set_multisite_config(self, realm_name=None, zonegroup_name=None,
+ zone_name=None, daemon_name=None):
+ CephService.set_multisite_config(realm_name, zonegroup_name, zone_name, daemon_name)
+
class RgwRESTController(RESTController):
def proxy(self, daemon_name, method, path, params=None, json_response=True):
class RgwRealm(RESTController):
@allow_empty_body
# pylint: disable=W0613
- def create(self, realm_name, default, daemon_name=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.create_realm(realm_name, default)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def create(self, realm_name, default):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.create_realm(realm_name, default)
+ return result
@allow_empty_body
# pylint: disable=W0613
- def list(self, daemon_name=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.list_realms()
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def list(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.list_realms()
+ return result
@allow_empty_body
# pylint: disable=W0613
- def get(self, realm_name, daemon_name=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.get_realm(realm_name)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def get(self, realm_name):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_realm(realm_name)
+ return result
@Endpoint()
@ReadPermission
def get_all_realms_info(self):
- try:
- instance = RgwClient.admin_instance()
- result = instance.get_all_realms_info()
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_all_realms_info()
+ return result
@allow_empty_body
# pylint: disable=W0613
- def set(self, realm_name: str, new_realm_name: str, default: str = '', daemon_name=None):
+ def set(self, realm_name: str, new_realm_name: str, default: str = ''):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.edit_realm(realm_name, new_realm_name, default)
+ return result
+
+ @Endpoint()
+ @ReadPermission
+ def get_realm_tokens(self):
try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.edit_realm(realm_name, new_realm_name, default)
+ result = CephService.get_realm_tokens()
return result
except NoRgwDaemonsException as e:
raise DashboardException(e, http_status_code=404, component='rgw')
- def delete(self, realm_name, daemon_name=None):
+ @Endpoint(method='POST')
+ @UpdatePermission
+ @allow_empty_body
+ # pylint: disable=W0613
+ def import_realm_token(self, realm_token, zone_name, daemon_name=None):
try:
- instance = RgwClient.admin_instance(daemon_name)
- result = instance.delete_realm(realm_name)
+ multisite_instance = RgwMultisite()
+ result = CephService.import_realm_token(realm_token, zone_name)
+ multisite_instance.update_period()
return result
except NoRgwDaemonsException as e:
raise DashboardException(e, http_status_code=404, component='rgw')
+ def delete(self, realm_name):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.delete_realm(realm_name)
+ return result
+
@APIRouter('/rgw/zonegroup', Scope.RGW)
class RgwZonegroup(RESTController):
@allow_empty_body
# pylint: disable=W0613
def create(self, realm_name, zonegroup_name, default=None, master=None,
- zonegroup_endpoints=None, daemon_name=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.create_zonegroup(realm_name, zonegroup_name, default,
- master, zonegroup_endpoints)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ zonegroup_endpoints=None):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.create_zonegroup(realm_name, zonegroup_name, default,
+ master, zonegroup_endpoints)
+ return result
@allow_empty_body
# pylint: disable=W0613
- def list(self, daemon_name=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.list_zonegroups()
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def list(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.list_zonegroups()
+ return result
@allow_empty_body
# pylint: disable=W0613
- def get(self, zonegroup_name, daemon_name=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.get_zonegroup(zonegroup_name)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def get(self, zonegroup_name):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_zonegroup(zonegroup_name)
+ return result
@Endpoint()
@ReadPermission
def get_all_zonegroups_info(self):
- try:
- instance = RgwClient.admin_instance()
- result = instance.get_all_zonegroups_info()
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_all_zonegroups_info()
+ return result
- def delete(self, zonegroup_name, delete_pools, pools: Optional[List[str]] = None,
- daemon_name=None):
+ def delete(self, zonegroup_name, delete_pools, pools: Optional[List[str]] = None):
if pools is None:
pools = []
try:
- instance = RgwClient.admin_instance(daemon_name)
- result = instance.delete_zonegroup(zonegroup_name, delete_pools, pools)
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.delete_zonegroup(zonegroup_name, delete_pools, pools)
return result
except NoRgwDaemonsException as e:
raise DashboardException(e, http_status_code=404, component='rgw')
@allow_empty_body
# pylint: disable=W0613,W0102
- def set(self, zonegroup_name: str, realm_name: str, new_zonegroup_name: str, default: str = '',
- master: str = '', zonegroup_endpoints: List[str] = [], add_zones: List[str] = [],
- remove_zones: List[str] = [], placement_targets: List[Dict[str, str]] = [],
- daemon_name=None):
- try:
- instance = RgwClient.admin_instance()
- result = instance.edit_zonegroup(realm_name, zonegroup_name, new_zonegroup_name,
- default, master, zonegroup_endpoints, add_zones,
- remove_zones, placement_targets)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def set(self, zonegroup_name: str, realm_name: str, new_zonegroup_name: str,
+ default: str = '', master: str = '', zonegroup_endpoints: str = '',
+ add_zones: List[str] = [], remove_zones: List[str] = [],
+ placement_targets: List[Dict[str, str]] = []):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.edit_zonegroup(realm_name, zonegroup_name, new_zonegroup_name,
+ default, master, zonegroup_endpoints, add_zones,
+ remove_zones, placement_targets)
+ return result
@APIRouter('/rgw/zone', Scope.RGW)
@allow_empty_body
# pylint: disable=W0613
def create(self, zone_name, zonegroup_name=None, default=False, master=False,
- zone_endpoints=None, user=None, createSystemUser=False, daemon_name=None,
- master_zone_of_master_zonegroup=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.create_zone(zone_name, zonegroup_name, default,
- master, zone_endpoints, user, createSystemUser,
- master_zone_of_master_zonegroup)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ zone_endpoints=None, access_key=None, secret_key=None):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.create_zone(zone_name, zonegroup_name, default,
+ master, zone_endpoints, access_key,
+ secret_key)
+ return result
@allow_empty_body
# pylint: disable=W0613
- def list(self, daemon_name=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.list_zones()
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def list(self):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.list_zones()
+ return result
@allow_empty_body
# pylint: disable=W0613
- def get(self, zone_name, daemon_name=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.get_zone(zone_name)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def get(self, zone_name):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_zone(zone_name)
+ return result
@Endpoint()
@ReadPermission
def get_all_zones_info(self):
- try:
- instance = RgwClient.admin_instance()
- result = instance.get_all_zones_info()
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_all_zones_info()
+ return result
def delete(self, zone_name, delete_pools, pools: Optional[List[str]] = None,
- zonegroup_name=None, daemon_name=None):
+ zonegroup_name=None):
if pools is None:
pools = []
if zonegroup_name is None:
zonegroup_name = ''
try:
- instance = RgwClient.admin_instance(daemon_name)
- result = instance.delete_zone(zone_name, delete_pools, pools, zonegroup_name)
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.delete_zone(zone_name, delete_pools, pools, zonegroup_name)
return result
except NoRgwDaemonsException as e:
raise DashboardException(e, http_status_code=404, component='rgw')
@allow_empty_body
# pylint: disable=W0613,W0102
def set(self, zone_name: str, new_zone_name: str, zonegroup_name: str, default: str = '',
- master: str = '', zone_endpoints: List[str] = [], user: str = '',
+ master: str = '', zone_endpoints: str = '', access_key: str = '', secret_key: str = '',
placement_target: str = '', data_pool: str = '', index_pool: str = '',
data_extra_pool: str = '', storage_class: str = '', data_pool_class: str = '',
- compression: str = '', daemon_name=None, master_zone_of_master_zonegroup=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.edit_zone(zone_name, new_zone_name, zonegroup_name, default,
- master, zone_endpoints, user, placement_target,
- data_pool, index_pool, data_extra_pool, storage_class,
- data_pool_class, compression,
- master_zone_of_master_zonegroup)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ compression: str = ''):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.edit_zone(zone_name, new_zone_name, zonegroup_name, default,
+ master, zone_endpoints, access_key, secret_key,
+ placement_target, data_pool, index_pool,
+ data_extra_pool, storage_class, data_pool_class,
+ compression)
+ return result
@Endpoint()
@ReadPermission
@Endpoint('PUT')
@CreatePermission
- def create_system_user(self, userName: str, zoneName: str, daemon_name=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.create_system_user(userName, zoneName)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def create_system_user(self, userName: str, zoneName: str):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.create_system_user(userName, zoneName)
+ return result
@Endpoint()
@ReadPermission
- def get_user_list(self, daemon_name=None, zoneName=None):
- try:
- instance = RgwClient.admin_instance(daemon_name=daemon_name)
- result = instance.get_user_list(zoneName)
- return result
- except NoRgwDaemonsException as e:
- raise DashboardException(e, http_status_code=404, component='rgw')
+ def get_user_list(self, zoneName=None):
+ multisite_instance = RgwMultisite()
+ result = multisite_instance.get_user_list(zoneName)
+ return result
[formGroup]="serviceForm"
novalidate>
<div class="modal-body">
-
+ <cd-alert-panel *ngIf="serviceForm.controls.service_type.value === 'rgw' && showRealmCreationForm"
+ type="info"
+ spacingClass="mb-3"
+ i18n>
+ <a class="text-decoration-underline"
+ (click)="createMultisiteSetup()">
+ Click here</a> to create a new realm/zonegroup/zone
+ </cd-alert-panel>
<!-- Service type -->
<div class="form-group row">
<label class="cd-col-form-label required"
<span class="invalid-feedback"
*ngIf="serviceForm.showError('service_id', frm, 'uniqueName')"
i18n>This service id is already in use.</span>
- <span class="invalid-feedback"
- *ngIf="serviceForm.showError('service_id', frm, 'rgwPattern')"
- i18n>The value does not match the pattern <strong><service_id>[.<realm_name>.<zone_name>]</strong>.</span>
<span class="invalid-feedback"
*ngIf="serviceForm.showError('service_id', frm, 'mdsPattern')"
i18n>MDS service id must start with a letter and contain alphanumeric characters or '.', '-', and '_'</span>
</div>
</div>
+ <div class="form-group row"
+ *ngIf="serviceForm.controls.service_type.value === 'rgw'">
+ <label class="cd-col-form-label"
+ for="realm_name"
+ i18n>Realm</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="realm_name"
+ formControlName="realm_name"
+ name="realm_name"
+ [attr.disabled]="realmList.length === 0 || editing ? true : null">
+ <option *ngIf="realmList.length === 0"
+ i18n
+ selected>-- No realm available --</option>
+ <option *ngFor="let realm of realmList"
+ [value]="realm.name">
+ {{ realm.name }}
+ </option>
+ </select>
+ </div>
+ </div>
+
+ <div class="form-group row"
+ *ngIf="serviceForm.controls.service_type.value === 'rgw'">
+ <label class="cd-col-form-label"
+ for="zonegroup_name"
+ i18n>Zonegroup</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="zonegroup_name"
+ formControlName="zonegroup_name"
+ name="zonegroup_name"
+ [attr.disabled]="zonegroupList.length === 0 || editing ? true : null">
+ <option *ngFor="let zonegroup of zonegroupList"
+ [value]="zonegroup.name">
+ {{ zonegroup.name }}
+ </option>
+ </select>
+ </div>
+ </div>
+
+ <div class="form-group row"
+ *ngIf="serviceForm.controls.service_type.value === 'rgw'">
+ <label class="cd-col-form-label"
+ for="zone_name"
+ i18n>Zone</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="zone_name"
+ formControlName="zone_name"
+ name="zone_name"
+ [attr.disabled]="zoneList.length === 0 || editing ? true : null">
+ <option *ngFor="let zone of zoneList"
+ [value]="zone.name">
+ {{ zone.name }}
+ </option>
+ </select>
+ </div>
+ </div>
+
<!-- unmanaged -->
<div class="form-group row">
<div class="cd-col-form-offset">
formHelper.expectValid('service_id');
});
- it('should test rgw invalid service id', () => {
- formHelper.setValue('service_id', '.');
- formHelper.expectError('service_id', 'rgwPattern');
- formHelper.setValue('service_id', 'svc.');
- formHelper.expectError('service_id', 'rgwPattern');
- formHelper.setValue('service_id', 'svc.realm');
- formHelper.expectError('service_id', 'rgwPattern');
- formHelper.setValue('service_id', 'svc.realm.');
- formHelper.expectError('service_id', 'rgwPattern');
- formHelper.setValue('service_id', '.svc.realm');
- formHelper.expectError('service_id', 'rgwPattern');
- formHelper.setValue('service_id', 'svc.realm.zone.');
- formHelper.expectError('service_id', 'rgwPattern');
- });
-
- it('should submit rgw with realm and zone', () => {
- formHelper.setValue('service_id', 'svc.my-realm.my-zone');
+ it('should submit rgw with realm, zonegroup and zone', () => {
+ formHelper.setValue('service_id', 'svc');
+ formHelper.setValue('realm_name', 'my-realm');
+ formHelper.setValue('zone_name', 'my-zone');
+ formHelper.setValue('zonegroup_name', 'my-zonegroup');
component.onSubmit();
expect(cephServiceService.create).toHaveBeenCalledWith({
service_type: 'rgw',
service_id: 'svc',
rgw_realm: 'my-realm',
rgw_zone: 'my-zone',
+ rgw_zonegroup: 'my-zonegroup',
placement: {},
unmanaged: false,
ssl: false
expect(cephServiceService.create).toHaveBeenCalledWith({
service_type: 'rgw',
service_id: 'svc',
+ rgw_realm: null,
+ rgw_zone: null,
+ rgw_zonegroup: null,
placement: {},
unmanaged: false,
rgw_frontend_port: 1234,
expect(cephServiceService.create).toHaveBeenCalledWith({
service_type: 'rgw',
service_id: 'svc',
+ rgw_realm: null,
+ rgw_zone: null,
+ rgw_zonegroup: null,
placement: {},
unmanaged: false,
ssl: false
import { AbstractControl, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
-import { NgbActiveModal, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap';
+import { NgbActiveModal, NgbModalRef, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
-import { merge, Observable, Subject } from 'rxjs';
+import { forkJoin, merge, Observable, Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, map } from 'rxjs/operators';
+import { CreateRgwServiceEntitiesComponent } from '~/app/ceph/rgw/create-rgw-service-entities/create-rgw-service-entities.component';
+import { RgwRealm, RgwZonegroup, RgwZone } from '~/app/ceph/rgw/models/rgw-multisite';
import { CephServiceService } from '~/app/shared/api/ceph-service.service';
import { HostService } from '~/app/shared/api/host.service';
import { PoolService } from '~/app/shared/api/pool.service';
+import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
+import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
+import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
+import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
import { SelectMessages } from '~/app/shared/components/select/select-messages.model';
import { SelectOption } from '~/app/shared/components/select/select-option.model';
-import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
+import {
+ ActionLabelsI18n,
+ TimerServiceInterval,
+ URLVerbs
+} from '~/app/shared/constants/app.constants';
import { CdForm } from '~/app/shared/forms/cd-form';
import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { CdValidators } from '~/app/shared/forms/cd-validators';
import { FinishedTask } from '~/app/shared/models/finished-task';
import { CephServiceSpec } from '~/app/shared/models/service.interface';
+import { ModalService } from '~/app/shared/services/modal.service';
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
+import { TimerService } from '~/app/shared/services/timer.service';
@Component({
selector: 'cd-service-form',
styleUrls: ['./service-form.component.scss']
})
export class ServiceFormComponent extends CdForm implements OnInit {
- readonly RGW_SVC_ID_PATTERN = /^([^.]+)(\.([^.]+)\.([^.]+))?$/;
+ public sub = new Subscription();
+
readonly MDS_SVC_ID_PATTERN = /^[a-zA-Z_.-][a-zA-Z0-9_.-]*$/;
readonly SNMP_DESTINATION_PATTERN = /^[^\:]+:[0-9]/;
readonly SNMP_ENGINE_ID_PATTERN = /^[0-9A-Fa-f]{10,64}/g;
services: Array<CephServiceSpec> = [];
pageURL: string;
serviceList: CephServiceSpec[];
+ multisiteInfo: object[] = [];
+ defaultRealmId = '';
+ defaultZonegroupId = '';
+ defaultZoneId = '';
+ realmList: RgwRealm[] = [];
+ zonegroupList: RgwZonegroup[] = [];
+ zoneList: RgwZone[] = [];
+ bsModalRef: NgbModalRef;
+ defaultZonegroup: RgwZonegroup;
+ showRealmCreationForm = false;
+ defaultsInfo: { defaultRealmName: string; defaultZonegroupName: string; defaultZoneName: string };
+ realmNames: string[];
+ zonegroupNames: string[];
+ zoneNames: string[];
constructor(
public actionLabels: ActionLabelsI18n,
private poolService: PoolService,
private router: Router,
private taskWrapperService: TaskWrapperService,
+ public timerService: TimerService,
+ public timerServiceVariable: TimerServiceInterval,
+ public rgwRealmService: RgwRealmService,
+ public rgwZonegroupService: RgwZonegroupService,
+ public rgwZoneService: RgwZoneService,
+ public rgwMultisiteService: RgwMultisiteService,
private route: ActivatedRoute,
- public activeModal: NgbActiveModal
+ public activeModal: NgbActiveModal,
+ public modalService: ModalService
) {
super();
this.resource = $localize`service`;
{
service_type: 'rgw'
},
- [
- Validators.required,
- CdValidators.custom('rgwPattern', (value: string) => {
- if (_.isEmpty(value)) {
- return false;
- }
- return !this.RGW_SVC_ID_PATTERN.test(value);
- })
- ]
+ [Validators.required]
),
CdValidators.custom('uniqueName', (service_id: string) => {
return this.serviceIds && this.serviceIds.includes(service_id);
],
// RGW
rgw_frontend_port: [null, [CdValidators.number(false)]],
+ realm_name: [null],
+ zonegroup_name: [null],
+ zone_name: [null],
// iSCSI
trusted_ip_list: [null],
api_port: [null, [CdValidators.number(false)]],
this.serviceForm
.get('rgw_frontend_port')
.setValue(response[0].spec?.rgw_frontend_port);
+ this.getServiceIds(
+ 'rgw',
+ response[0].spec?.rgw_realm,
+ response[0].spec?.rgw_zonegroup,
+ response[0].spec?.rgw_zone
+ );
this.serviceForm.get('ssl').setValue(response[0].spec?.ssl);
if (response[0].spec?.ssl) {
this.serviceForm
}
}
- getServiceIds(selectedServiceType: string) {
+ getDefaultsEntities(
+ defaultRealmId: string,
+ defaultZonegroupId: string,
+ defaultZoneId: string
+ ): { defaultRealmName: string; defaultZonegroupName: string; defaultZoneName: string } {
+ const defaultRealm = this.realmList.find((x: { id: string }) => x.id === defaultRealmId);
+ const defaultZonegroup = this.zonegroupList.find(
+ (x: { id: string }) => x.id === defaultZonegroupId
+ );
+ const defaultZone = this.zoneList.find((x: { id: string }) => x.id === defaultZoneId);
+ const defaultRealmName = defaultRealm !== undefined ? defaultRealm.name : null;
+ const defaultZonegroupName = defaultZonegroup !== undefined ? defaultZonegroup.name : 'default';
+ const defaultZoneName = defaultZone !== undefined ? defaultZone.name : 'default';
+ if (defaultZonegroupName === 'default' && !this.zonegroupNames.includes(defaultZonegroupName)) {
+ const defaultZonegroup = new RgwZonegroup();
+ defaultZonegroup.name = 'default';
+ this.zonegroupList.push(defaultZonegroup);
+ }
+ if (defaultZoneName === 'default' && !this.zoneNames.includes(defaultZoneName)) {
+ const defaultZone = new RgwZone();
+ defaultZone.name = 'default';
+ this.zoneList.push(defaultZone);
+ }
+ return {
+ defaultRealmName: defaultRealmName,
+ defaultZonegroupName: defaultZonegroupName,
+ defaultZoneName: defaultZoneName
+ };
+ }
+
+ getServiceIds(
+ selectedServiceType: string,
+ realm_name?: string,
+ zonegroup_name?: string,
+ zone_name?: string
+ ) {
this.serviceIds = this.serviceList
?.filter((service) => service['service_type'] === selectedServiceType)
.map((service) => service['service_id']);
+
+ if (selectedServiceType === 'rgw') {
+ const observables = [
+ this.rgwRealmService.getAllRealmsInfo(),
+ this.rgwZonegroupService.getAllZonegroupsInfo(),
+ this.rgwZoneService.getAllZonesInfo()
+ ];
+ this.sub = forkJoin(observables).subscribe(
+ (multisiteInfo: [object, object, object]) => {
+ this.multisiteInfo = multisiteInfo;
+ this.realmList =
+ this.multisiteInfo[0] !== undefined && this.multisiteInfo[0].hasOwnProperty('realms')
+ ? this.multisiteInfo[0]['realms']
+ : [];
+ this.zonegroupList =
+ this.multisiteInfo[1] !== undefined &&
+ this.multisiteInfo[1].hasOwnProperty('zonegroups')
+ ? this.multisiteInfo[1]['zonegroups']
+ : [];
+ this.zoneList =
+ this.multisiteInfo[2] !== undefined && this.multisiteInfo[2].hasOwnProperty('zones')
+ ? this.multisiteInfo[2]['zones']
+ : [];
+ this.realmNames = this.realmList.map((realm) => {
+ return realm['name'];
+ });
+ this.zonegroupNames = this.zonegroupList.map((zonegroup) => {
+ return zonegroup['name'];
+ });
+ this.zoneNames = this.zoneList.map((zone) => {
+ return zone['name'];
+ });
+ this.defaultRealmId = multisiteInfo[0]['default_realm'];
+ this.defaultZonegroupId = multisiteInfo[1]['default_zonegroup'];
+ this.defaultZoneId = multisiteInfo[2]['default_zone'];
+ this.defaultsInfo = this.getDefaultsEntities(
+ this.defaultRealmId,
+ this.defaultZonegroupId,
+ this.defaultZoneId
+ );
+ if (!this.editing) {
+ this.serviceForm.get('realm_name').setValue(this.defaultsInfo['defaultRealmName']);
+ this.serviceForm
+ .get('zonegroup_name')
+ .setValue(this.defaultsInfo['defaultZonegroupName']);
+ this.serviceForm.get('zone_name').setValue(this.defaultsInfo['defaultZoneName']);
+ } else {
+ if (realm_name && !this.realmNames.includes(realm_name)) {
+ const realm = new RgwRealm();
+ realm.name = realm_name;
+ this.realmList.push(realm);
+ }
+ if (zonegroup_name && !this.zonegroupNames.includes(zonegroup_name)) {
+ const zonegroup = new RgwZonegroup();
+ zonegroup.name = zonegroup_name;
+ this.zonegroupList.push(zonegroup);
+ }
+ if (zone_name && !this.zoneNames.includes(zone_name)) {
+ const zone = new RgwZone();
+ zone.name = zone_name;
+ this.zoneList.push(zone);
+ }
+ if (zonegroup_name === undefined && zone_name === undefined) {
+ zonegroup_name = 'default';
+ zone_name = 'default';
+ }
+ this.serviceForm.get('realm_name').setValue(realm_name);
+ this.serviceForm.get('zonegroup_name').setValue(zonegroup_name);
+ this.serviceForm.get('zone_name').setValue(zone_name);
+ }
+ if (this.realmList.length === 0) {
+ this.showRealmCreationForm = true;
+ } else {
+ this.showRealmCreationForm = false;
+ }
+ },
+ (_error) => {
+ const defaultZone = new RgwZone();
+ defaultZone.name = 'default';
+ const defaultZonegroup = new RgwZonegroup();
+ defaultZonegroup.name = 'default';
+ this.zoneList.push(defaultZone);
+ this.zonegroupList.push(defaultZonegroup);
+ }
+ );
+ }
}
disableForEditing(serviceType: string) {
};
let svcId: string;
if (serviceType === 'rgw') {
- const svcIdMatch = values['service_id'].match(this.RGW_SVC_ID_PATTERN);
- svcId = svcIdMatch[1];
- if (svcIdMatch[3]) {
- serviceSpec['rgw_realm'] = svcIdMatch[3];
- serviceSpec['rgw_zone'] = svcIdMatch[4];
- }
+ serviceSpec['rgw_realm'] = values['realm_name'] ? values['realm_name'] : null;
+ serviceSpec['rgw_zonegroup'] =
+ values['zonegroup_name'] !== 'default' ? values['zonegroup_name'] : null;
+ serviceSpec['rgw_zone'] = values['zone_name'] !== 'default' ? values['zone_name'] : null;
+ svcId = values['service_id'];
} else {
svcId = values['service_id'];
}
this.serviceForm.get('snmp_v3_priv_password').clearValidators();
}
}
+
+ createMultisiteSetup() {
+ this.bsModalRef = this.modalService.show(CreateRgwServiceEntitiesComponent, {
+ size: 'lg'
+ });
+ this.bsModalRef.componentInstance.submitAction.subscribe(() => {
+ this.getServiceIds('rgw');
+ });
+ }
}
icon: Icons.add,
click: () => this.openModal(),
name: this.actionLabels.CREATE,
- canBePrimary: (selection: CdTableSelection) => !selection.hasSelection,
- disable: (selection: CdTableSelection) => this.getDisable('create', selection)
+ canBePrimary: (selection: CdTableSelection) => !selection.hasSelection
+ // disable: (selection: CdTableSelection) => this.getDisable('create', selection)
},
{
permission: 'update',
--- /dev/null
+<cd-modal [modalRef]="activeModal">
+ <ng-container i18n="form title"
+ class="modal-title">Create Realm/Zonegroup/Zone
+ </ng-container>
+
+ <ng-container class="modal-content">
+ <form name="createMultisiteEntitiesForm"
+ #formDir="ngForm"
+ [formGroup]="createMultisiteEntitiesForm"
+ novalidate>
+ <div class="modal-body">
+ <cd-alert-panel type="info"
+ spacingClass="mb-3">The realm/zonegroup/zone created will be set as default and master.
+ </cd-alert-panel>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="realmName"
+ i18n>Realm Name</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="Realm name..."
+ id="realmName"
+ name="realmName"
+ formControlName="realmName">
+ <span class="invalid-feedback"
+ *ngIf="createMultisiteEntitiesForm.showError('realmName', formDir, 'required')"
+ i18n>This field is required.</span>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zonegroupName"
+ i18n>Zonegroup Name</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="Zonegroup name..."
+ id="zonegroupName"
+ name="zonegroupName"
+ formControlName="zonegroupName">
+ <span class="invalid-feedback"
+ *ngIf="createMultisiteEntitiesForm.showError('zonegroupName', formDir, 'required')"
+ i18n>This field is required.</span>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zoneName"
+ i18n>Zone Name</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="Zone name..."
+ id="zoneName"
+ name="zoneName"
+ formControlName="zoneName">
+ <span class="invalid-feedback"
+ *ngIf="createMultisiteEntitiesForm.showError('zoneName', formDir, 'required')"
+ i18n>This field is required.</span>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="createMultisiteEntitiesForm"></cd-form-button-panel>
+ </div>
+ </form>
+ </ng-container>
+</cd-modal>
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { ToastrModule } from 'ngx-toastr';
+import { SharedModule } from '~/app/shared/shared.module';
+
+import { CreateRgwServiceEntitiesComponent } from './create-rgw-service-entities.component';
+
+describe('CreateRgwServiceEntitiesComponent', () => {
+ let component: CreateRgwServiceEntitiesComponent;
+ let fixture: ComponentFixture<CreateRgwServiceEntitiesComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ SharedModule,
+ ReactiveFormsModule,
+ RouterTestingModule,
+ HttpClientTestingModule,
+ ToastrModule.forRoot()
+ ],
+ providers: [NgbActiveModal],
+ declarations: [CreateRgwServiceEntitiesComponent]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CreateRgwServiceEntitiesComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, EventEmitter, Output } from '@angular/core';
+import { FormControl, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
+import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
+import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
+import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
+import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
+import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
+import { ModalService } from '~/app/shared/services/modal.service';
+import { NotificationService } from '~/app/shared/services/notification.service';
+import { RgwRealm, RgwZonegroup, RgwZone, SystemKey } from '../models/rgw-multisite';
+import { NotificationType } from '~/app/shared/enum/notification-type.enum';
+import { Subscription } from 'rxjs';
+
+@Component({
+ selector: 'cd-create-rgw-service-entities',
+ templateUrl: './create-rgw-service-entities.component.html',
+ styleUrls: ['./create-rgw-service-entities.component.scss']
+})
+export class CreateRgwServiceEntitiesComponent {
+ public sub = new Subscription();
+ createMultisiteEntitiesForm: CdFormGroup;
+ realm: RgwRealm;
+ zonegroup: RgwZonegroup;
+ zone: RgwZone;
+
+ @Output()
+ submitAction = new EventEmitter();
+
+ constructor(
+ public activeModal: NgbActiveModal,
+ public actionLabels: ActionLabelsI18n,
+ public rgwMultisiteService: RgwMultisiteService,
+ public rgwZoneService: RgwZoneService,
+ public notificationService: NotificationService,
+ public rgwZonegroupService: RgwZonegroupService,
+ public rgwRealmService: RgwRealmService,
+ public modalService: ModalService
+ ) {
+ this.createForm();
+ }
+
+ createForm() {
+ this.createMultisiteEntitiesForm = new CdFormGroup({
+ realmName: new FormControl(null, {
+ validators: [Validators.required]
+ }),
+ zonegroupName: new FormControl(null, {
+ validators: [Validators.required]
+ }),
+ zoneName: new FormControl(null, {
+ validators: [Validators.required]
+ })
+ });
+ }
+
+ submit() {
+ const values = this.createMultisiteEntitiesForm.value;
+ this.realm = new RgwRealm();
+ this.realm.name = values['realmName'];
+ this.zonegroup = new RgwZonegroup();
+ this.zonegroup.name = values['zonegroupName'];
+ this.zonegroup.endpoints = '';
+ this.zone = new RgwZone();
+ this.zone.name = values['zoneName'];
+ this.zone.endpoints = '';
+ this.zone.system_key = new SystemKey();
+ this.zone.system_key.access_key = '';
+ this.zone.system_key.secret_key = '';
+ this.rgwRealmService
+ .create(this.realm, true)
+ .toPromise()
+ .then(() => {
+ this.rgwZonegroupService
+ .create(this.realm, this.zonegroup, true, true)
+ .toPromise()
+ .then(() => {
+ this.rgwZoneService
+ .create(this.zone, this.zonegroup, true, true, this.zone.endpoints)
+ .toPromise()
+ .then(() => {
+ this.notificationService.show(
+ NotificationType.success,
+ $localize`Realm/Zonegroup/Zone created successfully`
+ );
+ this.submitAction.emit();
+ this.activeModal.close();
+ })
+ .catch(() => {
+ this.notificationService.show(
+ NotificationType.error,
+ $localize`Realm/Zonegroup/Zone creation failed`
+ );
+ });
+ });
+ });
+ }
+}
import { ToastrModule } from 'ngx-toastr';
import { SharedModule } from '~/app/shared/shared.module';
import { configureTestBed } from '~/testing/unit-test-helper';
+import { RgwZone } from '../rgw-multisite';
import { RgwMultisiteZoneDeletionFormComponent } from './rgw-multisite-zone-deletion-form.component';
beforeEach(() => {
fixture = TestBed.createComponent(RgwMultisiteZoneDeletionFormComponent);
component = fixture.componentInstance;
+ component.zone = new RgwZone();
fixture.detectChanges();
});
import { ToastrModule } from 'ngx-toastr';
import { SharedModule } from '~/app/shared/shared.module';
import { configureTestBed } from '~/testing/unit-test-helper';
+import { RgwZonegroup } from '../rgw-multisite';
import { RgwMultisiteZonegroupDeletionFormComponent } from './rgw-multisite-zonegroup-deletion-form.component';
beforeEach(() => {
fixture = TestBed.createComponent(RgwMultisiteZonegroupDeletionFormComponent);
component = fixture.componentInstance;
+ component.zonegroup = new RgwZonegroup();
fixture.detectChanges();
});
name: string;
api_name: string;
is_master: boolean;
- endpoints: string[];
+ endpoints: string;
hostnames: string[];
hostnames_s3website: string[];
master_zone: string;
user_swift_pool: string;
user_uid_pool: string;
otp_pool: string;
- system_key: object;
+ system_key: SystemKey;
placement_pools: any[];
realm_id: string;
notif_pool: string;
- endpoints: string[];
+ endpoints: string;
+}
+
+export class SystemKey {
+ access_key: string;
+ secret_key: string;
}
<div class="row">
<div class="col-sm-12 col-lg-12">
<div>
+ <cd-alert-panel *ngIf="!rgwModuleStatus"
+ type="info"
+ spacingClass="mb-3"
+ i18n>You need to enable the rgw module to access the import/export feature.
+ <a class="text-decoration-underline"
+ (click)="enableRgwModule()">
+ Enable RGW Module</a>
+ </cd-alert-panel>
<cd-table-actions class="btn-group mb-4 me-2"
[permission]="permission"
[selection]="selection"
[tableActions]="migrateTableAction">
</cd-table-actions>
</span>
+ <cd-table-actions class="btn-group mb-4 me-2"
+ [permission]="permission"
+ [btnColor]="'light'"
+ [selection]="selection"
+ [tableActions]="importAction">
+ </cd-table-actions>
+ <cd-table-actions class="btn-group mb-4 me-2"
+ [permission]="permission"
+ [btnColor]="'light'"
+ [selection]="selection"
+ [tableActions]="exportAction">
+ </cd-table-actions>
</div>
<div class="card">
<div class="card-header"
let-node>
<span *ngIf="node.data.name"
class="me-3">
+ <span *ngIf="(node.data.show_warning)">
+ <i class="text-danger"
+ i18n-title
+ [title]="node.data.warning_message"
+ [ngClass]="icons.danger"></i>
+ </span>
<i [ngClass]="node.data.icon"></i>
{{ node.data.name }}
</span>
*ngIf="node.data.is_master">
master
</span>
+ <span class="badge badge-warning me-2"
+ *ngIf="node.data.secondary_zone">
+ secondary-zone
+ </span>
<div class="btn-group align-inline-btns"
*ngIf="node.isFocused"
role="group">
<button type="button"
class="btn btn-light dropdown-toggle-split ms-1"
(click)="openModal(node, true)"
- [disabled]="getDisable()">
+ [disabled]="getDisable() || node.data.secondary_zone">
<i [ngClass]="[icons.edit]"></i>
</button>
</div>
i18n-title>
<button type="button"
class="btn btn-light ms-1"
- [disabled]="isDeleteDisabled(node)"
+ [disabled]="isDeleteDisabled(node) || node.data.secondary_zone"
(click)="delete(node)">
<i [ngClass]="[icons.destroy]"></i>
</button>
*ngIf="metadata">
<legend>{{ metadataTitle }}</legend>
<cd-table-key-value cdTableDetail
- [data]="metadata"
- [renderObjects]="true"
- [customCss]="customCss">
+ [data]="metadata">
</cd-table-key-value>
</div>
</div>
.btn:disabled {
pointer-events: none;
}
+
+cd-table-key-value {
+ ::ng-deep .table-scroller {
+ overflow: unset;
+ }
+}
import { SharedModule } from '~/app/shared/shared.module';
import { RgwMultisiteDetailsComponent } from './rgw-multisite-details.component';
+import { RouterTestingModule } from '@angular/router/testing';
describe('RgwMultisiteDetailsComponent', () => {
let component: RgwMultisiteDetailsComponent;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [RgwMultisiteDetailsComponent],
- imports: [HttpClientTestingModule, TreeModule, SharedModule, ToastrModule.forRoot()]
+ imports: [
+ HttpClientTestingModule,
+ TreeModule,
+ SharedModule,
+ ToastrModule.forRoot(),
+ RouterTestingModule
+ ]
}).compileComponents();
});
} from '@circlon/angular-tree-component';
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
-import { forkJoin, Subscription } from 'rxjs';
+
+import { forkJoin, Subscription, timer as observableTimer } from 'rxjs';
import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
import { RgwMultisiteMigrateComponent } from '../rgw-multisite-migrate/rgw-multisite-migrate.component';
import { RgwMultisiteZoneDeletionFormComponent } from '../models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component';
import { RgwMultisiteZonegroupDeletionFormComponent } from '../models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component';
+import { RgwMultisiteExportComponent } from '../rgw-multisite-export/rgw-multisite-export.component';
+import { RgwMultisiteImportComponent } from '../rgw-multisite-import/rgw-multisite-import.component';
import { RgwMultisiteRealmFormComponent } from '../rgw-multisite-realm-form/rgw-multisite-realm-form.component';
import { RgwMultisiteZoneFormComponent } from '../rgw-multisite-zone-form/rgw-multisite-zone-form.component';
import { RgwMultisiteZonegroupFormComponent } from '../rgw-multisite-zonegroup-form/rgw-multisite-zonegroup-form.component';
+import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
+import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
+import { BlockUI, NgBlockUI } from 'ng-block-ui';
+import { Router } from '@angular/router';
@Component({
selector: 'cd-rgw-multisite-details',
messages = {
noDefaultRealm: $localize`Please create a default realm first to enable this feature`,
- noMasterZone: $localize`Please create a master zone for each zonegroup to enable this feature`,
- disableMigrate: $localize`Deployment is already migrated to multi-site system.`
+ noMasterZone: $localize`Please create a master zone for each zonegroups to enable this feature`,
+ noRealmExists: $localize`No realm exists`,
+ disableExport: $localize`Please create master zonegroup and master zone for each of the realms`
};
+ @BlockUI()
+ blockUI: NgBlockUI;
+
icons = Icons;
permission: Permission;
selection = new CdTableSelection();
createTableActions: CdTableAction[];
migrateTableAction: CdTableAction[];
+ importAction: CdTableAction[];
+ exportAction: CdTableAction[];
loadingIndicator = true;
nodes: object[] = [];
treeOptions: ITreeOptions = {
showMigrateAction: boolean = false;
editTitle: string = 'Edit';
deleteTitle: string = 'Delete';
+ disableExport = true;
+ rgwModuleStatus: boolean;
+ rgwModuleData: string | any[] = [];
constructor(
private modalService: ModalService,
private authStorageService: AuthStorageService,
public actionLabels: ActionLabelsI18n,
public timerServiceVariable: TimerServiceInterval,
+ public router: Router,
public rgwRealmService: RgwRealmService,
public rgwZonegroupService: RgwZonegroupService,
public rgwZoneService: RgwZoneService,
+ public rgwDaemonService: RgwDaemonService,
+ public mgrModuleService: MgrModuleService,
private notificationService: NotificationService
) {
this.permission = this.authStorageService.getPermissions().rgw;
- const createRealmAction: CdTableAction = {
- permission: 'create',
- icon: Icons.add,
- name: this.actionLabels.CREATE + ' Realm',
- click: () => this.openModal('realm')
- };
- const createZonegroupAction: CdTableAction = {
- permission: 'create',
- icon: Icons.add,
- name: this.actionLabels.CREATE + ' Zonegroup',
- click: () => this.openModal('zonegroup'),
- disable: () => this.getDisable()
- };
- const createZoneAction: CdTableAction = {
- permission: 'create',
- icon: Icons.add,
- name: this.actionLabels.CREATE + ' Zone',
- click: () => this.openModal('zone')
- };
- const migrateMultsiteAction: CdTableAction = {
- permission: 'read',
- icon: Icons.exchange,
- name: this.actionLabels.MIGRATE,
- click: () => this.openMigrateModal()
- };
- this.createTableActions = [createRealmAction, createZonegroupAction, createZoneAction];
- this.migrateTableAction = [migrateMultsiteAction];
}
openModal(entity: any, edit = false) {
});
}
+ openImportModal() {
+ const initialState = {
+ multisiteInfo: this.multisiteInfo
+ };
+ this.bsModalRef = this.modalService.show(RgwMultisiteImportComponent, initialState, {
+ size: 'lg'
+ });
+ }
+
+ openExportModal() {
+ const initialState = {
+ defaultsInfo: this.defaultsInfo,
+ multisiteInfo: this.multisiteInfo
+ };
+ this.bsModalRef = this.modalService.show(RgwMultisiteExportComponent, initialState, {
+ size: 'lg'
+ });
+ }
+
+ getDisableExport() {
+ this.realms.forEach((realm: any) => {
+ this.zonegroups.forEach((zonegroup) => {
+ if (realm.id === zonegroup.realm_id) {
+ if (zonegroup.is_master && zonegroup.master_zone !== '') {
+ this.disableExport = false;
+ }
+ }
+ });
+ });
+ if (!this.rgwModuleStatus) {
+ return true;
+ }
+ if (this.realms.length < 1) {
+ return this.messages.noRealmExists;
+ } else if (this.disableExport) {
+ return this.messages.disableExport;
+ } else {
+ return false;
+ }
+ }
+
+ getDisableImport() {
+ if (!this.rgwModuleStatus) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
ngOnInit() {
+ const createRealmAction: CdTableAction = {
+ permission: 'create',
+ icon: Icons.add,
+ name: this.actionLabels.CREATE + ' Realm',
+ click: () => this.openModal('realm')
+ };
+ const createZonegroupAction: CdTableAction = {
+ permission: 'create',
+ icon: Icons.add,
+ name: this.actionLabels.CREATE + ' Zonegroup',
+ click: () => this.openModal('zonegroup'),
+ disable: () => this.getDisable()
+ };
+ const createZoneAction: CdTableAction = {
+ permission: 'create',
+ icon: Icons.add,
+ name: this.actionLabels.CREATE + ' Zone',
+ click: () => this.openModal('zone')
+ };
+ const migrateMultsiteAction: CdTableAction = {
+ permission: 'read',
+ icon: Icons.exchange,
+ name: this.actionLabels.MIGRATE,
+ click: () => this.openMigrateModal()
+ };
+ const importMultsiteAction: CdTableAction = {
+ permission: 'read',
+ icon: Icons.download,
+ name: this.actionLabels.IMPORT,
+ click: () => this.openImportModal(),
+ disable: () => this.getDisableImport()
+ };
+ const exportMultsiteAction: CdTableAction = {
+ permission: 'read',
+ icon: Icons.upload,
+ name: this.actionLabels.EXPORT,
+ click: () => this.openExportModal(),
+ disable: () => this.getDisableExport()
+ };
+ this.createTableActions = [createRealmAction, createZonegroupAction, createZoneAction];
+ this.migrateTableAction = [migrateMultsiteAction];
+ this.importAction = [importMultsiteAction];
+ this.exportAction = [exportMultsiteAction];
+
const observables = [
this.rgwRealmService.getAllRealmsInfo(),
this.rgwZonegroupService.getAllZonegroupsInfo(),
},
(_error) => {}
);
+ this.mgrModuleService.list().subscribe((moduleData: any) => {
+ this.rgwModuleData = moduleData.filter((module: object) => module['name'] === 'rgw');
+ if (this.rgwModuleData.length > 0) {
+ this.rgwModuleStatus = this.rgwModuleData[0].enabled;
+ }
+ });
}
+ /* setConfigValues() {
+ this.rgwDaemonService
+ .setMultisiteConfig(
+ this.defaultsInfo['defaultRealmName'],
+ this.defaultsInfo['defaultZonegroupName'],
+ this.defaultsInfo['defaultZoneName']
+ )
+ .subscribe(() => {});
+ }*/
+
ngOnDestroy() {
this.sub.unsubscribe();
}
const zoneResult = this.rgwZoneService.getZoneTree(
zone,
this.defaultZoneId,
+ this.zones,
zonegroup,
realm
);
if (!this.realmIds.includes(zonegroup.realm_id)) {
rootNodes = this.rgwZonegroupService.getZonegroupTree(zonegroup, this.defaultZonegroupId);
for (const zone of zonegroup.zones) {
- const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId, zonegroup);
+ const zoneResult = this.rgwZoneService.getZoneTree(
+ zone,
+ this.defaultZoneId,
+ this.zones,
+ zonegroup
+ );
firstChildNodes = zoneResult['nodes'];
this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
allFirstChildNodes.push(firstChildNodes);
// get tree for standalone zones(zones that do not belong to a zonegroup)
for (const zone of this.zones) {
if (this.zoneIds.length > 0 && !this.zoneIds.includes(zone.id)) {
- const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId);
+ const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId, this.zones);
rootNodes = zoneResult['nodes'];
allNodes.push(rootNodes);
rootNodes = {};
});
}
}
+
+ enableRgwModule() {
+ let $obs;
+ const fnWaitUntilReconnected = () => {
+ observableTimer(2000).subscribe(() => {
+ // Trigger an API request to check if the connection is
+ // re-established.
+ this.mgrModuleService.list().subscribe(
+ () => {
+ // Resume showing the notification toasties.
+ this.notificationService.suspendToasties(false);
+ // Unblock the whole UI.
+ this.blockUI.stop();
+ // Reload the data table content.
+ this.notificationService.show(NotificationType.success, $localize`Enabled RGW Module`);
+ this.router.navigateByUrl('/', { skipLocationChange: true }).then(() => {
+ this.router.navigate(['/rgw/multisite']);
+ });
+ // Reload the data table content.
+ },
+ () => {
+ fnWaitUntilReconnected();
+ }
+ );
+ });
+ };
+
+ if (!this.rgwModuleStatus) {
+ $obs = this.mgrModuleService.enable('rgw');
+ }
+ $obs.subscribe(
+ () => undefined,
+ () => {
+ // Suspend showing the notification toasties.
+ this.notificationService.suspendToasties(true);
+ // Block the whole UI to prevent user interactions until
+ // the connection to the backend is reestablished
+ this.blockUI.start($localize`Reconnecting, please wait ...`);
+ fnWaitUntilReconnected();
+ }
+ );
+ }
}
--- /dev/null
+<cd-modal [modalRef]="activeModal">
+ <ng-container i18n="form title"
+ class="modal-title">Export Multi-site Realm Token</ng-container>
+
+ <ng-container class="modal-content">
+ <form name="exportTokenForm"
+ #frm="ngForm"
+ [formGroup]="exportTokenForm">
+ <span *ngIf="loading"
+ class="d-flex justify-content-center">
+ <i [ngClass]="[icons.large3x, icons.spinner, icons.spin]"></i></span>
+ <div class="modal-body"
+ *ngIf="!loading">
+ <cd-alert-panel *ngIf="!tokenValid"
+ type="warning"
+ class="mx-3"
+ i18n>
+ <div *ngFor="let realminfo of realms">
+ <b>{{realminfo.realm}}</b> -
+ {{realminfo.token}}
+ </div>
+ </cd-alert-panel>
+ <div *ngFor="let realminfo of realms">
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="realmName"
+ i18n>Realm Name
+ </label>
+ <div class="cd-col-form-input">
+ <input id="realmName"
+ name="realmName"
+ type="text"
+ value="{{ realminfo.realm }}"
+ readonly>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="token"
+ i18n>Token
+ </label>
+ <div class="cd-col-form-input">
+ <input id="realmToken"
+ name="realmToken"
+ type="text"
+ value="{{ realminfo.token }}"
+ class="me-2 mb-4"
+ readonly>
+ <cd-copy-2-clipboard-button
+ source="{{ realminfo.token }}"
+ [byId]="false">
+ </cd-copy-2-clipboard-button>
+ </div>
+ <hr *ngIf="realms.length > 1">
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <cd-back-button class="m-2 float-end"
+ aria-label="Close"
+ (backAction)="activeModal.close()"></cd-back-button>
+ </div>
+ </form>
+ </ng-container>
+</cd-modal>
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { ToastrModule } from 'ngx-toastr';
+import { SharedModule } from '~/app/shared/shared.module';
+
+import { RgwMultisiteExportComponent } from './rgw-multisite-export.component';
+
+describe('RgwMultisiteExportComponent', () => {
+ let component: RgwMultisiteExportComponent;
+ let fixture: ComponentFixture<RgwMultisiteExportComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ SharedModule,
+ ReactiveFormsModule,
+ RouterTestingModule,
+ HttpClientTestingModule,
+ ToastrModule.forRoot()
+ ],
+ declarations: [RgwMultisiteExportComponent],
+ providers: [NgbActiveModal]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RgwMultisiteExportComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { AfterViewChecked, ChangeDetectorRef, Component, OnInit } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
+import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
+import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
+import { NotificationService } from '~/app/shared/services/notification.service';
+import { RgwRealm } from '../models/rgw-multisite';
+import { Icons } from '~/app/shared/enum/icons.enum';
+
+@Component({
+ selector: 'cd-rgw-multisite-export',
+ templateUrl: './rgw-multisite-export.component.html',
+ styleUrls: ['./rgw-multisite-export.component.scss']
+})
+export class RgwMultisiteExportComponent implements OnInit, AfterViewChecked {
+ exportTokenForm: CdFormGroup;
+ realms: any;
+ realmList: RgwRealm[];
+ multisiteInfo: any;
+ tokenValid = false;
+ loading = true;
+ icons = Icons;
+
+ constructor(
+ public activeModal: NgbActiveModal,
+ public rgwRealmService: RgwRealmService,
+ public actionLabels: ActionLabelsI18n,
+ public notificationService: NotificationService,
+ private readonly changeDetectorRef: ChangeDetectorRef
+ ) {
+ this.createForm();
+ }
+
+ createForm() {
+ this.exportTokenForm = new CdFormGroup({});
+ }
+
+ onSubmit() {
+ this.activeModal.close();
+ }
+
+ ngOnInit(): void {
+ this.rgwRealmService.getRealmTokens().subscribe((data: object[]) => {
+ this.loading = false;
+ this.realms = data;
+ var base64Matcher = new RegExp(
+ '^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$'
+ );
+ this.realms.forEach((realmInfo: any) => {
+ if (base64Matcher.test(realmInfo.token)) {
+ this.tokenValid = true;
+ } else {
+ this.tokenValid = false;
+ }
+ });
+ });
+ }
+
+ ngAfterViewChecked(): void {
+ this.changeDetectorRef.detectChanges();
+ }
+}
--- /dev/null
+<cd-modal [modalRef]="activeModal">
+ <ng-container i18n="form title"
+ class="modal-title">Import Multi-site Token</ng-container>
+
+ <ng-container class="modal-content">
+ <form name="importTokenForm"
+ #frm="ngForm"
+ [formGroup]="importTokenForm">
+ <div class="modal-body">
+ <cd-alert-panel type="info"
+ spacingClass="mb-3">Please create a rgw service using the secondary zone(created after submitting this form) to start the replication between zones.
+ </cd-alert-panel>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="realmToken"
+ i18n>Token
+ </label>
+ <div class="cd-col-form-input">
+ <input id="realmToken"
+ name="realmToken"
+ class="form-control"
+ type="text"
+ formControlName="realmToken">
+ <span class="invalid-feedback"
+ *ngIf="importTokenForm.showError('realmToken', frm, 'required')"
+ i18n>This field is required.</span>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zoneName"
+ i18n>Secondary Zone Name</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="Zone name..."
+ id="zoneName"
+ name="zoneName"
+ formControlName="zoneName">
+ <span class="invalid-feedback"
+ *ngIf="importTokenForm.showError('zoneName', frm, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="importTokenForm.showError('zoneName', frm, 'uniqueName')"
+ i18n>The chosen zone name is already in use.</span>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <cd-form-button-panel (submitActionEvent)="onSubmit()"
+ [submitText]="actionLabels.IMPORT"
+ [form]="importTokenForm"></cd-form-button-panel>
+ </div>
+ </form>
+ </ng-container>
+</cd-modal>
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { ToastrModule } from 'ngx-toastr';
+import { SharedModule } from '~/app/shared/shared.module';
+
+import { RgwMultisiteImportComponent } from './rgw-multisite-import.component';
+
+describe('RgwMultisiteImportComponent', () => {
+ let component: RgwMultisiteImportComponent;
+ let fixture: ComponentFixture<RgwMultisiteImportComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ SharedModule,
+ ReactiveFormsModule,
+ RouterTestingModule,
+ HttpClientTestingModule,
+ ToastrModule.forRoot()
+ ],
+ declarations: [RgwMultisiteImportComponent],
+ providers: [NgbActiveModal]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RgwMultisiteImportComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, OnInit } from '@angular/core';
+import { FormControl, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
+import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
+import { NotificationType } from '~/app/shared/enum/notification-type.enum';
+import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
+import { CdValidators } from '~/app/shared/forms/cd-validators';
+import { NotificationService } from '~/app/shared/services/notification.service';
+import { RgwZone } from '../models/rgw-multisite';
+import _ from 'lodash';
+
+@Component({
+ selector: 'cd-rgw-multisite-import',
+ templateUrl: './rgw-multisite-import.component.html',
+ styleUrls: ['./rgw-multisite-import.component.scss']
+})
+export class RgwMultisiteImportComponent implements OnInit {
+ readonly endpoints = /^((https?:\/\/)|(www.))(?:([a-zA-Z]+)|(\d+\.\d+.\d+.\d+)):\d{2,4}$/;
+ readonly ipv4Rgx = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i;
+ readonly ipv6Rgx = /^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$/i;
+
+ importTokenForm: CdFormGroup;
+ multisiteInfo: object[] = [];
+ zoneList: RgwZone[] = [];
+ zoneNames: string[];
+
+ constructor(
+ public activeModal: NgbActiveModal,
+ public rgwRealmService: RgwRealmService,
+ public actionLabels: ActionLabelsI18n,
+ public notificationService: NotificationService
+ ) {
+ this.createForm();
+ }
+ ngOnInit(): void {
+ this.zoneList =
+ this.multisiteInfo[2] !== undefined && this.multisiteInfo[2].hasOwnProperty('zones')
+ ? this.multisiteInfo[2]['zones']
+ : [];
+ this.zoneNames = this.zoneList.map((zone) => {
+ return zone['name'];
+ });
+ }
+
+ createForm() {
+ this.importTokenForm = new CdFormGroup({
+ realmToken: new FormControl('', {
+ validators: [Validators.required]
+ }),
+ zoneName: new FormControl(null, {
+ validators: [
+ Validators.required,
+ CdValidators.custom('uniqueName', (zoneName: string) => {
+ return this.zoneNames && this.zoneNames.indexOf(zoneName) !== -1;
+ })
+ ]
+ })
+ });
+ }
+
+ onSubmit() {
+ const values = this.importTokenForm.value;
+ this.rgwRealmService.importRealmToken(values['realmToken'], values['zoneName']).subscribe(
+ () => {
+ this.notificationService.show(
+ NotificationType.success,
+ $localize`Realm token import successfull`
+ );
+ this.activeModal.close();
+ },
+ () => {
+ this.importTokenForm.setErrors({ cdSubmitButton: true });
+ }
+ );
+ }
+}
</div>
</div>
<div class="form-group row">
- <label class="cd-col-form-label"
- for="users"
- i18n>System User</label>
+ <label class="cd-col-form-label required"
+ for="access_key"
+ i18n>Access key</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="e.g."
+ id="access_key"
+ name="access_key"
+ formControlName="access_key">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="access_key"
+ i18n>Secret key</label>
<div class="cd-col-form-input">
- <select id="users"
- name="users"
- class="form-select"
- formControlName="users">
- <option i18n
- *ngIf="users === null"
- [ngValue]="null">Loading...</option>
- <option i18n
- *ngIf="users !== null"
- [ngValue]="null">-- Select a user --</option>
- <option *ngFor="let user of users"
- [value]="user.user_id">{{ user.user_id }}</option>
- </select>
+ <input class="form-control"
+ type="text"
+ placeholder="e.g."
+ id="secret_key"
+ name="secret_key"
+ formControlName="secret_key">
</div>
</div>
</div>
<div class="modal-footer">
<cd-form-button-panel (submitActionEvent)="submit()"
+ [submitText]="actionLabels.MIGRATE"
[form]="multisiteMigrateForm"></cd-form-button-panel>
</div>
</form>
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { CdValidators } from '~/app/shared/forms/cd-validators';
import { NotificationService } from '~/app/shared/services/notification.service';
-import { RgwRealm, RgwZone, RgwZonegroup } from '../models/rgw-multisite';
+import { RgwRealm, RgwZone, RgwZonegroup, SystemKey } from '../models/rgw-multisite';
import { ModalService } from '~/app/shared/services/modal.service';
+import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
@Component({
selector: 'cd-rgw-multisite-migrate',
public notificationService: NotificationService,
public rgwZonegroupService: RgwZonegroupService,
public rgwRealmService: RgwRealmService,
+ public rgwDaemonService: RgwDaemonService,
public modalService: ModalService
) {
this.createForm();
Validators.required
]
),
- users: new FormControl(null)
+ access_key: new FormControl(null),
+ secret_key: new FormControl(null)
});
}
this.zoneNames = this.zoneList.map((zone) => {
return zone['name'];
});
- this.rgwZoneService.getUserList('default').subscribe((users: any) => {
- this.users = users.filter((user: any) => user['system'] === true);
- });
}
submit() {
this.realm.name = values['realmName'];
this.zonegroup = new RgwZonegroup();
this.zonegroup.name = values['zonegroupName'];
- this.zonegroup.endpoints = this.checkUrlArray(values['zonegroup_endpoints']);
+ this.zonegroup.endpoints = values['zonegroup_endpoints'];
this.zone = new RgwZone();
this.zone.name = values['zoneName'];
- this.zone.endpoints = this.checkUrlArray(values['zone_endpoints']);
- const user = values['users'];
- this.rgwMultisiteService.migrate(this.realm, this.zonegroup, this.zone, user).subscribe(
+ this.zone.endpoints = values['zone_endpoints'];
+ this.zone.system_key = new SystemKey();
+ this.zone.system_key.access_key = values['access_key'];
+ this.zone.system_key.secret_key = values['secret_key'];
+ this.rgwMultisiteService.migrate(this.realm, this.zonegroup, this.zone).subscribe(
() => {
this.notificationService.show(
NotificationType.success,
$localize`${this.actionLabels.MIGRATE} done successfully`
);
+ this.notificationService.show(NotificationType.success, `Daemon restart scheduled`);
this.submitAction.emit();
this.activeModal.close();
},
}
);
}
-
- checkUrlArray(endpoints: string) {
- let endpointsArray = [];
- if (endpoints.includes(',')) {
- endpointsArray = endpoints.split(',');
- } else {
- endpointsArray.push(endpoints);
- }
- return endpointsArray;
- }
}
i18n>Please enter a valid IP address.</span>
</div>
</div>
- <div class="form-group row"
- *ngIf="action === 'edit'">
- <label class="cd-col-form-label"
- for="users"
- i18n>System User</label>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="access_key"
+ i18n>Access key</label>
<div class="cd-col-form-input">
- <select id="users"
- name="users"
- class="form-select"
- formControlName="users">
- <option i18n
- *ngIf="users === null"
- [ngValue]="null">Loading...</option>
- <option i18n
- *ngIf="users !== null"
- [ngValue]="null">-- Select a user --</option>
- <option *ngFor="let user of users"
- [value]="user.user_id">{{ user.user_id }}</option>
- </select><br><br>
- <div *ngIf="info.data.zone_zonegroup.is_master && info.data.is_master">
- <button type="button"
- class="btn btn-light"
- (click)="CreateSystemUser()">
- Create System User
- </button>
- </div>
+ <input class="form-control"
+ type="text"
+ placeholder="DiPt4V7WWvy2njL1z6aC"
+ id="access_key"
+ name="access_key"
+ formControlName="access_key">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="access_key"
+ i18n>Secret key</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="xSZUdYky0bTctAdCEEW8ikhfBVKsBV5LFYL82vvh"
+ id="secret_key"
+ name="secret_key"
+ formControlName="secret_key">
</div>
+ </div>
+ <div class="form-group row"
+ *ngIf="action === 'edit'">
<div *ngIf="action === 'edit'">
<legend>Placement Targets</legend>
<div class="form-group row">
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { CdValidators } from '~/app/shared/forms/cd-validators';
import { NotificationService } from '~/app/shared/services/notification.service';
-import { RgwRealm, RgwZone, RgwZonegroup } from '../models/rgw-multisite';
+import { RgwRealm, RgwZone, RgwZonegroup, SystemKey } from '../models/rgw-multisite';
import { ModalService } from '~/app/shared/services/modal.service';
-import { RgwSystemUserComponent } from '../rgw-system-user/rgw-system-user.component';
@Component({
selector: 'cd-rgw-multisite-zone-form',
access_key: any;
master_zonegroup_of_realm: RgwZonegroup;
compressionTypes = ['lz4', 'zlib', 'snappy'];
+ userListReady: boolean = false;
constructor(
public activeModal: NgbActiveModal,
default_zone: new FormControl(false),
master_zone: new FormControl(false),
selectedZonegroup: new FormControl(null),
- zone_endpoints: new FormControl([], {
+ zone_endpoints: new FormControl(null, {
validators: [
CdValidators.custom('endpoint', (value: string) => {
if (_.isEmpty(value)) {
Validators.required
]
}),
- users: new FormControl(null),
+ access_key: new FormControl(null),
+ secret_key: new FormControl(null),
placementTarget: new FormControl(null),
placementDataPool: new FormControl(''),
placementIndexPool: new FormControl(null),
this.multisiteZoneForm.get('master_zone').disable();
this.disableMaster = true;
}
- const zonegroupInfo = this.zonegroupList.filter((zgroup: any) => zgroup.name === zg.name)[0];
- if (zonegroupInfo) {
- const realm_id = zonegroupInfo.realm_id;
- this.master_zonegroup_of_realm = this.zonegroupList.filter(
- (zg: any) => zg.realm_id === realm_id && zg.is_master === true
- )[0];
- }
- if (this.master_zonegroup_of_realm) {
- this.master_zone_of_master_zonegroup = this.zoneList.filter(
- (zone: any) => zone.id === this.master_zonegroup_of_realm.master_zone
- )[0];
- }
- if (this.master_zone_of_master_zonegroup) {
- this.getUserInfo(this.master_zone_of_master_zonegroup);
- }
- if (zonegroupInfo.is_master && this.multisiteZoneForm.getValue('master_zone') === true) {
- this.createSystemUser = true;
- }
});
if (
this.multisiteZoneForm.getValue('selectedZonegroup') !==
this.multisiteZoneForm.get('selectedZonegroup').setValue(this.info.data.parent);
this.multisiteZoneForm.get('default_zone').setValue(this.info.data.is_default);
this.multisiteZoneForm.get('master_zone').setValue(this.info.data.is_master);
- this.multisiteZoneForm.get('zone_endpoints').setValue(this.info.data.endpoints);
+ this.multisiteZoneForm.get('zone_endpoints').setValue(this.info.data.endpoints.toString());
+ this.multisiteZoneForm.get('access_key').setValue(this.info.data.access_key);
+ this.multisiteZoneForm.get('secret_key').setValue(this.info.data.secret_key);
this.multisiteZoneForm
.get('placementTarget')
.setValue(this.info.parent.data.default_placement);
const zone = new RgwZone();
zone.name = this.info.data.name;
this.onZoneGroupChange(this.info.data.parent);
- setTimeout(() => {
- this.getUserInfo(zone);
- }, 1000);
}
if (
this.multisiteZoneForm.getValue('selectedZonegroup') !==
}
}
- getUserInfo(zone: RgwZone) {
- this.rgwZoneService
- .getUserList(this.master_zone_of_master_zonegroup.name)
- .subscribe((users: any) => {
- this.users = users.filter((user: any) => user.keys.length !== 0);
- this.rgwZoneService.get(zone).subscribe((zone: RgwZone) => {
- const access_key = zone.system_key['access_key'];
- const user = this.users.filter((user: any) => user.keys[0].access_key === access_key);
- if (user.length > 0) {
- this.multisiteZoneForm.get('users').setValue(user[0].user_id);
- }
- return user[0].user_id;
- });
- });
- }
-
getZonePlacementData(placementTarget: string) {
this.zone = new RgwZone();
this.zone.name = this.info.data.name;
this.zonegroup.name = values['selectedZonegroup'];
this.zone = new RgwZone();
this.zone.name = values['zoneName'];
- this.zone.endpoints = this.checkUrlArray(values['zone_endpoints']);
- if (this.createSystemUser) {
- values['users'] = values['zoneName'] + '_User';
- }
+ this.zone.endpoints = values['zone_endpoints'];
+ this.zone.system_key = new SystemKey();
+ this.zone.system_key.access_key = values['access_key'];
+ this.zone.system_key.secret_key = values['secret_key'];
this.rgwZoneService
.create(
this.zone,
this.zonegroup,
values['default_zone'],
values['master_zone'],
- this.zone.endpoints,
- values['users'],
- this.createSystemUser,
- this.master_zone_of_master_zonegroup
+ this.zone.endpoints
)
.subscribe(
() => {
this.zonegroup.name = values['selectedZonegroup'];
this.zone = new RgwZone();
this.zone.name = this.info.data.name;
- this.zone.endpoints =
- values['zone_endpoints'] === this.info.data.endpoints
- ? values['zone_endpoints']
- : this.checkUrlArray(values['zone_endpoints']);
+ this.zone.endpoints = values['zone_endpoints'];
+ this.zone.system_key = new SystemKey();
+ this.zone.system_key.access_key = values['access_key'];
+ this.zone.system_key.secret_key = values['secret_key'];
this.rgwZoneService
.update(
this.zone,
values['default_zone'],
values['master_zone'],
this.zone.endpoints,
- values['users'],
values['placementTarget'],
values['placementDataPool'],
values['placementIndexPool'],
values['placementDataExtraPool'],
values['storageClass'],
values['storageDataPool'],
- values['storageCompression'],
- this.master_zone_of_master_zonegroup
+ values['storageCompression']
)
.subscribe(
() => {
}
return endpointsArray;
}
-
- CreateSystemUser() {
- const initialState = {
- zoneName: this.master_zone_of_master_zonegroup.name
- };
- this.bsModalRef = this.modalService.show(RgwSystemUserComponent, initialState);
- this.bsModalRef.componentInstance.submitAction.subscribe(() => {
- this.getUserInfo(this.master_zone_of_master_zonegroup);
- });
- }
}
this.realm.name = values['selectedRealm'];
this.zonegroup = new RgwZonegroup();
this.zonegroup.name = values['zonegroupName'];
- this.zonegroup.endpoints = this.checkUrlArray(values['zonegroup_endpoints']);
+ this.zonegroup.endpoints = values['zonegroup_endpoints'];
this.rgwZonegroupService
.create(this.realm, this.zonegroup, values['default_zonegroup'], values['master_zonegroup'])
.subscribe(
this.zonegroup = new RgwZonegroup();
this.zonegroup.name = this.info.data.name;
this.newZonegroupName = values['zonegroupName'];
- this.zonegroup.endpoints =
- values['zonegroup_endpoints'] === this.info.data.endpoints
- ? values['zonegroup_endpoints']
- : this.checkUrlArray(values['zonegroup_endpoints']);
+ this.zonegroup.endpoints = values['zonegroup_endpoints'];
this.zonegroup.placement_targets = values['placementTargets'];
this.rgwZonegroupService
.update(
import { RgwMultisiteZonegroupDeletionFormComponent } from './models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component';
import { RgwSystemUserComponent } from './rgw-system-user/rgw-system-user.component';
import { RgwMultisiteMigrateComponent } from './rgw-multisite-migrate/rgw-multisite-migrate.component';
+import { RgwMultisiteImportComponent } from './rgw-multisite-import/rgw-multisite-import.component';
+import { RgwMultisiteExportComponent } from './rgw-multisite-export/rgw-multisite-export.component';
+import { CreateRgwServiceEntitiesComponent } from './create-rgw-service-entities/create-rgw-service-entities.component';
@NgModule({
imports: [
RgwMultisiteZoneDeletionFormComponent,
RgwMultisiteZonegroupDeletionFormComponent,
RgwSystemUserComponent,
- RgwMultisiteMigrateComponent
+ RgwMultisiteMigrateComponent,
+ RgwMultisiteImportComponent,
+ RgwMultisiteExportComponent,
+ CreateRgwServiceEntitiesComponent
]
})
export class RgwModule {}
})
);
}
+
+ setMultisiteConfig(realm_name: string, zonegroup_name: string, zone_name: string) {
+ return this.request((params: HttpParams) => {
+ params = params.appendAll({
+ realm_name: realm_name,
+ zonegroup_name: zonegroup_name,
+ zone_name: zone_name
+ });
+ return this.http.put(`${this.url}/set_multisite_config`, null, { params: params });
+ });
+ }
}
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
-import { RgwDaemonService } from './rgw-daemon.service';
import { RgwRealm, RgwZone, RgwZonegroup } from '~/app/ceph/rgw/models/rgw-multisite';
+import { RgwDaemonService } from './rgw-daemon.service';
@Injectable({
providedIn: 'root'
export class RgwMultisiteService {
private url = 'ui-api/rgw/multisite';
- constructor(private http: HttpClient, private rgwDaemonService: RgwDaemonService) {}
-
- getMultisiteSyncStatus() {
- return this.rgwDaemonService.request(() => {
- return this.http.get(`${this.url}/sync_status`);
- });
- }
+ constructor(private http: HttpClient, public rgwDaemonService: RgwDaemonService) {}
- migrate(realm: RgwRealm, zonegroup: RgwZonegroup, zone: RgwZone, user: string) {
- return this.rgwDaemonService.request((requestBody: any) => {
- requestBody = {
+ migrate(realm: RgwRealm, zonegroup: RgwZonegroup, zone: RgwZone) {
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ params = params.appendAll({
realm_name: realm.name,
zonegroup_name: zonegroup.name,
zone_name: zone.name,
zonegroup_endpoints: zonegroup.endpoints,
zone_endpoints: zone.endpoints,
- user: user
- };
- return this.http.put(`${this.url}/migrate`, requestBody);
+ access_key: zone.system_key.access_key,
+ secret_key: zone.system_key.secret_key
+ });
+ return this.http.put(`${this.url}/migrate`, null, { params: params });
});
}
}
export class RgwRealmService {
private url = 'api/rgw/realm';
- constructor(private http: HttpClient, private rgwDaemonService: RgwDaemonService) {}
+ constructor(private http: HttpClient, public rgwDaemonService: RgwDaemonService) {}
create(realm: RgwRealm, defaultRealm: boolean) {
- return this.rgwDaemonService.request((params: HttpParams) => {
- params = params.appendAll({
- realm_name: realm.name,
- default: defaultRealm
- });
- return this.http.post(`${this.url}`, null, { params: params });
- });
+ let requestBody = {
+ realm_name: realm.name,
+ default: defaultRealm
+ };
+ return this.http.post(`${this.url}`, requestBody);
}
update(realm: RgwRealm, defaultRealm: boolean, newRealmName: string) {
- return this.rgwDaemonService.request((requestBody: any) => {
- requestBody = {
- realm_name: realm.name,
- default: defaultRealm,
- new_realm_name: newRealmName
- };
- return this.http.put(`${this.url}/${realm.name}`, requestBody);
- });
+ let requestBody = {
+ realm_name: realm.name,
+ default: defaultRealm,
+ new_realm_name: newRealmName
+ };
+ return this.http.put(`${this.url}/${realm.name}`, requestBody);
}
list(): Observable<object> {
- return this.rgwDaemonService.request(() => {
- return this.http.get<object>(`${this.url}`);
- });
+ return this.http.get<object>(`${this.url}`);
}
- get(realm: RgwRealm): Observable<RgwRealm> {
- return this.rgwDaemonService.request(() => {
- return this.http.get(`${this.url}/${realm.name}`);
- });
+ get(realm: RgwRealm): Observable<object> {
+ return this.http.get(`${this.url}/${realm.name}`);
}
getAllRealmsInfo(): Observable<object> {
- return this.rgwDaemonService.request(() => {
- return this.http.get(`${this.url}/get_all_realms_info`);
- });
+ return this.http.get(`${this.url}/get_all_realms_info`);
}
delete(realmName: string): Observable<any> {
- return this.rgwDaemonService.request((params: HttpParams) => {
- params = params.appendAll({
- realm_name: realmName
- });
- return this.http.delete(`${this.url}/${realmName}`, { params: params });
+ let params = new HttpParams();
+ params = params.appendAll({
+ realm_name: realmName
});
+ return this.http.delete(`${this.url}/${realmName}`, { params: params });
}
getRealmTree(realm: RgwRealm, defaultRealmId: string) {
realmIds: realmIds
};
}
+
+ importRealmToken(realm_token: string, zone_name: string) {
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ params = params.appendAll({
+ realm_token: realm_token,
+ zone_name: zone_name
+ });
+ return this.http.post(`${this.url}/import_realm_token`, null, { params: params });
+ });
+ }
+
+ getRealmTokens() {
+ return this.rgwDaemonService.request(() => {
+ return this.http.get(`${this.url}/get_realm_tokens`);
+ });
+ }
}
import { Observable } from 'rxjs';
import { RgwRealm, RgwZone, RgwZonegroup } from '~/app/ceph/rgw/models/rgw-multisite';
import { Icons } from '../enum/icons.enum';
-import { RgwDaemonService } from './rgw-daemon.service';
@Injectable({
providedIn: 'root'
export class RgwZoneService {
private url = 'api/rgw/zone';
- constructor(private http: HttpClient, private rgwDaemonService: RgwDaemonService) {}
+ constructor(private http: HttpClient) {}
create(
zone: RgwZone,
zonegroup: RgwZonegroup,
defaultZone: boolean,
master: boolean,
- endpoints: Array<string>,
- user: string,
- createSystemUser: boolean,
- master_zone_of_master_zonegroup: RgwZone
+ endpoints: string
) {
- let master_zone_name = '';
- if (master_zone_of_master_zonegroup !== undefined) {
- master_zone_name = master_zone_of_master_zonegroup.name;
- } else {
- master_zone_name = '';
- }
- return this.rgwDaemonService.request((params: HttpParams) => {
- params = params.appendAll({
- zone_name: zone.name,
- zonegroup_name: zonegroup.name,
- default: defaultZone,
- master: master,
- zone_endpoints: endpoints,
- user: user,
- createSystemUser: createSystemUser,
- master_zone_of_master_zonegroup: master_zone_name
- });
- return this.http.post(`${this.url}`, null, { params: params });
+ let params = new HttpParams();
+ params = params.appendAll({
+ zone_name: zone.name,
+ zonegroup_name: zonegroup.name,
+ default: defaultZone,
+ master: master,
+ zone_endpoints: endpoints,
+ access_key: zone.system_key.access_key,
+ secret_key: zone.system_key.secret_key
});
+ return this.http.post(`${this.url}`, null, { params: params });
}
list(): Observable<object> {
- return this.rgwDaemonService.request(() => {
- return this.http.get<object>(`${this.url}`);
- });
+ return this.http.get<object>(`${this.url}`);
}
- get(zone: RgwZone): Observable<RgwZone> {
- return this.rgwDaemonService.request(() => {
- return this.http.get(`${this.url}/${zone.name}`);
- });
+ get(zone: RgwZone): Observable<object> {
+ return this.http.get(`${this.url}/${zone.name}`);
}
getAllZonesInfo(): Observable<object> {
- return this.rgwDaemonService.request(() => {
- return this.http.get(`${this.url}/get_all_zones_info`);
- });
+ return this.http.get(`${this.url}/get_all_zones_info`);
}
delete(
pools: Set<string>,
zonegroupName: string
): Observable<any> {
- return this.rgwDaemonService.request((params: HttpParams) => {
- params = params.appendAll({
- zone_name: zoneName,
- delete_pools: deletePools,
- pools: Array.from(pools.values()),
- zonegroup_name: zonegroupName
- });
- return this.http.delete(`${this.url}/${zoneName}`, { params: params });
+ let params = new HttpParams();
+ params = params.appendAll({
+ zone_name: zoneName,
+ delete_pools: deletePools,
+ pools: Array.from(pools.values()),
+ zonegroup_name: zonegroupName
});
+ return this.http.delete(`${this.url}/${zoneName}`, { params: params });
}
update(
newZoneName: string,
defaultZone?: boolean,
master?: boolean,
- endpoints?: Array<string>,
- user?: string,
+ endpoints?: string,
placementTarget?: string,
dataPool?: string,
indexPool?: string,
dataExtraPool?: string,
storageClass?: string,
dataPoolClass?: string,
- compression?: string,
- master_zone_of_master_zonegroup?: RgwZone
+ compression?: string
) {
- let master_zone_name = '';
- if (master_zone_of_master_zonegroup !== undefined) {
- master_zone_name = master_zone_of_master_zonegroup.name;
- } else {
- master_zone_name = '';
- }
- return this.rgwDaemonService.request((requestBody: any) => {
- requestBody = {
- zone_name: zone.name,
- zonegroup_name: zonegroup.name,
- new_zone_name: newZoneName,
- default: defaultZone,
- master: master,
- zone_endpoints: endpoints,
- user: user,
- placement_target: placementTarget,
- data_pool: dataPool,
- index_pool: indexPool,
- data_extra_pool: dataExtraPool,
- storage_class: storageClass,
- data_pool_class: dataPoolClass,
- compression: compression,
- master_zone_of_master_zonegroup: master_zone_name
- };
- return this.http.put(`${this.url}/${zone.name}`, requestBody);
- });
+ let requestBody = {
+ zone_name: zone.name,
+ zonegroup_name: zonegroup.name,
+ new_zone_name: newZoneName,
+ default: defaultZone,
+ master: master,
+ zone_endpoints: endpoints,
+ access_key: zone.system_key.access_key,
+ secret_key: zone.system_key.secret_key,
+ placement_target: placementTarget,
+ data_pool: dataPool,
+ index_pool: indexPool,
+ data_extra_pool: dataExtraPool,
+ storage_class: storageClass,
+ data_pool_class: dataPoolClass,
+ compression: compression
+ };
+ return this.http.put(`${this.url}/${zone.name}`, requestBody);
}
- getZoneTree(zone: RgwZone, defaultZoneId: string, zonegroup?: RgwZonegroup, realm?: RgwRealm) {
+ getZoneTree(
+ zone: RgwZone,
+ defaultZoneId: string,
+ zones: RgwZone[],
+ zonegroup?: RgwZonegroup,
+ realm?: RgwRealm
+ ) {
let nodes = {};
let zoneIds = [];
nodes['id'] = zone.id;
nodes['endpoints'] = zone.endpoints;
nodes['is_master'] = zonegroup && zonegroup.master_zone === zone.id ? true : false;
nodes['type'] = 'zone';
+ const zoneNames = zones.map((zone: RgwZone) => {
+ return zone['name'];
+ });
+ nodes['secondary_zone'] = !zoneNames.includes(zone.name) ? true : false;
+ const zoneInfo = zones.filter((zoneInfo) => zoneInfo.name === zone.name);
+ if (zoneInfo && zoneInfo.length > 0) {
+ const access_key = zoneInfo[0].system_key['access_key'];
+ const secret_key = zoneInfo[0].system_key['secret_key'];
+ nodes['access_key'] = access_key ? access_key : '';
+ nodes['secret_key'] = secret_key ? secret_key : '';
+ nodes['user'] = access_key && access_key !== '' ? true : false;
+ }
+ if (nodes['access_key'] === '' || nodes['access_key'] === 'null') {
+ nodes['show_warning'] = true;
+ nodes['warning_message'] = 'Access/Secret keys not found';
+ } else {
+ nodes['show_warning'] = false;
+ }
+ if (nodes['endpoints'] && nodes['endpoints'].length === 0) {
+ nodes['show_warning'] = true;
+ nodes['warning_message'] = nodes['warning_message'] + '\n' + 'Endpoints not configured';
+ }
return {
nodes: nodes,
zoneIds: zoneIds
}
getPoolNames() {
- return this.rgwDaemonService.request(() => {
- return this.http.get(`${this.url}/get_pool_names`);
- });
+ return this.http.get(`${this.url}/get_pool_names`);
}
createSystemUser(userName: string, zone: string) {
- return this.rgwDaemonService.request((requestBody: any) => {
- requestBody = {
- userName: userName,
- zoneName: zone
- };
- return this.http.put(`${this.url}/create_system_user`, requestBody);
- });
+ let requestBody = {
+ userName: userName,
+ zoneName: zone
+ };
+ return this.http.put(`${this.url}/create_system_user`, requestBody);
}
getUserList(zoneName: string) {
- return this.rgwDaemonService.request((params: HttpParams) => {
- params = params.appendAll({
- zoneName: zoneName
- });
- return this.http.get(`${this.url}/get_user_list`, { params: params });
+ let params = new HttpParams();
+ params = params.appendAll({
+ zoneName: zoneName
});
+ return this.http.get(`${this.url}/get_user_list`, { params: params });
}
}
import { Observable } from 'rxjs';
import { RgwRealm, RgwZonegroup } from '~/app/ceph/rgw/models/rgw-multisite';
import { Icons } from '../enum/icons.enum';
-import { RgwDaemonService } from './rgw-daemon.service';
@Injectable({
providedIn: 'root'
export class RgwZonegroupService {
private url = 'api/rgw/zonegroup';
- constructor(private http: HttpClient, private rgwDaemonService: RgwDaemonService) {}
+ constructor(private http: HttpClient) {}
create(realm: RgwRealm, zonegroup: RgwZonegroup, defaultZonegroup: boolean, master: boolean) {
- return this.rgwDaemonService.request((params: HttpParams) => {
- params = params.appendAll({
- realm_name: realm.name,
- zonegroup_name: zonegroup.name,
- default: defaultZonegroup,
- master: master,
- zonegroup_endpoints: zonegroup.endpoints
- });
- return this.http.post(`${this.url}`, null, { params: params });
+ let params = new HttpParams();
+ params = params.appendAll({
+ realm_name: realm.name,
+ zonegroup_name: zonegroup.name,
+ default: defaultZonegroup,
+ master: master,
+ zonegroup_endpoints: zonegroup.endpoints
});
+ return this.http.post(`${this.url}`, null, { params: params });
}
update(
removedZones?: string[],
addedZones?: string[]
) {
- return this.rgwDaemonService.request((requestBody: any) => {
- requestBody = {
- zonegroup_name: zonegroup.name,
- realm_name: realm.name,
- new_zonegroup_name: newZonegroupName,
- default: defaultZonegroup,
- master: master,
- zonegroup_endpoints: zonegroup.endpoints,
- placement_targets: zonegroup.placement_targets,
- remove_zones: removedZones,
- add_zones: addedZones
- };
- return this.http.put(`${this.url}/${zonegroup.name}`, requestBody);
- });
+ let requestBody = {
+ zonegroup_name: zonegroup.name,
+ realm_name: realm.name,
+ new_zonegroup_name: newZonegroupName,
+ default: defaultZonegroup,
+ master: master,
+ zonegroup_endpoints: zonegroup.endpoints,
+ placement_targets: zonegroup.placement_targets,
+ remove_zones: removedZones,
+ add_zones: addedZones
+ };
+ return this.http.put(`${this.url}/${zonegroup.name}`, requestBody);
}
list(): Observable<object> {
- return this.rgwDaemonService.request(() => {
- return this.http.get<object>(`${this.url}`);
- });
+ return this.http.get<object>(`${this.url}`);
}
- get(zonegroup: RgwZonegroup): Observable<RgwZonegroup> {
- return this.rgwDaemonService.request(() => {
- return this.http.get(`${this.url}/${zonegroup.name}`);
- });
+ get(zonegroup: RgwZonegroup): Observable<object> {
+ return this.http.get(`${this.url}/${zonegroup.name}`);
}
getAllZonegroupsInfo(): Observable<object> {
- return this.rgwDaemonService.request(() => {
- return this.http.get(`${this.url}/get_all_zonegroups_info`);
- });
+ return this.http.get(`${this.url}/get_all_zonegroups_info`);
}
delete(zonegroupName: string, deletePools: boolean, pools: Set<string>): Observable<any> {
- return this.rgwDaemonService.request((params: HttpParams) => {
- params = params.appendAll({
- zonegroup_name: zonegroupName,
- delete_pools: deletePools,
- pools: Array.from(pools.values())
- });
- return this.http.delete(`${this.url}/${zonegroupName}`, { params: params });
+ let params = new HttpParams();
+ params = params.appendAll({
+ zonegroup_name: zonegroupName,
+ delete_pools: deletePools,
+ pools: Array.from(pools.values())
});
+ return this.http.delete(`${this.url}/${zonegroupName}`, { params: params });
}
getZonegroupTree(zonegroup: RgwZonegroup, defaultZonegroupId: string, realm?: RgwRealm) {
nodes['zones'] = zonegroup.zones;
nodes['placement_targets'] = zonegroup.placement_targets;
nodes['default_placement'] = zonegroup.default_placement;
+ if (nodes['endpoints'].length === 0) {
+ nodes['show_warning'] = true;
+ nodes['warning_message'] = 'Endpoints not configured';
+ }
return nodes;
}
}
<ngb-alert type="{{ bootstrapClass }}"
[dismissible]="dismissible"
- (closed)="onClose()">
+ (closed)="onClose()"
+ [ngClass]="spacingClass">
<table>
<ng-container *ngIf="size === 'normal'; else slim">
<tr>
showTitle = true;
@Input()
dismissible = false;
+ @Input()
+ spacingClass = '';
/**
* The event that is triggered when the close button (x) has been
ssl_key: string;
port: number;
initial_admin_password: string;
+ rgw_realm: string;
+ rgw_zonegroup: string;
+ rgw_zone: string;
}
export interface CephServicePlacement {
summary: Display RGW Daemons
tags:
- RgwDaemon
+ /api/rgw/daemon/set_multisite_config:
+ put:
+ parameters: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ properties:
+ daemon_name:
+ type: string
+ realm_name:
+ type: string
+ zone_name:
+ type: string
+ zonegroup_name:
+ type: string
+ type: object
+ responses:
+ '200':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Resource updated.
+ '202':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Operation is still executing. Please check the task queue.
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ tags:
+ - RgwDaemon
/api/rgw/daemon/{svc_id}:
get:
parameters:
- jwt: []
tags:
- RgwRealm
+ /api/rgw/realm/get_realm_tokens:
+ get:
+ parameters: []
+ responses:
+ '200':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: OK
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ tags:
+ - RgwRealm
+ /api/rgw/realm/import_realm_token:
+ post:
+ parameters: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ properties:
+ daemon_name:
+ type: string
+ realm_token:
+ type: string
+ zone_name:
+ type: string
+ required:
+ - realm_token
+ - zone_name
+ type: object
+ responses:
+ '201':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Resource created.
+ '202':
+ content:
+ application/vnd.ceph.api.v1.0+json:
+ type: object
+ description: Operation is still executing. Please check the task queue.
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ tags:
+ - RgwRealm
/api/rgw/realm/{realm_name}:
delete:
parameters:
required: true
schema:
type: string
- - allowEmptyValue: true
- in: query
- name: daemon_name
- schema:
- type: string
responses:
'202':
content:
- RgwRealm
/api/rgw/realm/{realm_name}:
delete:
- parameters:
- - in: path
- name: realm_name
- required: true
- schema:
- type: string
- responses:
- '202':
- content:
- application/vnd.ceph.api.v1.0+json:
- type: object
- description: Operation is still executing. Please check the task queue.
- '204':
- content:
- application/vnd.ceph.api.v1.0+json:
- type: object
- description: Resource deleted.
- '400':
- description: Operation exception. Please check the response body for details.
- '401':
- description: Unauthenticated access. Please login first.
- '403':
- description: Unauthorized access. Please check your permissions.
- '500':
- description: Unexpected error. Please check the response body for the stack
- trace.
- security:
- - jwt: []
- tags:
- - RgwRealm
- get:
parameters:
- in: path
name: realm_name
application/json:
schema:
properties:
- daemon_name:
- type: string
default:
default: ''
type: string
- RgwUser
/api/rgw/zone:
get:
- parameters:
- - allowEmptyValue: true
- in: query
- name: daemon_name
- schema:
- type: string
+ parameters: []
responses:
'200':
content:
application/json:
schema:
properties:
- createSystemUser:
- default: false
- type: boolean
- daemon_name:
+ access_key:
type: string
default:
default: false
master:
default: false
type: boolean
- master_zone_of_master_zonegroup:
- type: string
- user:
+ secret_key:
type: string
zone_endpoints:
type: string
application/json:
schema:
properties:
- daemon_name:
- type: string
userName:
type: string
zoneName:
/api/rgw/zone/get_user_list:
get:
parameters:
- - allowEmptyValue: true
- in: query
- name: daemon_name
- schema:
- type: string
- allowEmptyValue: true
in: query
name: zoneName
name: zonegroup_name
schema:
type: string
- - allowEmptyValue: true
- in: query
- name: daemon_name
- schema:
- type: string
responses:
'202':
content:
required: true
schema:
type: string
- - allowEmptyValue: true
- in: query
- name: daemon_name
- schema:
- type: string
responses:
'200':
content:
application/json:
schema:
properties:
- compression:
+ access_key:
default: ''
type: string
- daemon_name:
+ compression:
+ default: ''
type: string
data_extra_pool:
default: ''
master:
default: ''
type: string
- master_zone_of_master_zonegroup:
- type: string
new_zone_name:
type: string
placement_target:
default: ''
type: string
- storage_class:
+ secret_key:
default: ''
type: string
- user:
+ storage_class:
default: ''
type: string
zone_endpoints:
- default: []
+ default: ''
type: string
zonegroup_name:
type: string
- RgwZone
/api/rgw/zonegroup:
get:
- parameters:
- - allowEmptyValue: true
- in: query
- name: daemon_name
- schema:
- type: string
+ parameters: []
responses:
'200':
content:
application/json:
schema:
properties:
- daemon_name:
- type: string
default:
type: string
master:
name: pools
schema:
type: string
- - allowEmptyValue: true
- in: query
- name: daemon_name
- schema:
- type: string
responses:
'202':
content:
required: true
schema:
type: string
- - allowEmptyValue: true
- in: query
- name: daemon_name
- schema:
- type: string
responses:
'200':
content:
add_zones:
default: []
type: string
- daemon_name:
- type: string
default:
default: ''
type: string
default: []
type: string
zonegroup_endpoints:
- default: []
+ default: ''
type: string
required:
- realm_name
return {}
+ @classmethod
+ def set_multisite_config(cls, realm_name, zonegroup_name, zone_name, daemon_name):
+ full_daemon_name = 'rgw.' + daemon_name
+
+ KMS_CONFIG = [
+ ['rgw_realm', realm_name],
+ ['rgw_zonegroup', zonegroup_name],
+ ['rgw_zone', zone_name]
+ ]
+
+ for (key, value) in KMS_CONFIG:
+ if value == 'null':
+ continue
+ CephService.send_command('mon', 'config set',
+ who=name_to_config_section(full_daemon_name),
+ name=key, value=value)
+ return {}
+
+ @classmethod
+ def get_realm_tokens(cls):
+ tokens_info = mgr.remote('rgw', 'get_realm_tokens')
+ return tokens_info
+
+ @classmethod
+ def import_realm_token(cls, realm_token, zone_name):
+ tokens_info = mgr.remote('rgw', 'import_realm_token', zone_name=zone_name,
+ realm_token=realm_token, start_radosgw=True)
+ return tokens_info
+
@classmethod
def get_pool_pg_status(cls, pool_name):
# type: (str) -> dict
import logging
import os
import re
-import subprocess
import xml.etree.ElementTree as ET # noqa: N814
from subprocess import SubprocessError
return realm_info['name']
return None
- def create_realm(self, realm_name: str, default: bool):
- rgw_realm_create_cmd = ['realm', 'create']
- cmd_create_realm_options = ['--rgw-realm', realm_name]
- if default != 'false':
- cmd_create_realm_options.append('--default')
- rgw_realm_create_cmd += cmd_create_realm_options
+ @RestClient.api_get('/{bucket_name}?versioning')
+ def get_bucket_versioning(self, bucket_name, request=None):
+ """
+ Get bucket versioning.
+ :param str bucket_name: the name of the bucket.
+ :return: versioning info
+ :rtype: Dict
+ """
+ # pylint: disable=unused-argument
+ result = request()
+ if 'Status' not in result:
+ result['Status'] = 'Suspended'
+ if 'MfaDelete' not in result:
+ result['MfaDelete'] = 'Disabled'
+ return result
+
+ @RestClient.api_put('/{bucket_name}?versioning')
+ def set_bucket_versioning(self, bucket_name, versioning_state, mfa_delete,
+ mfa_token_serial, mfa_token_pin, request=None):
+ """
+ Set bucket versioning.
+ :param str bucket_name: the name of the bucket.
+ :param str versioning_state:
+ https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTVersioningStatus.html
+ :param str mfa_delete: MFA Delete state.
+ :param str mfa_token_serial:
+ https://docs.ceph.com/docs/master/radosgw/mfa/
+ :param str mfa_token_pin: value of a TOTP token at a certain time (auth code)
+ :return: None
+ """
+ # pylint: disable=unused-argument
+ versioning_configuration = ET.Element('VersioningConfiguration')
+ status_element = ET.SubElement(versioning_configuration, 'Status')
+ status_element.text = versioning_state
+
+ headers = {}
+ if mfa_delete and mfa_token_serial and mfa_token_pin:
+ headers['x-amz-mfa'] = '{} {}'.format(mfa_token_serial, mfa_token_pin)
+ mfa_delete_element = ET.SubElement(versioning_configuration, 'MfaDelete')
+ mfa_delete_element.text = mfa_delete
+
+ data = ET.tostring(versioning_configuration, encoding='unicode')
+
try:
- exit_code, _, _ = mgr.send_rgwadmin_command(rgw_realm_create_cmd)
- if exit_code > 0:
- raise DashboardException(msg='Unable to create realm',
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
+ request(data=data, headers=headers)
+ except RequestException as error:
+ msg = str(error)
+ if mfa_delete and mfa_token_serial and mfa_token_pin \
+ and 'AccessDenied' in error.content.decode():
+ msg = 'Bad MFA credentials: {}'.format(msg)
+ raise DashboardException(msg=msg,
+ http_status_code=error.status_code,
+ component='rgw')
- def list_realms(self):
- rgw_realm_list = {}
- rgw_realm_list_cmd = ['realm', 'list']
+ @RestClient.api_get('/{bucket_name}?encryption')
+ def get_bucket_encryption(self, bucket_name, request=None):
+ # pylint: disable=unused-argument
try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_realm_list_cmd)
- if exit_code > 0:
- raise DashboardException(msg='Unable to fetch realm list',
- http_status_code=500, component='rgw')
- rgw_realm_list = out
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- return rgw_realm_list
+ result = request() # type: ignore
+ result['Status'] = 'Enabled'
+ return result
+ except RequestException as e:
+ if e.content:
+ content = json_str_to_object(e.content)
+ if content.get(
+ 'Code') == 'ServerSideEncryptionConfigurationNotFoundError':
+ return {
+ 'Status': 'Disabled',
+ }
+ raise e
- def get_realm(self, realm_name: str):
- realm_info = {}
- rgw_realm_info_cmd = ['realm', 'get', '--rgw-realm', realm_name]
+ @RestClient.api_delete('/{bucket_name}?encryption')
+ def delete_bucket_encryption(self, bucket_name, request=None):
+ # pylint: disable=unused-argument
+ result = request() # type: ignore
+ return result
+
+ @RestClient.api_put('/{bucket_name}?encryption')
+ def set_bucket_encryption(self, bucket_name, key_id,
+ sse_algorithm, request: Optional[object] = None):
+ # pylint: disable=unused-argument
+ encryption_configuration = ET.Element('ServerSideEncryptionConfiguration')
+ rule_element = ET.SubElement(encryption_configuration, 'Rule')
+ default_encryption_element = ET.SubElement(rule_element,
+ 'ApplyServerSideEncryptionByDefault')
+ sse_algo_element = ET.SubElement(default_encryption_element,
+ 'SSEAlgorithm')
+ sse_algo_element.text = sse_algorithm
+ if sse_algorithm == 'aws:kms':
+ kms_master_key_element = ET.SubElement(default_encryption_element,
+ 'KMSMasterKeyID')
+ kms_master_key_element.text = key_id
+ data = ET.tostring(encryption_configuration, encoding='unicode')
try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_realm_info_cmd)
- if exit_code > 0:
- raise DashboardException('Unable to get realm info',
- http_status_code=500, component='rgw')
- realm_info = out
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- return realm_info
+ _ = request(data=data) # type: ignore
+ except RequestException as e:
+ raise DashboardException(msg=str(e), component='rgw')
- def get_all_realms_info(self):
- all_realms_info = {}
- realms_info = []
- rgw_realm_list = self.list_realms()
- if 'realms' in rgw_realm_list:
- if rgw_realm_list['realms'] != []:
- for rgw_realm in rgw_realm_list['realms']:
- realm_info = self.get_realm(rgw_realm)
- realms_info.append(realm_info)
- all_realms_info['realms'] = realms_info # type: ignore
- else:
- all_realms_info['realms'] = [] # type: ignore
- if 'default_info' in rgw_realm_list and rgw_realm_list['default_info'] != '':
- all_realms_info['default_realm'] = rgw_realm_list['default_info'] # type: ignore
- else:
- all_realms_info['default_realm'] = '' # type: ignore
- return all_realms_info
+ @RestClient.api_get('/{bucket_name}?object-lock')
+ def get_bucket_locking(self, bucket_name, request=None):
+ # type: (str, Optional[object]) -> dict
+ """
+ Gets the locking configuration for a bucket. The locking
+ configuration will be applied by default to every new object
+ placed in the specified bucket.
+ :param bucket_name: The name of the bucket.
+ :type bucket_name: str
+ :return: The locking configuration.
+ :rtype: Dict
+ """
+ # pylint: disable=unused-argument
- def delete_realm(self, realm_name: str):
- rgw_delete_realm_cmd = ['realm', 'rm', '--rgw-realm', realm_name]
+ # Try to get the Object Lock configuration. If there is none,
+ # then return default values.
try:
- exit_code, _, _ = mgr.send_rgwadmin_command(rgw_delete_realm_cmd)
- if exit_code > 0:
- raise DashboardException(msg='Unable to delete realm',
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
+ result = request() # type: ignore
+ return {
+ 'lock_enabled': dict_get(result, 'ObjectLockEnabled') == 'Enabled',
+ 'lock_mode': dict_get(result, 'Rule.DefaultRetention.Mode'),
+ 'lock_retention_period_days': dict_get(result, 'Rule.DefaultRetention.Days', 0),
+ 'lock_retention_period_years': dict_get(result, 'Rule.DefaultRetention.Years', 0)
+ }
+ except RequestException as e:
+ if e.content:
+ content = json_str_to_object(e.content)
+ if content.get(
+ 'Code') == 'ObjectLockConfigurationNotFoundError':
+ return {
+ 'lock_enabled': False,
+ 'lock_mode': 'compliance',
+ 'lock_retention_period_days': None,
+ 'lock_retention_period_years': None
+ }
+ raise e
+
+ @RestClient.api_put('/{bucket_name}?object-lock')
+ def set_bucket_locking(self,
+ bucket_name: str,
+ mode: str,
+ retention_period_days: Optional[Union[int, str]] = None,
+ retention_period_years: Optional[Union[int, str]] = None,
+ request: Optional[object] = None) -> None:
+ """
+ Places the locking configuration on the specified bucket. The
+ locking configuration will be applied by default to every new
+ object placed in the specified bucket.
+ :param bucket_name: The name of the bucket.
+ :type bucket_name: str
+ :param mode: The lock mode, e.g. `COMPLIANCE` or `GOVERNANCE`.
+ :type mode: str
+ :param retention_period_days:
+ :type retention_period_days: int
+ :param retention_period_years:
+ :type retention_period_years: int
+ :rtype: None
+ """
+ # pylint: disable=unused-argument
+
+ retention_period_days, retention_period_years = self.perform_validations(
+ retention_period_days, retention_period_years, mode)
+
+ # Generate the XML data like this:
+ # <ObjectLockConfiguration>
+ # <ObjectLockEnabled>string</ObjectLockEnabled>
+ # <Rule>
+ # <DefaultRetention>
+ # <Days>integer</Days>
+ # <Mode>string</Mode>
+ # <Years>integer</Years>
+ # </DefaultRetention>
+ # </Rule>
+ # </ObjectLockConfiguration>
+ locking_configuration = ET.Element('ObjectLockConfiguration')
+ enabled_element = ET.SubElement(locking_configuration,
+ 'ObjectLockEnabled')
+ enabled_element.text = 'Enabled' # Locking can't be disabled.
+ rule_element = ET.SubElement(locking_configuration, 'Rule')
+ default_retention_element = ET.SubElement(rule_element,
+ 'DefaultRetention')
+ mode_element = ET.SubElement(default_retention_element, 'Mode')
+ mode_element.text = mode.upper()
+ if retention_period_days:
+ days_element = ET.SubElement(default_retention_element, 'Days')
+ days_element.text = str(retention_period_days)
+ if retention_period_years:
+ years_element = ET.SubElement(default_retention_element, 'Years')
+ years_element.text = str(retention_period_years)
+
+ data = ET.tostring(locking_configuration, encoding='unicode')
+
+ try:
+ _ = request(data=data) # type: ignore
+ except RequestException as e:
+ raise DashboardException(msg=str(e), component='rgw')
+
+ def list_roles(self) -> List[Dict[str, Any]]:
+ rgw_list_roles_command = ['role', 'list']
+ code, roles, err = mgr.send_rgwadmin_command(rgw_list_roles_command)
+ if code < 0:
+ logger.warning('Error listing roles with code %d: %s', code, err)
+ return []
+
+ return roles
+
+ def create_role(self, role_name: str, role_path: str, role_assume_policy_doc: str) -> None:
+ try:
+ json.loads(role_assume_policy_doc)
+ except: # noqa: E722
+ raise DashboardException('Assume role policy document is not a valid json')
+
+ # valid values:
+ # pylint: disable=C0301
+ # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-path # noqa: E501
+ if len(role_name) > 64:
+ raise DashboardException(
+ f'Role name "{role_name}" is invalid. Should be 64 characters or less')
- def update_period(self):
- rgw_update_period_cmd = ['period', 'update', '--commit']
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_update_period_cmd)
- if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to update period',
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
+ role_name_regex = '[0-9a-zA-Z_+=,.@-]+'
+ if not re.fullmatch(role_name_regex, role_name):
+ raise DashboardException(
+ f'Role name "{role_name}" is invalid. Valid characters are "{role_name_regex}"')
- def edit_realm(self, realm_name: str, new_realm_name: str, default: str = ''):
- rgw_realm_edit_cmd = []
- if new_realm_name != realm_name:
- rgw_realm_edit_cmd = ['realm', 'rename', '--rgw-realm',
- realm_name, '--realm-new-name', new_realm_name]
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_realm_edit_cmd, False)
- if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to edit realm',
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- if default and str_to_bool(default):
- rgw_realm_edit_cmd = ['realm', 'default', '--rgw-realm', new_realm_name]
- try:
- exit_code, _, _ = mgr.send_rgwadmin_command(rgw_realm_edit_cmd, False)
- if exit_code > 0:
- raise DashboardException(msg='Unable to set {} as default realm'.format(new_realm_name), # noqa E501 #pylint: disable=line-too-long
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
+ if not os.path.isabs(role_path):
+ raise DashboardException(
+ f'Role path "{role_path}" is invalid. It should be an absolute path')
+ if role_path[-1] != '/':
+ raise DashboardException(
+ f'Role path "{role_path}" is invalid. It should start and end with a slash')
+ path_regex = '(\u002F)|(\u002F[\u0021-\u007E]+\u002F)'
+ if not re.fullmatch(path_regex, role_path):
+ raise DashboardException(
+ (f'Role path "{role_path}" is invalid.'
+ f'Role path should follow the pattern "{path_regex}"'))
- def create_zonegroup(self, realm_name: str, zonegroup_name: str,
- default: bool, master: bool, endpoints: List[str]):
- rgw_zonegroup_create_cmd = ['zonegroup', 'create']
- cmd_create_zonegroup_options = ['--rgw-zonegroup', zonegroup_name]
- if realm_name != 'null':
- cmd_create_zonegroup_options.append('--rgw-realm')
- cmd_create_zonegroup_options.append(realm_name)
- if default != 'false':
- cmd_create_zonegroup_options.append('--default')
- if master != 'false':
- cmd_create_zonegroup_options.append('--master')
- if endpoints != 'null': # type: ignore
- if isinstance(endpoints, list) and len(endpoints) > 1:
- endpoint = ','.join(endpoints)
- else:
- endpoint = endpoints # type: ignore
- cmd_create_zonegroup_options.append('--endpoints')
- cmd_create_zonegroup_options.append(endpoint)
- rgw_zonegroup_create_cmd += cmd_create_zonegroup_options
- try:
- exit_code, out, err = mgr.send_rgwadmin_command(rgw_zonegroup_create_cmd)
- if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to get realm info',
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- return out
+ rgw_create_role_command = ['role', 'create', '--role-name', role_name, '--path', role_path]
+ if role_assume_policy_doc:
+ rgw_create_role_command += ['--assume-role-policy-doc', f"{role_assume_policy_doc}"]
- def modify_zonegroup(self, realm_name: str, zonegroup_name: str, default: str, master: str,
- endpoints: List[str]):
- if realm_name:
- rgw_zonegroup_modify_cmd = ['zonegroup', 'modify',
- '--rgw-realm', realm_name,
- '--rgw-zonegroup', zonegroup_name]
- if endpoints:
- if len(endpoints) > 1:
- endpoint = ','.join(str(e) for e in endpoints)
- else:
- endpoint = endpoints[0]
- rgw_zonegroup_modify_cmd.append('--endpoints')
- rgw_zonegroup_modify_cmd.append(endpoint)
- if master and str_to_bool(master):
- rgw_zonegroup_modify_cmd.append('--master')
- if default and str_to_bool(default):
- rgw_zonegroup_modify_cmd.append('--default')
+ code, _roles, _err = mgr.send_rgwadmin_command(rgw_create_role_command,
+ stdout_as_json=False)
+ if code != 0:
+ # pylint: disable=C0301
+ link = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-path' # noqa: E501
+ msg = (f'Error creating role with code {code}: '
+ 'Looks like the document has a wrong format.'
+ f' For more information about the format look at {link}')
+ raise DashboardException(msg=msg, component='rgw')
+
+ def perform_validations(self, retention_period_days, retention_period_years, mode):
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_modify_cmd)
- if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to modify zonegroup {}'.format(zonegroup_name), # noqa E501 #pylint: disable=line-too-long
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
+ retention_period_days = int(retention_period_days) if retention_period_days else 0
+ retention_period_years = int(retention_period_years) if retention_period_years else 0
+ if retention_period_days < 0 or retention_period_years < 0:
+ raise ValueError
+ except (TypeError, ValueError):
+ msg = "Retention period must be a positive integer."
+ raise DashboardException(msg=msg, component='rgw')
+ if retention_period_days and retention_period_years:
+ # https://docs.aws.amazon.com/AmazonS3/latest/API/archive-RESTBucketPUTObjectLockConfiguration.html
+ msg = "Retention period requires either Days or Years. "\
+ "You can't specify both at the same time."
+ raise DashboardException(msg=msg, component='rgw')
+ if not retention_period_days and not retention_period_years:
+ msg = "Retention period requires either Days or Years. "\
+ "You must specify at least one."
+ raise DashboardException(msg=msg, component='rgw')
+ if not isinstance(mode, str) or mode.upper() not in ['COMPLIANCE', 'GOVERNANCE']:
+ msg = "Retention mode must be either COMPLIANCE or GOVERNANCE."
+ raise DashboardException(msg=msg, component='rgw')
+ return retention_period_days, retention_period_years
- def add_or_remove_zone(self, zonegroup_name: str, zone_name: str, action: str):
- if action == 'add':
- rgw_zonegroup_add_zone_cmd = ['zonegroup', 'add', '--rgw-zonegroup',
- zonegroup_name, '--rgw-zone', zone_name]
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_add_zone_cmd)
- if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to add zone {} to zonegroup {}'.format(zone_name, zonegroup_name), # noqa E501 #pylint: disable=line-too-long
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
- if action == 'remove':
- rgw_zonegroup_rm_zone_cmd = ['zonegroup', 'remove',
- '--rgw-zonegroup', zonegroup_name, '--rgw-zone', zone_name]
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_rm_zone_cmd)
- if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to remove zone {} from zonegroup {}'.format(zone_name, zonegroup_name), # noqa E501 #pylint: disable=line-too-long
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
- def get_placement_targets_by_zonegroup(self, zonegroup_name: str):
- rgw_get_placement_cmd = ['zonegroup', 'placement',
- 'list', '--rgw-zonegroup', zonegroup_name]
+class RgwMultisite:
+ def migrate_to_multisite(self, realm_name: str, zonegroup_name: str, zone_name: str,
+ zonegroup_endpoints: str, zone_endpoints: str, access_key: str,
+ secret_key: str):
+ rgw_realm_create_cmd = ['realm', 'create', '--rgw-realm', realm_name, '--default']
try:
- exit_code, out, err = mgr.send_rgwadmin_command(rgw_get_placement_cmd)
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_realm_create_cmd, False)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to get placement targets',
+ raise DashboardException(e=err, msg='Unable to create realm',
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- return out
-
- def add_placement_targets(self, zonegroup_name: str, placement_targets: List[Dict]):
- rgw_add_placement_cmd = ['zonegroup', 'placement', 'add']
- for placement_target in placement_targets:
- cmd_add_placement_options = ['--rgw-zonegroup', zonegroup_name,
- '--placement-id', placement_target['placement_id']]
- if placement_target['tags']:
- cmd_add_placement_options += ['--tags', placement_target['tags']]
- rgw_add_placement_cmd += cmd_add_placement_options
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_add_placement_cmd)
- if exit_code > 0:
- raise DashboardException(e=err,
- msg='Unable to add placement target {} to zonegroup {}'.format(placement_target['placement_id'], zonegroup_name), # noqa E501 #pylint: disable=line-too-long
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
- storage_classes = placement_target['storage_class'].split(",") if placement_target['storage_class'] else [] # noqa E501 #pylint: disable=line-too-long
- if storage_classes:
- for sc in storage_classes:
- cmd_add_placement_options = ['--storage-class', sc]
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(
- rgw_add_placement_cmd + cmd_add_placement_options)
- if exit_code > 0:
- raise DashboardException(e=err,
- msg='Unable to add placement target {} to zonegroup {}'.format(placement_target['placement_id'], zonegroup_name), # noqa E501 #pylint: disable=line-too-long
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
-
- def modify_placement_targets(self, zonegroup_name: str, placement_targets: List[Dict]):
- rgw_add_placement_cmd = ['zonegroup', 'placement', 'modify']
- for placement_target in placement_targets:
- cmd_add_placement_options = ['--rgw-zonegroup', zonegroup_name,
- '--placement-id', placement_target['placement_id']]
- if placement_target['tags']:
- cmd_add_placement_options += ['--tags', placement_target['tags']]
- rgw_add_placement_cmd += cmd_add_placement_options
- storage_classes = placement_target['storage_class'].split(",") if placement_target['storage_class'] else [] # noqa E501 #pylint: disable=line-too-long
- if storage_classes:
- for sc in storage_classes:
- cmd_add_placement_options = []
- cmd_add_placement_options = ['--storage-class', sc]
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(
- rgw_add_placement_cmd + cmd_add_placement_options)
- if exit_code > 0:
- raise DashboardException(e=err,
- msg='Unable to add placement target {} to zonegroup {}'.format(placement_target['placement_id'], zonegroup_name), # noqa E501 #pylint: disable=line-too-long
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
- else:
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_add_placement_cmd)
- if exit_code > 0:
- raise DashboardException(e=err,
- msg='Unable to add placement target {} to zonegroup {}'.format(placement_target['placement_id'], zonegroup_name), # noqa E501 #pylint: disable=line-too-long
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
-
- # pylint: disable=W0102
- def edit_zonegroup(self, realm_name: str, zonegroup_name: str, new_zonegroup_name: str,
- default: str = '', master: str = '', endpoints: List[str] = [],
- add_zones: List[str] = [], remove_zones: List[str] = [],
- placement_targets: List[Dict[str, str]] = []):
- rgw_zonegroup_edit_cmd = []
- if new_zonegroup_name != zonegroup_name:
- rgw_zonegroup_edit_cmd = ['zonegroup', 'rename', '--rgw-zonegroup', zonegroup_name,
- '--zonegroup-new-name', new_zonegroup_name]
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_edit_cmd, False)
- if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to rename zonegroup to {}'.format(new_zonegroup_name), # noqa E501 #pylint: disable=line-too-long
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
- self.modify_zonegroup(realm_name, new_zonegroup_name, default, master, endpoints)
- if add_zones:
- for zone_name in add_zones:
- self.add_or_remove_zone(new_zonegroup_name, zone_name, 'add')
- if remove_zones:
- for zone_name in remove_zones:
- self.add_or_remove_zone(new_zonegroup_name, zone_name, 'remove')
- existing_placement_targets = self.get_placement_targets_by_zonegroup(new_zonegroup_name)
- existing_placement_targets_ids = [pt['key'] for pt in existing_placement_targets]
- if placement_targets:
- for pt in placement_targets:
- if pt['placement_id'] in existing_placement_targets_ids:
- self.modify_placement_targets(new_zonegroup_name, placement_targets)
- else:
- self.add_placement_targets(new_zonegroup_name, placement_targets)
- def list_zonegroups(self):
- rgw_zonegroup_list = {}
- rgw_zonegroup_list_cmd = ['zonegroup', 'list']
+ rgw_zonegroup_edit_cmd = ['zonegroup', 'rename', '--rgw-zonegroup', 'default',
+ '--zonegroup-new-name', zonegroup_name]
try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zonegroup_list_cmd)
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_edit_cmd, False)
if exit_code > 0:
- raise DashboardException(msg='Unable to fetch zonegroup list',
+ raise DashboardException(e=err, msg='Unable to rename zonegroup to {}'.format(zonegroup_name), # noqa E501 #pylint: disable=line-too-long
http_status_code=500, component='rgw')
- rgw_zonegroup_list = out
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- return rgw_zonegroup_list
- def get_zonegroup(self, zonegroup_name: str):
- zonegroup_info = {}
- rgw_zonegroup_info_cmd = ['zonegroup', 'get', '--rgw-zonegroup', zonegroup_name]
+ rgw_zone_edit_cmd = ['zone', 'rename', '--rgw-zone',
+ 'default', '--zone-new-name', zone_name,
+ '--rgw-zonegroup', zonegroup_name]
try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zonegroup_info_cmd)
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_edit_cmd, False)
if exit_code > 0:
- raise DashboardException('Unable to get zonegroup info',
+ raise DashboardException(e=err, msg='Unable to rename zone to {}'.format(zone_name), # noqa E501 #pylint: disable=line-too-long
http_status_code=500, component='rgw')
- zonegroup_info = out
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- return zonegroup_info
-
- def get_all_zonegroups_info(self):
- all_zonegroups_info = {}
- zonegroups_info = []
- rgw_zonegroup_list = self.list_zonegroups()
- if 'zonegroups' in rgw_zonegroup_list:
- if rgw_zonegroup_list['zonegroups'] != []:
- for rgw_zonegroup in rgw_zonegroup_list['zonegroups']:
- zonegroup_info = self.get_zonegroup(rgw_zonegroup)
- zonegroups_info.append(zonegroup_info)
- all_zonegroups_info['zonegroups'] = zonegroups_info # type: ignore
- else:
- all_zonegroups_info['zonegroups'] = [] # type: ignore
- if 'default_info' in rgw_zonegroup_list and rgw_zonegroup_list['default_info'] != '':
- all_zonegroups_info['default_zonegroup'] = rgw_zonegroup_list['default_info']
- else:
- all_zonegroups_info['default_zonegroup'] = '' # type: ignore
- return all_zonegroups_info
- def delete_zonegroup(self, zonegroup_name: str, delete_pools: str, pools: List[str]):
- if delete_pools == 'true':
- zonegroup_info = self.get_zonegroup(zonegroup_name)
- rgw_delete_zonegroup_cmd = ['zonegroup', 'delete', '--rgw-zonegroup', zonegroup_name]
+ rgw_zonegroup_modify_cmd = ['zonegroup', 'modify',
+ '--rgw-realm', realm_name,
+ '--rgw-zonegroup', zonegroup_name]
+ if zonegroup_endpoints:
+ rgw_zonegroup_modify_cmd.append('--endpoints')
+ rgw_zonegroup_modify_cmd.append(zonegroup_endpoints)
+ rgw_zonegroup_modify_cmd.append('--master')
+ rgw_zonegroup_modify_cmd.append('--default')
try:
- exit_code, _, _ = mgr.send_rgwadmin_command(rgw_delete_zonegroup_cmd)
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_modify_cmd)
if exit_code > 0:
- raise DashboardException(msg='Unable to delete zonegroup',
+ raise DashboardException(e=err, msg='Unable to modify zonegroup {}'.format(zonegroup_name), # noqa E501 #pylint: disable=line-too-long
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
- if delete_pools == 'true':
- for zone in zonegroup_info['zones']:
- self.delete_zone(zone['name'], 'true', pools)
- def create_zone(self, zone_name, zonegroup_name, default, master, endpoints, user,
- createSystemUser, master_zone_of_master_zonegroup):
- if user != 'null':
- access_key, secret_key = self.get_rgw_user_keys(user, master_zone_of_master_zonegroup)
- else:
- access_key = None # type: ignore
- secret_key = None # type: ignore
- rgw_zone_create_cmd = ['zone', 'create']
- cmd_create_zone_options = ['--rgw-zone', zone_name]
- if zonegroup_name != 'null':
- cmd_create_zone_options.append('--rgw-zonegroup')
- cmd_create_zone_options.append(zonegroup_name)
- if default != 'false':
- cmd_create_zone_options.append('--default')
- if master != 'false':
- cmd_create_zone_options.append('--master')
- if endpoints != 'null':
- cmd_create_zone_options.append('--endpoints')
- cmd_create_zone_options.append(endpoints)
- if access_key is not None:
- cmd_create_zone_options.append('--access-key')
- cmd_create_zone_options.append(access_key)
- if secret_key is not None:
- cmd_create_zone_options.append('--secret')
- cmd_create_zone_options.append(secret_key)
- rgw_zone_create_cmd += cmd_create_zone_options
+ rgw_zone_modify_cmd = ['zone', 'modify', '--rgw-realm', realm_name,
+ '--rgw-zonegroup', zonegroup_name,
+ '--rgw-zone', zone_name]
+ if zone_endpoints:
+ rgw_zone_modify_cmd.append('--endpoints')
+ rgw_zone_modify_cmd.append(zone_endpoints)
+ rgw_zone_modify_cmd.append('--master')
+ rgw_zone_modify_cmd.append('--default')
try:
- exit_code, out, err = mgr.send_rgwadmin_command(rgw_zone_create_cmd)
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_modify_cmd)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to create zone',
+ raise DashboardException(e=err, msg='Unable to modify zone',
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
-
- if createSystemUser == 'true':
- self.create_system_user(user, zone_name)
- access_key, secret_key = self.get_rgw_user_keys(user, zone_name)
+ if access_key and secret_key:
rgw_zone_modify_cmd = ['zone', 'modify', '--rgw-zone', zone_name,
'--access-key', access_key, '--secret', secret_key]
try:
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
-
- return out
-
- def get_rgw_user_keys(self, user, zone_name):
- access_key = ''
- secret_key = ''
- rgw_user_info_cmd = ['user', 'info', '--uid', user, '--rgw-zone', zone_name]
- try:
- _, out, _ = mgr.send_rgwadmin_command(rgw_user_info_cmd)
- if out:
- access_key, secret_key = self.parse_secrets(user, out)
- except SubprocessError as error:
- logger.exception(error)
-
- return access_key, secret_key
-
- def parse_secrets(self, user, data):
- for key in data.get('keys', []):
- if key.get('user') == user:
- access_key = key.get('access_key')
- secret_key = key.get('secret_key')
- return access_key, secret_key
- return '', ''
- def modify_zone(self, zone_name: str, zonegroup_name: str, default: str, master: str,
- endpoints: List[str], user: str, master_zone_of_master_zonegroup):
- if user:
- access_key, secret_key = self.get_rgw_user_keys(user, master_zone_of_master_zonegroup)
- else:
- access_key = None
- secret_key = None
- rgw_zone_modify_cmd = ['zone', 'modify', '--rgw-zonegroup',
- zonegroup_name, '--rgw-zone', zone_name]
- if endpoints:
- if len(endpoints) > 1:
- endpoint = ','.join(str(e) for e in endpoints)
- else:
- endpoint = endpoints[0]
- rgw_zone_modify_cmd.append('--endpoints')
- rgw_zone_modify_cmd.append(endpoint)
- if default and str_to_bool(default):
- rgw_zone_modify_cmd.append('--default')
- if master and str_to_bool(master):
- rgw_zone_modify_cmd.append('--master')
- if access_key is not None:
- rgw_zone_modify_cmd.append('--access-key')
- rgw_zone_modify_cmd.append(access_key)
- if secret_key is not None:
- rgw_zone_modify_cmd.append('--secret')
- rgw_zone_modify_cmd.append(secret_key)
+ def create_realm(self, realm_name: str, default: bool):
+ rgw_realm_create_cmd = ['realm', 'create']
+ cmd_create_realm_options = ['--rgw-realm', realm_name]
+ if default != 'false':
+ cmd_create_realm_options.append('--default')
+ rgw_realm_create_cmd += cmd_create_realm_options
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_modify_cmd)
+ exit_code, _, _ = mgr.send_rgwadmin_command(rgw_realm_create_cmd)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to modify zone',
+ raise DashboardException(msg='Unable to create realm',
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
- def add_placement_targets_zone(self, zone_name: str, placement_target: str, data_pool: str,
- index_pool: str, data_extra_pool: str):
- rgw_zone_add_placement_cmd = ['zone', 'placement', 'add', '--rgw-zone', zone_name,
- '--placement-id', placement_target, '--data-pool', data_pool,
- '--index-pool', index_pool,
- '--data-extra-pool', data_extra_pool]
+ def list_realms(self):
+ rgw_realm_list = {}
+ rgw_realm_list_cmd = ['realm', 'list']
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_add_placement_cmd)
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_realm_list_cmd)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to add placement target {} to zone {}'.format(placement_target, zone_name), # noqa E501 #pylint: disable=line-too-long
+ raise DashboardException(msg='Unable to fetch realm list',
http_status_code=500, component='rgw')
+ rgw_realm_list = out
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
+ return rgw_realm_list
- def add_storage_class_zone(self, zone_name: str, placement_target: str, storage_class: str,
- data_pool: str, compression: str):
- rgw_zone_add_storage_class_cmd = ['zone', 'placement', 'add', '--rgw-zone', zone_name,
- '--placement-id', placement_target,
- '--storage-class', storage_class,
- '--data-pool', data_pool,
- '--compression', compression]
+ def get_realm(self, realm_name: str):
+ realm_info = {}
+ rgw_realm_info_cmd = ['realm', 'get', '--rgw-realm', realm_name]
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_add_storage_class_cmd)
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_realm_info_cmd)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to add storage class {} to zone {}'.format(storage_class, zone_name), # noqa E501 #pylint: disable=line-too-long
+ raise DashboardException('Unable to get realm info',
http_status_code=500, component='rgw')
+ realm_info = out
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
+ return realm_info
- def edit_zone(self, zone_name: str, new_zone_name: str, zonegroup_name: str, default: str = '',
- master: str = '', endpoints: List[str] = [], user: str = '',
- placement_target: str = '', data_pool: str = '', index_pool: str = '',
- data_extra_pool: str = '', storage_class: str = '', data_pool_class: str = '',
- compression: str = '', master_zone_of_master_zonegroup=None):
- if new_zone_name != zone_name:
- rgw_zone_rename_cmd = ['zone', 'rename', '--rgw-zone',
- zone_name, '--zone-new-name', new_zone_name]
+ def get_all_realms_info(self):
+ all_realms_info = {}
+ realms_info = []
+ rgw_realm_list = self.list_realms()
+ if 'realms' in rgw_realm_list:
+ if rgw_realm_list['realms'] != []:
+ for rgw_realm in rgw_realm_list['realms']:
+ realm_info = self.get_realm(rgw_realm)
+ realms_info.append(realm_info)
+ all_realms_info['realms'] = realms_info # type: ignore
+ else:
+ all_realms_info['realms'] = [] # type: ignore
+ if 'default_info' in rgw_realm_list and rgw_realm_list['default_info'] != '':
+ all_realms_info['default_realm'] = rgw_realm_list['default_info'] # type: ignore
+ else:
+ all_realms_info['default_realm'] = '' # type: ignore
+ return all_realms_info
+
+ def edit_realm(self, realm_name: str, new_realm_name: str, default: str = ''):
+ rgw_realm_edit_cmd = []
+ if new_realm_name != realm_name:
+ rgw_realm_edit_cmd = ['realm', 'rename', '--rgw-realm',
+ realm_name, '--realm-new-name', new_realm_name]
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_rename_cmd, False)
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_realm_edit_cmd, False)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to rename zone to {}'.format(new_zone_name), # noqa E501 #pylint: disable=line-too-long
+ raise DashboardException(e=err, msg='Unable to edit realm',
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
- self.modify_zone(new_zone_name, zonegroup_name, default, master, endpoints, user,
- master_zone_of_master_zonegroup)
- self.add_placement_targets_zone(new_zone_name, placement_target,
- data_pool, index_pool, data_extra_pool)
- self.add_storage_class_zone(new_zone_name, placement_target, storage_class,
- data_pool_class, compression)
-
- def list_zones(self):
- rgw_zone_list = {}
- rgw_zone_list_cmd = ['zone', 'list']
- try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zone_list_cmd)
- if exit_code > 0:
- raise DashboardException(msg='Unable to fetch zone list',
- http_status_code=500, component='rgw')
- rgw_zone_list = out
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- return rgw_zone_list
-
- def get_zone(self, zone_name: str):
- zone_info = {}
- rgw_zone_info_cmd = ['zone', 'get', '--rgw-zone', zone_name]
- try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zone_info_cmd)
- if exit_code > 0:
- raise DashboardException('Unable to get zone info',
- http_status_code=500, component='rgw')
- zone_info = out
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- return zone_info
-
- def get_all_zones_info(self):
- all_zones_info = {}
- zones_info = []
- rgw_zone_list = self.list_zones()
- if 'zones' in rgw_zone_list:
- if rgw_zone_list['zones'] != []:
- for rgw_zone in rgw_zone_list['zones']:
- zone_info = self.get_zone(rgw_zone)
- zones_info.append(zone_info)
- all_zones_info['zones'] = zones_info # type: ignore
- else:
- all_zones_info['zones'] = []
- if 'default_info' in rgw_zone_list and rgw_zone_list['default_info'] != '':
- all_zones_info['default_zone'] = rgw_zone_list['default_info'] # type: ignore
- else:
- all_zones_info['default_zone'] = '' # type: ignore
- return all_zones_info
-
- def delete_zone(self, zone_name: str, delete_pools: str, pools: List[str],
- zonegroup_name: str = '',):
- rgw_remove_zone_from_zonegroup_cmd = ['zonegroup', 'remove', '--rgw-zonegroup',
- zonegroup_name, '--rgw-zone', zone_name]
- rgw_delete_zone_cmd = ['zone', 'delete', '--rgw-zone', zone_name]
- if zonegroup_name:
+ if default and str_to_bool(default):
+ rgw_realm_edit_cmd = ['realm', 'default', '--rgw-realm', new_realm_name]
try:
- exit_code, _, _ = mgr.send_rgwadmin_command(rgw_remove_zone_from_zonegroup_cmd)
+ exit_code, _, _ = mgr.send_rgwadmin_command(rgw_realm_edit_cmd, False)
if exit_code > 0:
- raise DashboardException(msg='Unable to remove zone from zonegroup',
+ raise DashboardException(msg='Unable to set {} as default realm'.format(new_realm_name), # noqa E501 #pylint: disable=line-too-long
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
+
+ def delete_realm(self, realm_name: str):
+ rgw_delete_realm_cmd = ['realm', 'rm', '--rgw-realm', realm_name]
try:
- exit_code, _, _ = mgr.send_rgwadmin_command(rgw_delete_zone_cmd)
+ exit_code, _, _ = mgr.send_rgwadmin_command(rgw_delete_realm_cmd)
if exit_code > 0:
- raise DashboardException(msg='Unable to delete zone',
+ raise DashboardException(msg='Unable to delete realm',
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- self.update_period()
- if delete_pools == 'true':
- self.delete_pools(pools)
-
- def delete_pools(self, pools):
- for pool in pools:
- if mgr.rados.pool_exists(pool):
- mgr.rados.delete_pool(pool)
-
- def get_multisite_status(self):
- is_multisite_configured = True
- rgw_realm_list = self.list_realms()
- rgw_zonegroup_list = self.list_zonegroups()
- rgw_zone_list = self.list_zones()
- if len(rgw_realm_list['realms']) < 1 and len(rgw_zonegroup_list['zonegroups']) < 1 \
- and len(rgw_zone_list['zones']) < 1:
- is_multisite_configured = False
- return is_multisite_configured
- def get_multisite_sync_status(self):
- sync_status = ''
- rgw_sync_status_cmd = ['sync', 'status']
+ def create_zonegroup(self, realm_name: str, zonegroup_name: str,
+ default: bool, master: bool, endpoints: str):
+ rgw_zonegroup_create_cmd = ['zonegroup', 'create']
+ cmd_create_zonegroup_options = ['--rgw-zonegroup', zonegroup_name]
+ if realm_name != 'null':
+ cmd_create_zonegroup_options.append('--rgw-realm')
+ cmd_create_zonegroup_options.append(realm_name)
+ if default != 'false':
+ cmd_create_zonegroup_options.append('--default')
+ if master != 'false':
+ cmd_create_zonegroup_options.append('--master')
+ if endpoints:
+ cmd_create_zonegroup_options.append('--endpoints')
+ cmd_create_zonegroup_options.append(endpoints)
+ rgw_zonegroup_create_cmd += cmd_create_zonegroup_options
try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_sync_status_cmd, False)
+ exit_code, out, err = mgr.send_rgwadmin_command(rgw_zonegroup_create_cmd)
if exit_code > 0:
- raise DashboardException('Unable to get sync status',
+ raise DashboardException(e=err, msg='Unable to get realm info',
http_status_code=500, component='rgw')
- sync_status = out
- except subprocess.TimeoutExpired:
- sync_status = 'Timeout Expired'
- return sync_status
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ return out
- def create_system_user(self, userName: str, zoneName: str):
- rgw_user_create_cmd = ['user', 'create', '--uid', userName,
- '--display-name', userName, '--rgw-zone', zoneName, '--system']
+ def list_zonegroups(self):
+ rgw_zonegroup_list = {}
+ rgw_zonegroup_list_cmd = ['zonegroup', 'list']
try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_user_create_cmd)
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zonegroup_list_cmd)
if exit_code > 0:
- raise DashboardException(msg='Unable to create system user',
+ raise DashboardException(msg='Unable to fetch zonegroup list',
http_status_code=500, component='rgw')
- return out
+ rgw_zonegroup_list = out
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
+ return rgw_zonegroup_list
- def get_user_list(self, zoneName: str):
- all_users_info = []
- user_list = []
- rgw_user_list_cmd = ['user', 'list', '--rgw-zone', zoneName]
+ def get_zonegroup(self, zonegroup_name: str):
+ zonegroup_info = {}
+ if zonegroup_name != 'default':
+ rgw_zonegroup_info_cmd = ['zonegroup', 'get', '--rgw-zonegroup', zonegroup_name]
+ else:
+ rgw_zonegroup_info_cmd = ['zonegroup', 'get', '--rgw-zonegroup',
+ zonegroup_name, '--rgw-realm', 'default']
try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_user_list_cmd)
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zonegroup_info_cmd)
if exit_code > 0:
- raise DashboardException('Unable to get user list',
+ raise DashboardException('Unable to get zonegroup info',
http_status_code=500, component='rgw')
- user_list = out
+ zonegroup_info = out
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
+ return zonegroup_info
- if len(user_list) > 0:
- for user_name in user_list:
- rgw_user_info_cmd = ['user', 'info', '--uid', user_name, '--rgw-zone', zoneName]
- try:
- exit_code, out, _ = mgr.send_rgwadmin_command(rgw_user_info_cmd)
- if exit_code > 0:
- raise DashboardException('Unable to get user info',
- http_status_code=500, component='rgw')
- all_users_info.append(out)
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
- return all_users_info
+ def get_all_zonegroups_info(self):
+ all_zonegroups_info = {}
+ zonegroups_info = []
+ rgw_zonegroup_list = self.list_zonegroups()
+ if 'zonegroups' in rgw_zonegroup_list:
+ if rgw_zonegroup_list['zonegroups'] != []:
+ for rgw_zonegroup in rgw_zonegroup_list['zonegroups']:
+ zonegroup_info = self.get_zonegroup(rgw_zonegroup)
+ zonegroups_info.append(zonegroup_info)
+ all_zonegroups_info['zonegroups'] = zonegroups_info # type: ignore
+ else:
+ all_zonegroups_info['zonegroups'] = [] # type: ignore
+ if 'default_info' in rgw_zonegroup_list and rgw_zonegroup_list['default_info'] != '':
+ all_zonegroups_info['default_zonegroup'] = rgw_zonegroup_list['default_info']
+ else:
+ all_zonegroups_info['default_zonegroup'] = '' # type: ignore
+ return all_zonegroups_info
- def migrate_to_multisite(self, realm_name: str, zonegroup_name: str, zone_name: str,
- zonegroup_endpoints: List[str], zone_endpoints: List[str], user: str):
- rgw_realm_create_cmd = ['realm', 'create', '--rgw-realm', realm_name, '--default']
+ def delete_zonegroup(self, zonegroup_name: str, delete_pools: str, pools: List[str]):
+ if delete_pools == 'true':
+ zonegroup_info = self.get_zonegroup(zonegroup_name)
+ rgw_delete_zonegroup_cmd = ['zonegroup', 'delete', '--rgw-zonegroup', zonegroup_name]
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_realm_create_cmd, False)
+ exit_code, _, _ = mgr.send_rgwadmin_command(rgw_delete_zonegroup_cmd)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to create realm',
+ raise DashboardException(msg='Unable to delete zonegroup',
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+ if delete_pools == 'true':
+ for zone in zonegroup_info['zones']:
+ self.delete_zone(zone['name'], 'true', pools)
- rgw_zonegroup_edit_cmd = ['zonegroup', 'rename', '--rgw-zonegroup', 'default',
- '--zonegroup-new-name', zonegroup_name]
+ def modify_zonegroup(self, realm_name: str, zonegroup_name: str, default: str, master: str,
+ endpoints: str):
+
+ rgw_zonegroup_modify_cmd = ['zonegroup', 'modify',
+ '--rgw-realm', realm_name,
+ '--rgw-zonegroup', zonegroup_name]
+ if endpoints:
+ rgw_zonegroup_modify_cmd.append('--endpoints')
+ rgw_zonegroup_modify_cmd.append(endpoints)
+ if master and str_to_bool(master):
+ rgw_zonegroup_modify_cmd.append('--master')
+ if default and str_to_bool(default):
+ rgw_zonegroup_modify_cmd.append('--default')
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_edit_cmd, False)
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_modify_cmd)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to rename zonegroup to {}'.format(zonegroup_name), # noqa E501 #pylint: disable=line-too-long
+ raise DashboardException(e=err, msg='Unable to modify zonegroup {}'.format(zonegroup_name), # noqa E501 #pylint: disable=line-too-long
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
- rgw_zone_edit_cmd = ['zone', 'rename', '--rgw-zone',
- 'default', '--zone-new-name', zone_name,
- '--rgw-zonegroup', zonegroup_name]
+ def add_or_remove_zone(self, zonegroup_name: str, zone_name: str, action: str):
+ if action == 'add':
+ rgw_zonegroup_add_zone_cmd = ['zonegroup', 'add', '--rgw-zonegroup',
+ zonegroup_name, '--rgw-zone', zone_name]
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_add_zone_cmd)
+ if exit_code > 0:
+ raise DashboardException(e=err, msg='Unable to add zone {} to zonegroup {}'.format(zone_name, zonegroup_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+ if action == 'remove':
+ rgw_zonegroup_rm_zone_cmd = ['zonegroup', 'remove',
+ '--rgw-zonegroup', zonegroup_name, '--rgw-zone', zone_name]
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_rm_zone_cmd)
+ if exit_code > 0:
+ raise DashboardException(e=err, msg='Unable to remove zone {} from zonegroup {}'.format(zone_name, zonegroup_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+
+ def get_placement_targets_by_zonegroup(self, zonegroup_name: str):
+ rgw_get_placement_cmd = ['zonegroup', 'placement',
+ 'list', '--rgw-zonegroup', zonegroup_name]
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_edit_cmd, False)
+ exit_code, out, err = mgr.send_rgwadmin_command(rgw_get_placement_cmd)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to rename zone to {}'.format(zone_name), # noqa E501 #pylint: disable=line-too-long
+ raise DashboardException(e=err, msg='Unable to get placement targets',
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
+ return out
+
+ def add_placement_targets(self, zonegroup_name: str, placement_targets: List[Dict]):
+ rgw_add_placement_cmd = ['zonegroup', 'placement', 'add']
+ for placement_target in placement_targets:
+ cmd_add_placement_options = ['--rgw-zonegroup', zonegroup_name,
+ '--placement-id', placement_target['placement_id']]
+ if placement_target['tags']:
+ cmd_add_placement_options += ['--tags', placement_target['tags']]
+ rgw_add_placement_cmd += cmd_add_placement_options
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_add_placement_cmd)
+ if exit_code > 0:
+ raise DashboardException(e=err,
+ msg='Unable to add placement target {} to zonegroup {}'.format(placement_target['placement_id'], zonegroup_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+ storage_classes = placement_target['storage_class'].split(",") if placement_target['storage_class'] else [] # noqa E501 #pylint: disable=line-too-long
+ if storage_classes:
+ for sc in storage_classes:
+ cmd_add_placement_options = ['--storage-class', sc]
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(
+ rgw_add_placement_cmd + cmd_add_placement_options)
+ if exit_code > 0:
+ raise DashboardException(e=err,
+ msg='Unable to add placement target {} to zonegroup {}'.format(placement_target['placement_id'], zonegroup_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+
+ def modify_placement_targets(self, zonegroup_name: str, placement_targets: List[Dict]):
+ rgw_add_placement_cmd = ['zonegroup', 'placement', 'modify']
+ for placement_target in placement_targets:
+ cmd_add_placement_options = ['--rgw-zonegroup', zonegroup_name,
+ '--placement-id', placement_target['placement_id']]
+ if placement_target['tags']:
+ cmd_add_placement_options += ['--tags', placement_target['tags']]
+ rgw_add_placement_cmd += cmd_add_placement_options
+ storage_classes = placement_target['storage_class'].split(",") if placement_target['storage_class'] else [] # noqa E501 #pylint: disable=line-too-long
+ if storage_classes:
+ for sc in storage_classes:
+ cmd_add_placement_options = []
+ cmd_add_placement_options = ['--storage-class', sc]
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(
+ rgw_add_placement_cmd + cmd_add_placement_options)
+ if exit_code > 0:
+ raise DashboardException(e=err,
+ msg='Unable to add placement target {} to zonegroup {}'.format(placement_target['placement_id'], zonegroup_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+ else:
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_add_placement_cmd)
+ if exit_code > 0:
+ raise DashboardException(e=err,
+ msg='Unable to add placement target {} to zonegroup {}'.format(placement_target['placement_id'], zonegroup_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+
+ # pylint: disable=W0102
+ def edit_zonegroup(self, realm_name: str, zonegroup_name: str, new_zonegroup_name: str,
+ default: str = '', master: str = '', endpoints: str = '',
+ add_zones: List[str] = [], remove_zones: List[str] = [],
+ placement_targets: List[Dict[str, str]] = []):
+ rgw_zonegroup_edit_cmd = []
+ if new_zonegroup_name != zonegroup_name:
+ rgw_zonegroup_edit_cmd = ['zonegroup', 'rename', '--rgw-zonegroup', zonegroup_name,
+ '--zonegroup-new-name', new_zonegroup_name]
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_edit_cmd, False)
+ if exit_code > 0:
+ raise DashboardException(e=err, msg='Unable to rename zonegroup to {}'.format(new_zonegroup_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+ self.modify_zonegroup(realm_name, new_zonegroup_name, default, master, endpoints)
+ if add_zones:
+ for zone_name in add_zones:
+ self.add_or_remove_zone(new_zonegroup_name, zone_name, 'add')
+ if remove_zones:
+ for zone_name in remove_zones:
+ self.add_or_remove_zone(new_zonegroup_name, zone_name, 'remove')
+ existing_placement_targets = self.get_placement_targets_by_zonegroup(new_zonegroup_name)
+ existing_placement_targets_ids = [pt['key'] for pt in existing_placement_targets]
+ if placement_targets:
+ for pt in placement_targets:
+ if pt['placement_id'] in existing_placement_targets_ids:
+ self.modify_placement_targets(new_zonegroup_name, placement_targets)
+ else:
+ self.add_placement_targets(new_zonegroup_name, placement_targets)
- rgw_zonegroup_modify_cmd = ['zonegroup', 'modify',
- '--rgw-realm', realm_name,
- '--rgw-zonegroup', zonegroup_name]
- if zonegroup_endpoints:
- if len(zonegroup_endpoints) > 1:
- endpoint = ','.join(str(e) for e in zonegroup_endpoints)
- else:
- endpoint = zonegroup_endpoints[0]
- rgw_zonegroup_modify_cmd.append('--endpoints')
- rgw_zonegroup_modify_cmd.append(endpoint)
- rgw_zonegroup_modify_cmd.append('--master')
- rgw_zonegroup_modify_cmd.append('--default')
+ def update_period(self):
+ rgw_update_period_cmd = ['period', 'update', '--commit']
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zonegroup_modify_cmd)
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_update_period_cmd)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to modify zonegroup {}'.format(zonegroup_name), # noqa E501 #pylint: disable=line-too-long
+ raise DashboardException(e=err, msg='Unable to update period',
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- rgw_zone_modify_cmd = ['zone', 'modify', '--rgw-realm', realm_name,
- '--rgw-zonegroup', zonegroup_name,
- '--rgw-zone', zone_name]
- if zone_endpoints:
- if len(zone_endpoints) > 1:
- endpoint = ','.join(str(e) for e in zone_endpoints)
- else:
- endpoint = zone_endpoints[0]
- rgw_zone_modify_cmd.append('--endpoints')
- rgw_zone_modify_cmd.append(endpoint)
- rgw_zone_modify_cmd.append('--master')
- rgw_zone_modify_cmd.append('--default')
+ def create_zone(self, zone_name, zonegroup_name, default, master, endpoints, access_key,
+ secret_key):
+ rgw_zone_create_cmd = ['zone', 'create']
+ cmd_create_zone_options = ['--rgw-zone', zone_name]
+ if zonegroup_name != 'null':
+ cmd_create_zone_options.append('--rgw-zonegroup')
+ cmd_create_zone_options.append(zonegroup_name)
+ if default != 'false':
+ cmd_create_zone_options.append('--default')
+ if master != 'false':
+ cmd_create_zone_options.append('--master')
+ if endpoints != 'null':
+ cmd_create_zone_options.append('--endpoints')
+ cmd_create_zone_options.append(endpoints)
+ if access_key is not None:
+ cmd_create_zone_options.append('--access-key')
+ cmd_create_zone_options.append(access_key)
+ if secret_key is not None:
+ cmd_create_zone_options.append('--secret')
+ cmd_create_zone_options.append(secret_key)
+ rgw_zone_create_cmd += cmd_create_zone_options
try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_modify_cmd)
+ exit_code, out, err = mgr.send_rgwadmin_command(rgw_zone_create_cmd)
if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to modify zone',
+ raise DashboardException(e=err, msg='Unable to create zone',
http_status_code=500, component='rgw')
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
- if user:
- access_key, secret_key = self.get_rgw_user_keys(user, zone_name)
- rgw_zone_modify_cmd = ['zone', 'modify', '--rgw-zone', zone_name,
- '--access-key', access_key, '--secret', secret_key]
- try:
- exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_modify_cmd)
- if exit_code > 0:
- raise DashboardException(e=err, msg='Unable to modify zone',
- http_status_code=500, component='rgw')
- except SubprocessError as error:
- raise DashboardException(error, http_status_code=500, component='rgw')
self.update_period()
+ return out
- @RestClient.api_get('/{bucket_name}?versioning')
- def get_bucket_versioning(self, bucket_name, request=None):
- """
- Get bucket versioning.
- :param str bucket_name: the name of the bucket.
- :return: versioning info
- :rtype: Dict
- """
- # pylint: disable=unused-argument
- result = request()
- if 'Status' not in result:
- result['Status'] = 'Suspended'
- if 'MfaDelete' not in result:
- result['MfaDelete'] = 'Disabled'
- return result
-
- @RestClient.api_put('/{bucket_name}?versioning')
- def set_bucket_versioning(self, bucket_name, versioning_state, mfa_delete,
- mfa_token_serial, mfa_token_pin, request=None):
- """
- Set bucket versioning.
- :param str bucket_name: the name of the bucket.
- :param str versioning_state:
- https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTVersioningStatus.html
- :param str mfa_delete: MFA Delete state.
- :param str mfa_token_serial:
- https://docs.ceph.com/docs/master/radosgw/mfa/
- :param str mfa_token_pin: value of a TOTP token at a certain time (auth code)
- :return: None
- """
- # pylint: disable=unused-argument
- versioning_configuration = ET.Element('VersioningConfiguration')
- status_element = ET.SubElement(versioning_configuration, 'Status')
- status_element.text = versioning_state
-
- headers = {}
- if mfa_delete and mfa_token_serial and mfa_token_pin:
- headers['x-amz-mfa'] = '{} {}'.format(mfa_token_serial, mfa_token_pin)
- mfa_delete_element = ET.SubElement(versioning_configuration, 'MfaDelete')
- mfa_delete_element.text = mfa_delete
-
- data = ET.tostring(versioning_configuration, encoding='unicode')
-
- try:
- request(data=data, headers=headers)
- except RequestException as error:
- msg = str(error)
- if mfa_delete and mfa_token_serial and mfa_token_pin \
- and 'AccessDenied' in error.content.decode():
- msg = 'Bad MFA credentials: {}'.format(msg)
- raise DashboardException(msg=msg,
- http_status_code=error.status_code,
- component='rgw')
-
- @RestClient.api_get('/{bucket_name}?encryption')
- def get_bucket_encryption(self, bucket_name, request=None):
- # pylint: disable=unused-argument
- try:
- result = request() # type: ignore
- result['Status'] = 'Enabled'
- return result
- except RequestException as e:
- if e.content:
- content = json_str_to_object(e.content)
- if content.get(
- 'Code') == 'ServerSideEncryptionConfigurationNotFoundError':
- return {
- 'Status': 'Disabled',
- }
- raise e
-
- @RestClient.api_delete('/{bucket_name}?encryption')
- def delete_bucket_encryption(self, bucket_name, request=None):
- # pylint: disable=unused-argument
- result = request() # type: ignore
- return result
-
- @RestClient.api_put('/{bucket_name}?encryption')
- def set_bucket_encryption(self, bucket_name, key_id,
- sse_algorithm, request: Optional[object] = None):
- # pylint: disable=unused-argument
- encryption_configuration = ET.Element('ServerSideEncryptionConfiguration')
- rule_element = ET.SubElement(encryption_configuration, 'Rule')
- default_encryption_element = ET.SubElement(rule_element,
- 'ApplyServerSideEncryptionByDefault')
- sse_algo_element = ET.SubElement(default_encryption_element,
- 'SSEAlgorithm')
- sse_algo_element.text = sse_algorithm
- if sse_algorithm == 'aws:kms':
- kms_master_key_element = ET.SubElement(default_encryption_element,
- 'KMSMasterKeyID')
- kms_master_key_element.text = key_id
- data = ET.tostring(encryption_configuration, encoding='unicode')
- try:
- _ = request(data=data) # type: ignore
- except RequestException as e:
- raise DashboardException(msg=str(e), component='rgw')
-
- @RestClient.api_get('/{bucket_name}?object-lock')
- def get_bucket_locking(self, bucket_name, request=None):
- # type: (str, Optional[object]) -> dict
- """
- Gets the locking configuration for a bucket. The locking
- configuration will be applied by default to every new object
- placed in the specified bucket.
- :param bucket_name: The name of the bucket.
- :type bucket_name: str
- :return: The locking configuration.
- :rtype: Dict
- """
- # pylint: disable=unused-argument
-
- # Try to get the Object Lock configuration. If there is none,
- # then return default values.
- try:
- result = request() # type: ignore
- return {
- 'lock_enabled': dict_get(result, 'ObjectLockEnabled') == 'Enabled',
- 'lock_mode': dict_get(result, 'Rule.DefaultRetention.Mode'),
- 'lock_retention_period_days': dict_get(result, 'Rule.DefaultRetention.Days', 0),
- 'lock_retention_period_years': dict_get(result, 'Rule.DefaultRetention.Years', 0)
- }
- except RequestException as e:
- if e.content:
- content = json_str_to_object(e.content)
- if content.get(
- 'Code') == 'ObjectLockConfigurationNotFoundError':
- return {
- 'lock_enabled': False,
- 'lock_mode': 'compliance',
- 'lock_retention_period_days': None,
- 'lock_retention_period_years': None
- }
- raise e
-
- @RestClient.api_put('/{bucket_name}?object-lock')
- def set_bucket_locking(self,
- bucket_name: str,
- mode: str,
- retention_period_days: Optional[Union[int, str]] = None,
- retention_period_years: Optional[Union[int, str]] = None,
- request: Optional[object] = None) -> None:
- """
- Places the locking configuration on the specified bucket. The
- locking configuration will be applied by default to every new
- object placed in the specified bucket.
- :param bucket_name: The name of the bucket.
- :type bucket_name: str
- :param mode: The lock mode, e.g. `COMPLIANCE` or `GOVERNANCE`.
- :type mode: str
- :param retention_period_days:
- :type retention_period_days: int
- :param retention_period_years:
- :type retention_period_years: int
- :rtype: None
- """
- # pylint: disable=unused-argument
-
- retention_period_days, retention_period_years = self.perform_validations(
- retention_period_days, retention_period_years, mode)
-
- # Generate the XML data like this:
- # <ObjectLockConfiguration>
- # <ObjectLockEnabled>string</ObjectLockEnabled>
- # <Rule>
- # <DefaultRetention>
- # <Days>integer</Days>
- # <Mode>string</Mode>
- # <Years>integer</Years>
- # </DefaultRetention>
- # </Rule>
- # </ObjectLockConfiguration>
- locking_configuration = ET.Element('ObjectLockConfiguration')
- enabled_element = ET.SubElement(locking_configuration,
- 'ObjectLockEnabled')
- enabled_element.text = 'Enabled' # Locking can't be disabled.
- rule_element = ET.SubElement(locking_configuration, 'Rule')
- default_retention_element = ET.SubElement(rule_element,
- 'DefaultRetention')
- mode_element = ET.SubElement(default_retention_element, 'Mode')
- mode_element.text = mode.upper()
- if retention_period_days:
- days_element = ET.SubElement(default_retention_element, 'Days')
- days_element.text = str(retention_period_days)
- if retention_period_years:
- years_element = ET.SubElement(default_retention_element, 'Years')
- years_element.text = str(retention_period_years)
+ def parse_secrets(self, user, data):
+ for key in data.get('keys', []):
+ if key.get('user') == user:
+ access_key = key.get('access_key')
+ secret_key = key.get('secret_key')
+ return access_key, secret_key
+ return '', ''
- data = ET.tostring(locking_configuration, encoding='unicode')
+ def modify_zone(self, zone_name: str, zonegroup_name: str, default: str, master: str,
+ endpoints: str, access_key: str, secret_key: str):
+ rgw_zone_modify_cmd = ['zone', 'modify', '--rgw-zonegroup',
+ zonegroup_name, '--rgw-zone', zone_name]
+ if endpoints:
+ rgw_zone_modify_cmd.append('--endpoints')
+ rgw_zone_modify_cmd.append(endpoints)
+ if default and str_to_bool(default):
+ rgw_zone_modify_cmd.append('--default')
+ if master and str_to_bool(master):
+ rgw_zone_modify_cmd.append('--master')
+ if access_key is not None:
+ rgw_zone_modify_cmd.append('--access-key')
+ rgw_zone_modify_cmd.append(access_key)
+ if secret_key is not None:
+ rgw_zone_modify_cmd.append('--secret')
+ rgw_zone_modify_cmd.append(secret_key)
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_modify_cmd)
+ if exit_code > 0:
+ raise DashboardException(e=err, msg='Unable to modify zone',
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+ def add_placement_targets_zone(self, zone_name: str, placement_target: str, data_pool: str,
+ index_pool: str, data_extra_pool: str):
+ rgw_zone_add_placement_cmd = ['zone', 'placement', 'add', '--rgw-zone', zone_name,
+ '--placement-id', placement_target, '--data-pool', data_pool,
+ '--index-pool', index_pool,
+ '--data-extra-pool', data_extra_pool]
try:
- _ = request(data=data) # type: ignore
- except RequestException as e:
- raise DashboardException(msg=str(e), component='rgw')
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_add_placement_cmd)
+ if exit_code > 0:
+ raise DashboardException(e=err, msg='Unable to add placement target {} to zone {}'.format(placement_target, zone_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
- def list_roles(self) -> List[Dict[str, Any]]:
- rgw_list_roles_command = ['role', 'list']
- code, roles, err = mgr.send_rgwadmin_command(rgw_list_roles_command)
- if code < 0:
- logger.warning('Error listing roles with code %d: %s', code, err)
- return []
+ def add_storage_class_zone(self, zone_name: str, placement_target: str, storage_class: str,
+ data_pool: str, compression: str):
+ rgw_zone_add_storage_class_cmd = ['zone', 'placement', 'add', '--rgw-zone', zone_name,
+ '--placement-id', placement_target,
+ '--storage-class', storage_class,
+ '--data-pool', data_pool,
+ '--compression', compression]
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_add_storage_class_cmd)
+ if exit_code > 0:
+ raise DashboardException(e=err, msg='Unable to add storage class {} to zone {}'.format(storage_class, zone_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
- return roles
+ def edit_zone(self, zone_name: str, new_zone_name: str, zonegroup_name: str, default: str = '',
+ master: str = '', endpoints: str = '', access_key: str = '', secret_key: str = '',
+ placement_target: str = '', data_pool: str = '', index_pool: str = '',
+ data_extra_pool: str = '', storage_class: str = '', data_pool_class: str = '',
+ compression: str = ''):
+ if new_zone_name != zone_name:
+ rgw_zone_rename_cmd = ['zone', 'rename', '--rgw-zone',
+ zone_name, '--zone-new-name', new_zone_name]
+ try:
+ exit_code, _, err = mgr.send_rgwadmin_command(rgw_zone_rename_cmd, False)
+ if exit_code > 0:
+ raise DashboardException(e=err, msg='Unable to rename zone to {}'.format(new_zone_name), # noqa E501 #pylint: disable=line-too-long
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+ self.modify_zone(new_zone_name, zonegroup_name, default, master, endpoints, access_key,
+ secret_key)
+ self.add_placement_targets_zone(new_zone_name, placement_target,
+ data_pool, index_pool, data_extra_pool)
+ self.add_storage_class_zone(new_zone_name, placement_target, storage_class,
+ data_pool_class, compression)
- def create_role(self, role_name: str, role_path: str, role_assume_policy_doc: str) -> None:
+ def list_zones(self):
+ rgw_zone_list = {}
+ rgw_zone_list_cmd = ['zone', 'list']
try:
- json.loads(role_assume_policy_doc)
- except: # noqa: E722
- raise DashboardException('Assume role policy document is not a valid json')
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zone_list_cmd)
+ if exit_code > 0:
+ raise DashboardException(msg='Unable to fetch zone list',
+ http_status_code=500, component='rgw')
+ rgw_zone_list = out
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ return rgw_zone_list
- # valid values:
- # pylint: disable=C0301
- # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-path # noqa: E501
- if len(role_name) > 64:
- raise DashboardException(
- f'Role name "{role_name}" is invalid. Should be 64 characters or less')
+ def get_zone(self, zone_name: str):
+ zone_info = {}
+ rgw_zone_info_cmd = ['zone', 'get', '--rgw-zone', zone_name]
+ try:
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zone_info_cmd)
+ if exit_code > 0:
+ raise DashboardException('Unable to get zone info',
+ http_status_code=500, component='rgw')
+ zone_info = out
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ return zone_info
- role_name_regex = '[0-9a-zA-Z_+=,.@-]+'
- if not re.fullmatch(role_name_regex, role_name):
- raise DashboardException(
- f'Role name "{role_name}" is invalid. Valid characters are "{role_name_regex}"')
+ def get_all_zones_info(self):
+ all_zones_info = {}
+ zones_info = []
+ rgw_zone_list = self.list_zones()
+ if 'zones' in rgw_zone_list:
+ if rgw_zone_list['zones'] != []:
+ for rgw_zone in rgw_zone_list['zones']:
+ zone_info = self.get_zone(rgw_zone)
+ zones_info.append(zone_info)
+ all_zones_info['zones'] = zones_info # type: ignore
+ else:
+ all_zones_info['zones'] = []
+ if 'default_info' in rgw_zone_list and rgw_zone_list['default_info'] != '':
+ all_zones_info['default_zone'] = rgw_zone_list['default_info'] # type: ignore
+ else:
+ all_zones_info['default_zone'] = '' # type: ignore
+ return all_zones_info
- if not os.path.isabs(role_path):
- raise DashboardException(
- f'Role path "{role_path}" is invalid. It should be an absolute path')
- if role_path[-1] != '/':
- raise DashboardException(
- f'Role path "{role_path}" is invalid. It should start and end with a slash')
- path_regex = '(\u002F)|(\u002F[\u0021-\u007E]+\u002F)'
- if not re.fullmatch(path_regex, role_path):
- raise DashboardException(
- (f'Role path "{role_path}" is invalid.'
- f'Role path should follow the pattern "{path_regex}"'))
+ def delete_zone(self, zone_name: str, delete_pools: str, pools: List[str],
+ zonegroup_name: str = '',):
+ rgw_remove_zone_from_zonegroup_cmd = ['zonegroup', 'remove', '--rgw-zonegroup',
+ zonegroup_name, '--rgw-zone', zone_name]
+ rgw_delete_zone_cmd = ['zone', 'delete', '--rgw-zone', zone_name]
+ if zonegroup_name:
+ try:
+ exit_code, _, _ = mgr.send_rgwadmin_command(rgw_remove_zone_from_zonegroup_cmd)
+ if exit_code > 0:
+ raise DashboardException(msg='Unable to remove zone from zonegroup',
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+ try:
+ exit_code, _, _ = mgr.send_rgwadmin_command(rgw_delete_zone_cmd)
+ if exit_code > 0:
+ raise DashboardException(msg='Unable to delete zone',
+ http_status_code=500, component='rgw')
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ self.update_period()
+ if delete_pools == 'true':
+ self.delete_pools(pools)
- rgw_create_role_command = ['role', 'create', '--role-name', role_name, '--path', role_path]
- if role_assume_policy_doc:
- rgw_create_role_command += ['--assume-role-policy-doc', f"{role_assume_policy_doc}"]
+ def delete_pools(self, pools):
+ for pool in pools:
+ if mgr.rados.pool_exists(pool):
+ mgr.rados.delete_pool(pool)
- code, _roles, _err = mgr.send_rgwadmin_command(rgw_create_role_command,
- stdout_as_json=False)
- if code != 0:
- # pylint: disable=C0301
- link = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-path' # noqa: E501
- msg = (f'Error creating role with code {code}: '
- 'Looks like the document has a wrong format.'
- f' For more information about the format look at {link}')
- raise DashboardException(msg=msg, component='rgw')
+ def create_system_user(self, userName: str, zoneName: str):
+ rgw_user_create_cmd = ['user', 'create', '--uid', userName,
+ '--display-name', userName, '--rgw-zone', zoneName, '--system']
+ try:
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_user_create_cmd)
+ if exit_code > 0:
+ raise DashboardException(msg='Unable to create system user',
+ http_status_code=500, component='rgw')
+ return out
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
- def perform_validations(self, retention_period_days, retention_period_years, mode):
+ def get_user_list(self, zoneName: str):
+ all_users_info = []
+ user_list = []
+ rgw_user_list_cmd = ['user', 'list', '--rgw-zone', zoneName]
try:
- retention_period_days = int(retention_period_days) if retention_period_days else 0
- retention_period_years = int(retention_period_years) if retention_period_years else 0
- if retention_period_days < 0 or retention_period_years < 0:
- raise ValueError
- except (TypeError, ValueError):
- msg = "Retention period must be a positive integer."
- raise DashboardException(msg=msg, component='rgw')
- if retention_period_days and retention_period_years:
- # https://docs.aws.amazon.com/AmazonS3/latest/API/archive-RESTBucketPUTObjectLockConfiguration.html
- msg = "Retention period requires either Days or Years. "\
- "You can't specify both at the same time."
- raise DashboardException(msg=msg, component='rgw')
- if not retention_period_days and not retention_period_years:
- msg = "Retention period requires either Days or Years. "\
- "You must specify at least one."
- raise DashboardException(msg=msg, component='rgw')
- if not isinstance(mode, str) or mode.upper() not in ['COMPLIANCE', 'GOVERNANCE']:
- msg = "Retention mode must be either COMPLIANCE or GOVERNANCE."
- raise DashboardException(msg=msg, component='rgw')
- return retention_period_days, retention_period_years
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_user_list_cmd)
+ if exit_code > 0:
+ raise DashboardException('Unable to get user list',
+ http_status_code=500, component='rgw')
+ user_list = out
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+
+ if len(user_list) > 0:
+ for user_name in user_list:
+ rgw_user_info_cmd = ['user', 'info', '--uid', user_name, '--rgw-zone', zoneName]
+ try:
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_user_info_cmd)
+ if exit_code > 0:
+ raise DashboardException('Unable to get user info',
+ http_status_code=500, component='rgw')
+ all_users_info.append(out)
+ except SubprocessError as error:
+ raise DashboardException(error, http_status_code=500, component='rgw')
+ return all_users_info
+
+ def get_multisite_status(self):
+ is_multisite_configured = True
+ rgw_realm_list = self.list_realms()
+ rgw_zonegroup_list = self.list_zonegroups()
+ rgw_zone_list = self.list_zones()
+ if len(rgw_realm_list['realms']) < 1 and len(rgw_zonegroup_list['zonegroups']) < 1 \
+ and len(rgw_zone_list['zones']) < 1:
+ is_multisite_configured = False
+ return is_multisite_configured
@CLICommand('rgw realm tokens', perm='r')
def list_realm_tokens(self) -> HandleCommandResult:
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', '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})
+ realms_info = self.get_realm_tokens()
except RGWAMException as e:
self.log.error(f'cmd run exception: ({e.retcode}) {e.message}')
return HandleCommandResult(retval=e.retcode, stdout=e.stdout, stderr=e.stderr)
return HandleCommandResult(retval=0, stdout=json.dumps(realms_info, indent=4), stderr='')
+ def get_realm_tokens(self) -> List[Dict]:
+ 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', '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})
+ return realms_info
+
@CLICommand('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:
inbuf: Optional[str] = None) -> HandleCommandResult:
"""Bootstrap new rgw zone that syncs with zone on another cluster in the same realm"""
+ created_zones = self.rgw_zone_create(zone_name, realm_token, port, placement,
+ start_radosgw, zone_endpoints, inbuf)
+
+ return HandleCommandResult(retval=0, stdout=f"Zones {', '.join(created_zones)} created successfully")
+
+ def rgw_zone_create(self,
+ zone_name: Optional[str] = None,
+ realm_token: Optional[str] = None,
+ port: Optional[int] = None,
+ placement: Optional[str] = None,
+ start_radosgw: Optional[bool] = True,
+ zone_endpoints: Optional[str] = None,
+ inbuf: Optional[str] = None) -> Any:
if inbuf:
try:
rgw_specs = self._parse_rgw_specs(inbuf)
RGWAM(self.env).zone_create(rgw_spec, start_radosgw)
if rgw_spec.rgw_zone is not None:
created_zones.append(rgw_spec.rgw_zone)
+ return created_zones
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)
-
- return HandleCommandResult(retval=0, stdout=f"Zones {', '.join(created_zones)} created successfully")
+ return created_zones
@CLICommand('rgw realm reconcile', perm='rw')
def _cmd_rgw_realm_reconcile(self,
self.log.info('Stopping')
self.run = False
self.event.set()
+
+ def import_realm_token(self,
+ zone_name: Optional[str] = None,
+ realm_token: Optional[str] = None,
+ port: Optional[int] = None,
+ placement: Optional[str] = None,
+ start_radosgw: Optional[bool] = True,
+ zone_endpoints: Optional[str] = None) -> None:
+ self.rgw_zone_create(zone_name, realm_token, port, placement, start_radosgw,
+ zone_endpoints)