import cherrypy
+from .. import mgr
from ..exceptions import DashboardException
from ..rest_client import RequestException
from ..security import Permission, Scope
from ..services.ceph_service import CephService
from ..services.rgw_client import NoRgwDaemonsException, RgwClient
from ..tools import json_str_to_object, str_to_bool
-from . import APIDoc, APIRouter, BaseController, CRUDCollectionMethod, \
- CRUDEndpoint, Endpoint, EndpointDoc, ReadPermission, RESTController, \
- UIRouter, allow_empty_body
-from ._crud import CRUDMeta, Form, FormField, FormTaskInfo, Icon, MethodType, \
- TableAction, Validator, VerticalContainer
+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
from ._version import APIVersion
logger = logging.getLogger("controllers.rgw")
raise DashboardException(e, http_status_code=404, component='rgw')
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
+
@APIRouter('/rgw/daemon', Scope.RGW)
@APIDoc("RGW Daemon Management API", "RgwDaemon")
@allow_empty_body
# pylint: disable=W0613
def create(self, zone_name, zonegroup_name=None, default=False, master=False,
- zone_endpoints=None, user=None, daemon_name=None):
+ 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)
+ 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')
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 = '',
+ 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')
+
+ @Endpoint()
+ @ReadPermission
+ def get_pool_names(self):
+ pool_names = []
+ ret, out, _ = mgr.check_mon_command({
+ 'prefix': 'osd lspools',
+ 'format': 'json',
+ })
+ if ret == 0 and out is not None:
+ pool_names = json.loads(out)
+ return pool_names
+
+ @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')
+
+ @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')
placement_pools: any[];
realm_id: string;
notif_pool: string;
+ endpoints: string[];
}
</div>
<div class="card">
<div class="card-header"
- i18n>Multi-site Topology viewer</div>
+ i18n>Topology Viewer</div>
<div class="card-body">
<div class="row">
<div class="col-sm-6 col-lg-6 tree-container">
<i [ngClass]="node.data.icon"></i>
{{ node.data.name }}
</span>
- <span *ngIf="node.data.type"
- class="badge badge-info me-3">
- {{ node.data.type }}
- </span>
<span class="badge badge-success me-2"
*ngIf="node.data.is_default">
default
<button type="button"
class="btn btn-light dropdown-toggle-split ms-1"
(click)="openModal(node, true)"
- [disabled]="getDisable()"
- ngbDropdownToggle>
+ [disabled]="getDisable()">
<i [ngClass]="[icons.edit]"></i>
</button>
<button type="button"
<div class="col-sm-6 col-lg-6 metadata"
*ngIf="metadata">
<legend>{{ metadataTitle }}</legend>
- <cd-table-key-value [data]="metadata"></cd-table-key-value>
+ <cd-table-key-value cdTableDetail
+ [data]="metadata"
+ [renderObjects]="true"
+ [customCss]="customCss">
+ </cd-table-key-value>
</div>
</div>
</div>
it('should display right title', () => {
const span = debugElement.nativeElement.querySelector('.card-header');
- expect(span.textContent).toBe('Multi-site Topology viewer');
+ expect(span.textContent).toBe('Topology Viewer');
});
});
id="default_realm"
name="default_realm"
formControlName="default_realm"
+ [attr.disabled]="action === 'edit' ? true: null"
type="checkbox">
<label class="form-check-label"
for="default_realm"
i18n>Default</label>
- <cd-helper *ngIf="multisiteRealmForm.get('default_realm').disabled">
- <span i18n>You cannot unset the default flag. Please create another realm and set it as default.</span>
+ <cd-helper *ngIf="action === 'edit' && info.data.is_default">
+ <span i18n>You cannot unset the default flag.</span>
+ </cd-helper>
+ <cd-helper *ngIf="action === 'edit' && !info.data.is_default">
+ <span i18n>Please consult the <a href="{{ docUrl }}">documentation</a> to follow the failover mechanism</span>
+ </cd-helper>
+ <cd-helper *ngIf="defaultRealmDisabled && action === 'create'">
+ <span i18n>Default realm already exists.</span>
</cd-helper>
</div>
</div>
import { CdValidators } from '~/app/shared/forms/cd-validators';
import { NotificationService } from '~/app/shared/services/notification.service';
import { RgwRealm } from '../models/rgw-multisite';
+import { DocService } from '~/app/shared/services/doc.service';
@Component({
selector: 'cd-rgw-multisite-realm-form',
multisiteInfo: object[] = [];
realm: RgwRealm;
realmList: RgwRealm[] = [];
+ zonegroupList: RgwRealm[] = [];
realmNames: string[];
newRealmName: string;
+ isMaster: boolean;
+ defaultsInfo: string[];
+ defaultRealmDisabled = false;
+ docUrl: string;
constructor(
public activeModal: NgbActiveModal,
public actionLabels: ActionLabelsI18n,
public rgwRealmService: RgwRealmService,
- public notificationService: NotificationService
+ public notificationService: NotificationService,
+ public docService: DocService
) {
this.action = this.editing
? this.actionLabels.EDIT + this.resource
return realm['name'];
});
if (this.action === 'edit') {
+ this.zonegroupList =
+ this.multisiteInfo[1] !== undefined && this.multisiteInfo[1].hasOwnProperty('zonegroups')
+ ? this.multisiteInfo[1]['zonegroups']
+ : [];
this.multisiteRealmForm.get('realmName').setValue(this.info.data.name);
this.multisiteRealmForm.get('default_realm').setValue(this.info.data.is_default);
if (this.info.data.is_default) {
this.multisiteRealmForm.get('default_realm').disable();
}
}
+ this.zonegroupList.forEach((zgp: any) => {
+ if (zgp.is_master === true && zgp.realm_id === this.info.data.id) {
+ this.isMaster = true;
+ }
+ });
+ if (this.defaultsInfo && this.defaultsInfo['defaultRealmName'] !== null) {
+ this.multisiteRealmForm.get('default_realm').disable();
+ this.defaultRealmDisabled = true;
+ }
+ this.docUrl = this.docService.urlGenerator('rgw-multisite');
}
submit() {
- const values = this.multisiteRealmForm.value;
+ const values = this.multisiteRealmForm.getRawValue();
this.realm = new RgwRealm();
if (this.action === 'create') {
this.realm.name = values['realmName'];
<div class="cd-col-form-input">
<select class="form-select"
id="selectedZonegroup"
+ [attr.disabled]="action === 'edit' ? true : null"
formControlName="selectedZonegroup"
name="selectedZonegroup"
(change)="onZoneGroupChange($event.target.value)">
<div class="form-group row">
<label class="cd-col-form-label required"
for="zonegroupName"
- i18n>ZoneName</label>
+ i18n>Zone Name</label>
<div class="cd-col-form-input">
<input class="form-control"
type="text"
id="default_zone"
name="default_zone"
formControlName="default_zone"
+ [attr.disabled]="action === 'edit' ? true : null"
type="checkbox">
<label class="form-check-label"
for="default_zone"
- i18n>Default</label><br>
+ i18n>Default</label>
+ <span *ngIf="disableDefault && action === 'create'">
+ <cd-helper i18n>Default zone can only exist in a default zonegroup.
+ </cd-helper>
+ </span>
+ <span *ngIf="isDefaultZone">
+ <cd-helper i18n>You cannot unset the default flag.
+ </cd-helper>
+ </span>
+ <cd-helper *ngIf="action === 'edit' && !isDefaultZone">
+ <span i18n>Please consult the <a href="{{ docUrl }}">documentation</a> to follow the failover mechanism</span>
+ </cd-helper><br>
+ </div>
+ <div class="custom-control custom-checkbox">
<input class="form-check-input"
id="master_zone"
name="master_zone"
formControlName="master_zone"
+ [attr.disabled]="action === 'edit' ? true : null"
type="checkbox">
<label class="form-check-label"
for="master_zone"
i18n>Master</label>
+ <span *ngIf="disableMaster">
+ <cd-helper i18n>Master zone already exists for the selected zonegroup.
+ </cd-helper>
+ </span>
+ <span *ngIf="isMasterZone">
+ <cd-helper i18n>You cannot unset the master flag.
+ </cd-helper>
+ </span>
+ <cd-helper *ngIf="action === 'edit' && !isMasterZone">
+ <span i18n>Please consult the <a href="{{ docUrl }}">documentation</a> to follow the failover mechanism</span>
+ </cd-helper>
</div>
</div>
</div>
i18n>Please enter a valid IP address.</span>
</div>
</div>
- <div class="form-group row">
+ <div class="form-group row"
+ *ngIf="action === 'edit'">
<label class="cd-col-form-label"
for="users"
i18n>System User</label>
[ngValue]="null">-- Select a user --</option>
<option *ngFor="let user of users"
[value]="user.user_id">{{ user.user_id }}</option>
- </select>
+ </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>
+ </div>
+ <div *ngIf="action === 'edit'">
+ <legend>Placement Targets</legend>
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="placementTarget"
+ i18n>Placement target</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="placementTarget"
+ formControlName="placementTarget"
+ name="placementTarget"
+ (change)="getZonePlacementData($event.target.value)">
+ <option *ngFor="let placement of placementTargets"
+ [value]="placement.name"
+ [selected]="placement.name === multisiteZoneForm.getValue('placementTarget')">
+ {{ placement.name }}
+ </option>
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="placementDataPool"
+ i18n>Data pool</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="placementDataPool"
+ formControlName="placementDataPool"
+ [value]="placementDataPool"
+ name="placementDataPool">
+ <option *ngFor="let pool of poolList"
+ [value]="pool.poolname"
+ [selected]="pool.poolname === multisiteZoneForm.getValue('placementDataPool')">
+ {{ pool.poolname }}
+ </option>
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="placementIndexPool"
+ i18n>Index pool</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="placementIndexPool"
+ formControlName="placementIndexPool"
+ name="placementIndexPool">
+ <option *ngFor="let pool of poolList"
+ [value]="pool.poolname"
+ [selected]="pool.poolname === multisiteZoneForm.getValue('placementIndexPool')">
+ {{ pool.poolname }}
+ </option>
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="placementDataExtraPool"
+ i18n>Data extra pool</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="placementDataExtraPool"
+ formControlName="placementDataExtraPool"
+ name="placementDataExtraPool">
+ <option *ngFor="let pool of poolList"
+ [value]="pool.poolname"
+ [selected]="pool.poolname === multisiteZoneForm.getValue('placementDataExtraPool')">
+ {{ pool.poolname }}
+ </option>
+ </select>
+ </div>
+ </div>
+ <div>
+ <legend>Storage Classes</legend>
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="storageClass"
+ i18n>Storage Class</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="storageClass"
+ formControlName="storageClass"
+ (change)="getStorageClassData($event.target.value)"
+ name="storageClass">
+ <option *ngFor="let str of storageClassList"
+ [value]="str.value">
+ {{ str.value }}
+ </option>
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="storageDataPool"
+ i18n>Data pool</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="storageDataPool"
+ formControlName="storageDataPool"
+ name="storageDataPool">
+ <option *ngFor="let pool of poolList"
+ [value]="pool.poolname"
+ [selected]="pool.poolname === multisiteZoneForm.getValue('storageDataPool')">
+ {{ pool.poolname }}
+ </option>
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="storageCompression"
+ i18n>Compression</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="storageCompression"
+ formControlName="storageCompression"
+ name="storageCompression">
+ <option *ngFor="let compression of compressionTypes"
+ [value]="compression">
+ {{ compression }}
+ </option>
+ </select>
+ </div>
+ </div>
+ </div>
</div>
</div>
</div>
import { Component, OnInit } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
+import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
import { RgwUserService } from '~/app/shared/api/rgw-user.service';
import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
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 { ModalService } from '~/app/shared/services/modal.service';
+import { RgwSystemUserComponent } from '../rgw-system-user/rgw-system-user.component';
@Component({
selector: 'cd-rgw-multisite-zone-form',
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;
action: string;
+ info: any;
multisiteZoneForm: CdFormGroup;
editing = false;
resource: string;
zonegroupList: RgwZonegroup[] = [];
zoneList: RgwZone[] = [];
zoneNames: string[];
- users: string[];
+ users: any;
+ placementTargets: any;
+ zoneInfo: RgwZone;
+ poolList: object[] = [];
+ storageClassList: object[] = [];
+ disableDefault: boolean = false;
+ disableMaster: boolean = false;
+ isMetadataSync: boolean = false;
+ isMasterZone: boolean;
+ isDefaultZone: boolean;
+ syncStatusTimedOut: boolean = false;
+ bsModalRef: NgbModalRef;
+ createSystemUser: boolean = false;
+ master_zone_of_master_zonegroup: RgwZone;
+ masterZoneUser: any;
+ access_key: any;
+ master_zonegroup_of_realm: RgwZonegroup;
+ compressionTypes = ['lz4', 'zlib', 'snappy'];
constructor(
public activeModal: NgbActiveModal,
public actionLabels: ActionLabelsI18n,
+ public rgwMultisiteService: RgwMultisiteService,
public rgwZoneService: RgwZoneService,
public rgwZoneGroupService: RgwZonegroupService,
public notificationService: NotificationService,
- public rgwUserService: RgwUserService
+ public rgwUserService: RgwUserService,
+ public modalService: ModalService
) {
this.action = this.editing
? this.actionLabels.EDIT + this.resource
validators: [
Validators.required,
CdValidators.custom('uniqueName', (zoneName: string) => {
- return this.zoneNames && this.zoneNames.indexOf(zoneName) !== -1;
+ return (
+ this.action === 'create' && this.zoneNames && this.zoneNames.indexOf(zoneName) !== -1
+ );
})
]
}),
default_zone: new FormControl(false),
master_zone: new FormControl(false),
selectedZonegroup: new FormControl(null),
- zone_endpoints: new FormControl(null, {
+ zone_endpoints: new FormControl([], {
validators: [
CdValidators.custom('endpoint', (value: string) => {
if (_.isEmpty(value)) {
}
return false;
}
- })
+ }),
+ Validators.required
]
}),
- users: new FormControl(null)
+ users: new FormControl(null),
+ placementTarget: new FormControl(null),
+ placementDataPool: new FormControl(''),
+ placementIndexPool: new FormControl(null),
+ placementDataExtraPool: new FormControl(null),
+ storageClass: new FormControl(null),
+ storageDataPool: new FormControl(null),
+ storageCompression: new FormControl(null)
});
}
if (_.isEmpty(zonegroup.master_zone)) {
this.multisiteZoneForm.get('master_zone').setValue(true);
this.multisiteZoneForm.get('master_zone').disable();
+ this.disableMaster = false;
+ } else if (!_.isEmpty(zonegroup.master_zone) && this.action === 'create') {
+ this.multisiteZoneForm.get('master_zone').setValue(false);
+ 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.defaultsInfo['defaultZonegroupName']
+ ) {
+ this.disableDefault = true;
+ this.multisiteZoneForm.get('default_zone').disable();
+ }
}
ngOnInit(): void {
return zone['name'];
});
if (this.action === 'create') {
+ if (this.defaultsInfo['defaultZonegroupName'] !== undefined) {
+ this.multisiteZoneForm
+ .get('selectedZonegroup')
+ .setValue(this.defaultsInfo['defaultZonegroupName']);
+ this.onZoneGroupChange(this.defaultsInfo['defaultZonegroupName']);
+ }
+ }
+ if (this.action === 'edit') {
+ this.placementTargets = this.info.parent ? this.info.parent.data.placement_targets : [];
+ this.rgwZoneService.getPoolNames().subscribe((pools: object[]) => {
+ this.poolList = pools;
+ });
+ this.multisiteZoneForm.get('zoneName').setValue(this.info.data.name);
+ 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('selectedZonegroup')
- .setValue(this.defaultsInfo['defaultZonegroupName']);
- this.onZoneGroupChange(this.defaultsInfo['defaultZonegroupName']);
+ .get('placementTarget')
+ .setValue(this.info.parent.data.default_placement);
+ this.getZonePlacementData(this.multisiteZoneForm.getValue('placementTarget'));
+ if (this.info.data.is_default) {
+ this.isDefaultZone = true;
+ this.multisiteZoneForm.get('default_zone').disable();
+ }
+ if (this.info.data.is_master) {
+ this.isMasterZone = true;
+ this.multisiteZoneForm.get('master_zone').disable();
+ }
+ const zone = new RgwZone();
+ zone.name = this.info.data.name;
+ this.onZoneGroupChange(this.info.data.parent);
+ setTimeout(() => {
+ this.getUserInfo(zone);
+ }, 1500);
+ }
+ if (
+ this.multisiteZoneForm.getValue('selectedZonegroup') !==
+ this.defaultsInfo['defaultZonegroupName']
+ ) {
+ this.disableDefault = true;
+ this.multisiteZoneForm.get('default_zone').disable();
}
- this.rgwUserService.list().subscribe((users: any) => {
- this.users = users.filter((user: any) => user.keys.length !== 0);
- });
}
- submit() {
- const values = this.multisiteZoneForm.value;
- this.zonegroup = new RgwZonegroup();
- this.zonegroup.name = values['selectedZonegroup'];
- this.zone = new RgwZone();
- this.zone.name = values['zoneName'];
+ getUserInfo(zone: RgwZone) {
this.rgwZoneService
- .create(
- this.zone,
- this.zonegroup,
- values['default_zone'],
- values['master_zone'],
- values['zone_endpoints'],
- values['users']
- )
- .subscribe(
- () => {
- this.notificationService.show(
- NotificationType.success,
- $localize`Zone: '${values['zoneName']}' created successfully`
- );
- this.activeModal.close();
- },
- () => {
- this.multisiteZoneForm.setErrors({ cdSubmitButton: true });
+ .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;
+ if (this.placementTargets) {
+ this.placementTargets.forEach((placement: any) => {
+ if (placement.name === placementTarget) {
+ let storageClasses = placement.storage_classes;
+ this.storageClassList = Object.entries(storageClasses).map(([key, value]) => ({
+ key,
+ value
+ }));
}
- );
+ });
+ }
+ this.rgwZoneService.get(this.zone).subscribe((zoneInfo: RgwZone) => {
+ this.zoneInfo = zoneInfo;
+ if (this.zoneInfo && this.zoneInfo['placement_pools']) {
+ this.zoneInfo['placement_pools'].forEach((plc_pool) => {
+ if (plc_pool.key === placementTarget) {
+ let storageClasses = plc_pool.val.storage_classes;
+ let placementDataPool = storageClasses['STANDARD']
+ ? storageClasses['STANDARD']['data_pool']
+ : '';
+ let placementIndexPool = plc_pool.val.index_pool;
+ let placementDataExtraPool = plc_pool.val.data_extra_pool;
+ this.poolList.push({ poolname: placementDataPool });
+ this.poolList.push({ poolname: placementIndexPool });
+ this.poolList.push({ poolname: placementDataExtraPool });
+ this.multisiteZoneForm.get('storageClass').setValue(this.storageClassList[0]['value']);
+ this.multisiteZoneForm.get('storageDataPool').setValue(placementDataPool);
+ this.multisiteZoneForm.get('storageCompression').setValue(this.compressionTypes[0]);
+ this.multisiteZoneForm.get('placementDataPool').setValue(placementDataPool);
+ this.multisiteZoneForm.get('placementIndexPool').setValue(placementIndexPool);
+ this.multisiteZoneForm.get('placementDataExtraPool').setValue(placementDataExtraPool);
+ }
+ });
+ }
+ });
+ }
+
+ getStorageClassData(storageClass: string) {
+ let storageClassSelected = this.storageClassList.find((x) => x['value'] == storageClass)[
+ 'value'
+ ];
+ this.poolList.push({ poolname: storageClassSelected.data_pool });
+ this.multisiteZoneForm.get('storageDataPool').setValue(storageClassSelected.data_pool);
+ this.multisiteZoneForm
+ .get('storageCompression')
+ .setValue(storageClassSelected.compression_type);
+ }
+
+ submit() {
+ const values = this.multisiteZoneForm.getRawValue();
+ if (this.action === 'create') {
+ this.zonegroup = new RgwZonegroup();
+ 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.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
+ )
+ .subscribe(
+ () => {
+ this.notificationService.show(
+ NotificationType.success,
+ $localize`Zone: '${values['zoneName']}' created successfully`
+ );
+ this.activeModal.close();
+ },
+ () => {
+ this.multisiteZoneForm.setErrors({ cdSubmitButton: true });
+ }
+ );
+ } else if (this.action === 'edit') {
+ this.zonegroup = new RgwZonegroup();
+ 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['zonegroup_endpoints']
+ : this.checkUrlArray(values['zone_endpoints']);
+ this.rgwZoneService
+ .update(
+ this.zone,
+ this.zonegroup,
+ values['zoneName'],
+ 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
+ )
+ .subscribe(
+ () => {
+ this.notificationService.show(
+ NotificationType.success,
+ $localize`Zone: '${values['zoneName']}' updated successfully`
+ );
+ this.activeModal.close();
+ },
+ () => {
+ this.multisiteZoneForm.setErrors({ cdSubmitButton: true });
+ }
+ );
+ }
+ }
+
+ checkUrlArray(endpoints: string) {
+ let endpointsArray = [];
+ if (endpoints.includes(',')) {
+ endpointsArray = endpoints.split(',');
+ } else {
+ endpointsArray.push(endpoints);
+ }
+ 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);
+ });
}
}
id="default_zonegroup"
name="default_zonegroup"
formControlName="default_zonegroup"
+ [attr.disabled]="action === 'edit' ? true : null"
type="checkbox">
<label class="form-check-label"
for="default_zonegroup"
i18n>Default</label>
- <span *ngIf="disableDefault">
- <cd-helper i18n>Zonegroup doesn't belong to the default realm.</cd-helper>
- </span><br>
+ <span *ngIf="disableDefault && action === 'create'">
+ <cd-helper i18n>Zonegroup doesn't belong to the default realm.</cd-helper>
+ </span>
+ <cd-helper *ngIf="action === 'edit' && !info.data.is_default">
+ <span i18n>Please consult the <a href="{{ docUrl }}">documentation</a> to follow the failover mechanism</span>
+ </cd-helper>
+ <cd-helper *ngIf="action === 'edit' && info.data.is_default">
+ <span i18n>You cannot unset the default flag.</span>
+ </cd-helper><br>
<input class="form-check-input"
id="master_zonegroup"
name="master_zonegroup"
formControlName="master_zonegroup"
+ [attr.disabled]="action === 'edit' ? true : null"
type="checkbox">
<label class="form-check-label"
for="master_zonegroup"
i18n>Master</label>
- <span *ngIf="!isMaster">
- <cd-helper i18n>RGW multi-site configuration must have a master zonegroup. Setting
- the first zonegroup created as master, to avoid any errors on udating the period.
- Can be modified later by editing a zonegroup.
- </cd-helper>
- </span>
- <span *ngIf="disableMaster">
+ <span *ngIf="disableMaster && action === 'create'">
<cd-helper i18n>Multiple master zonegroups can't be configured. If you want to create a new zonegroup and make it the master zonegroup, you must delete the default zonegroup.</cd-helper>
</span>
+ <cd-helper *ngIf="action === 'edit' && !info.data.is_master">
+ <span i18n>Please consult the <a href="{{ docUrl }}">documentation</a> to follow the failover mechanism</span>
+ </cd-helper>
+ <cd-helper *ngIf="action === 'edit' && info.data.is_master">
+ <span i18n>You cannot unset the master flag.</span>
+ </cd-helper>
</div>
</div>
</div>
this.zonegroupNames = this.zonegroupList.map((zonegroup) => {
return zonegroup['name'];
});
+ let allZonegroupZonesList = this.zonegroupList.map((zonegroup: RgwZonegroup) => {
+ return zonegroup['zones'];
+ });
+ const allZonegroupZonesInfo = allZonegroupZonesList.reduce(
+ (accumulator, value) => accumulator.concat(value),
+ []
+ );
+ const allZonegroupZonesNames = allZonegroupZonesInfo.map((zone) => {
+ return zone['name'];
+ });
this.allZoneNames = this.zoneList.map((zone: RgwZone) => {
return zone['name'];
});
+ this.allZoneNames = _.difference(this.allZoneNames, allZonegroupZonesNames);
if (this.action === 'create' && this.defaultsInfo['defaultRealmName'] !== null) {
this.multisiteZonegroupForm
.get('selectedRealm')
--- /dev/null
+<cd-modal [modalRef]="activeModal">
+ <ng-container i18n="form title"
+ class="modal-title">Create System User</ng-container>
+
+ <ng-container class="modal-content">
+ <form name="multisiteSystemUserForm"
+ #formDir="ngForm"
+ [formGroup]="multisiteSystemUserForm"
+ novalidate>
+ <div class="modal-body">
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="userName"
+ i18n>User Name</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="User name..."
+ id="userName"
+ name="userName"
+ formControlName="userName">
+ <span class="invalid-feedback"
+ *ngIf="multisiteSystemUserForm.showError('userName', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteSystemUserForm.showError('userName', formDir, 'uniqueName')"
+ i18n>The chosen realm name is already in use.</span>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="multisiteSystemUserForm"></cd-form-button-panel>
+ </div>
+ </form>
+ </ng-container>
+</cd-modal>
--- /dev/null
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RgwSystemUserComponent } from './rgw-system-user.component';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ToastrModule } from 'ngx-toastr';
+import { SharedModule } from '~/app/shared/shared.module';
+
+describe('RgwSystemUserComponent', () => {
+ let component: RgwSystemUserComponent;
+ let fixture: ComponentFixture<RgwSystemUserComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ SharedModule,
+ ReactiveFormsModule,
+ RouterTestingModule,
+ HttpClientTestingModule,
+ ToastrModule.forRoot()
+ ],
+ declarations: [RgwSystemUserComponent],
+ providers: [NgbActiveModal]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RgwSystemUserComponent);
+ 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 { RgwZoneService } from '~/app/shared/api/rgw-zone.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 { NotificationService } from '~/app/shared/services/notification.service';
+
+@Component({
+ selector: 'cd-rgw-system-user',
+ templateUrl: './rgw-system-user.component.html',
+ styleUrls: ['./rgw-system-user.component.scss']
+})
+export class RgwSystemUserComponent {
+ multisiteSystemUserForm: CdFormGroup;
+ zoneName: string;
+
+ @Output()
+ submitAction = new EventEmitter();
+
+ constructor(
+ public activeModal: NgbActiveModal,
+ public actionLabels: ActionLabelsI18n,
+ public rgwZoneService: RgwZoneService,
+ public notificationService: NotificationService
+ ) {
+ this.createForm();
+ }
+
+ createForm() {
+ this.multisiteSystemUserForm = new CdFormGroup({
+ userName: new FormControl(null, {
+ validators: [Validators.required]
+ })
+ });
+ }
+
+ submit() {
+ const userName = this.multisiteSystemUserForm.getValue('userName');
+ this.rgwZoneService.createSystemUser(userName, this.zoneName).subscribe(() => {
+ this.submitAction.emit();
+ this.notificationService.show(
+ NotificationType.success,
+ $localize`User: '${this.multisiteSystemUserForm.getValue('userName')}' created successfully`
+ );
+ this.activeModal.close();
+ });
+ }
+}
import { RgwMultisiteZoneFormComponent } from './rgw-multisite-zone-form/rgw-multisite-zone-form.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 { RgwSystemUserComponent } from './rgw-system-user/rgw-system-user.component';
@NgModule({
imports: [
RgwMultisiteZonegroupFormComponent,
RgwMultisiteZoneFormComponent,
RgwMultisiteZoneDeletionFormComponent,
- RgwMultisiteZonegroupDeletionFormComponent
+ RgwMultisiteZonegroupDeletionFormComponent,
+ RgwSystemUserComponent
]
})
export class RgwModule {}
--- /dev/null
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+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`);
+ });
+ }
+}
defaultZone: boolean,
master: boolean,
endpoints: Array<string>,
- user: string
+ user: string,
+ createSystemUser: boolean,
+ master_zone_of_master_zonegroup: RgwZone
) {
+ 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,
default: defaultZone,
master: master,
zone_endpoints: endpoints,
- user: user
+ user: user,
+ createSystemUser: createSystemUser,
+ master_zone_of_master_zonegroup: master_zone_name
});
return this.http.post(`${this.url}`, null, { params: params });
});
});
}
+ update(
+ zone: RgwZone,
+ zonegroup: RgwZonegroup,
+ newZoneName: string,
+ defaultZone?: boolean,
+ master?: boolean,
+ endpoints?: Array<string>,
+ user?: string,
+ placementTarget?: string,
+ dataPool?: string,
+ indexPool?: string,
+ dataExtraPool?: string,
+ storageClass?: string,
+ dataPoolClass?: string,
+ compression?: string,
+ master_zone_of_master_zonegroup?: RgwZone
+ ) {
+ 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);
+ });
+ }
+
getZoneTree(zone: RgwZone, defaultZoneId: string, zonegroup?: RgwZonegroup, realm?: RgwRealm) {
let nodes = {};
let zoneIds = [];
nodes['id'] = zone.id;
zoneIds.push(zone.id);
nodes['name'] = zone.name;
+ nodes['type'] = 'zone';
+ nodes['name'] = zone.name;
nodes['info'] = zone;
nodes['icon'] = Icons.deploy;
+ nodes['zone_zonegroup'] = zonegroup;
nodes['parent'] = zonegroup ? zonegroup.name : '';
nodes['second_parent'] = realm ? realm.name : '';
nodes['is_default'] = zone.id === defaultZoneId ? true : false;
+ nodes['endpoints'] = zone.endpoints;
nodes['is_master'] = zonegroup && zonegroup.master_zone === zone.id ? true : false;
nodes['type'] = 'zone';
return {
zoneIds: zoneIds
};
}
+
+ getPoolNames() {
+ return this.rgwDaemonService.request(() => {
+ 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);
+ });
+ }
+
+ 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 });
+ });
+ }
}
realm: RgwRealm,
zonegroup: RgwZonegroup,
newZonegroupName: string,
- defaultZonegroup: boolean,
- master: boolean,
- removedZones: string[],
- addedZones: string[]
+ defaultZonegroup?: boolean,
+ master?: boolean,
+ removedZones?: string[],
+ addedZones?: string[]
) {
return this.rgwDaemonService.request((requestBody: any) => {
requestBody = {
nodes['master_zone'] = zonegroup.master_zone;
nodes['zones'] = zonegroup.zones;
nodes['placement_targets'] = zonegroup.placement_targets;
+ nodes['default_placement'] = zonegroup.default_placement;
return nodes;
}
}
'nfs-ganesha': `${domain}mgr/dashboard/#configuring-nfs-ganesha-in-the-dashboard`,
'rgw-nfs': `${domain}radosgw/nfs`,
rgw: `${domain}mgr/dashboard/#enabling-the-object-gateway-management-frontend`,
+ 'rgw-multisite': `${domain}/radosgw/multisite/#failover-and-disaster-recovery`,
dashboard: `${domain}mgr/dashboard`,
grafana: `${domain}mgr/dashboard/#enabling-the-embedding-of-grafana-dashboards`,
orch: `${domain}mgr/orchestrator`,
application/json:
schema:
properties:
+ createSystemUser:
+ default: false
+ type: boolean
daemon_name:
type: string
default:
master:
default: false
type: boolean
+ master_zone_of_master_zonegroup:
+ type: string
user:
type: string
zone_endpoints:
- jwt: []
tags:
- RgwZone
+ /api/rgw/zone/create_system_user:
+ put:
+ parameters: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ properties:
+ daemon_name:
+ type: string
+ userName:
+ type: string
+ zoneName:
+ type: string
+ required:
+ - userName
+ - zoneName
+ 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:
+ - RgwZone
/api/rgw/zone/get_all_zones_info:
get:
parameters: []
- jwt: []
tags:
- RgwZone
+ /api/rgw/zone/get_pool_names:
+ 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:
+ - RgwZone
+ /api/rgw/zone/get_user_list:
+ get:
+ parameters:
+ - allowEmptyValue: true
+ in: query
+ name: daemon_name
+ schema:
+ type: string
+ - allowEmptyValue: true
+ in: query
+ name: zoneName
+ schema:
+ type: string
+ 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:
+ - RgwZone
/api/rgw/zone/{zone_name}:
delete:
parameters:
- jwt: []
tags:
- RgwZone
+ put:
+ parameters:
+ - in: path
+ name: zone_name
+ required: true
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ properties:
+ compression:
+ default: ''
+ type: string
+ daemon_name:
+ type: string
+ data_extra_pool:
+ default: ''
+ type: string
+ data_pool:
+ default: ''
+ type: string
+ data_pool_class:
+ default: ''
+ type: string
+ default:
+ default: ''
+ type: string
+ index_pool:
+ default: ''
+ type: string
+ master:
+ default: ''
+ type: string
+ master_zone_of_master_zonegroup:
+ type: string
+ new_zone_name:
+ type: string
+ placement_target:
+ default: ''
+ type: string
+ storage_class:
+ default: ''
+ type: string
+ user:
+ default: ''
+ type: string
+ zone_endpoints:
+ default: []
+ type: string
+ zonegroup_name:
+ type: string
+ required:
+ - new_zone_name
+ - zonegroup_name
+ 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:
+ - RgwZone
/api/rgw/zonegroup:
get:
parameters:
pytest-cov
pytest-instafail
pyfakefs==4.5.0
-jsonschema==4.16.0
+jsonschema
import logging
import os
import re
+import subprocess
import xml.etree.ElementTree as ET # noqa: N814
from subprocess import SubprocessError
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:
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
self.update_period()
- return
- 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()
def modify_placement_targets(self, zonegroup_name: str, placement_targets: List[Dict]):
rgw_add_placement_cmd = ['zonegroup', 'placement', 'modify']
zone_info = self.get_zone(zone['name'])
self.delete_zone_pools(zone['name'], zone_info)
- def create_zone(self, zone_name, zonegroup_name, default, master, endpoints, user):
+ 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 = _get_user_keys(user)
+ 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
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)
+ 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
+ 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)
+ 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:
+ 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 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()
+
+ 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]
+ 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, 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']
is_multisite_configured = False
return is_multisite_configured
+ def get_multisite_sync_status(self):
+ sync_status = ''
+ rgw_sync_status_cmd = ['sync', 'status']
+ try:
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_sync_status_cmd, False)
+ if exit_code > 0:
+ raise DashboardException('Unable to get sync status',
+ http_status_code=500, component='rgw')
+ sync_status = out
+ except subprocess.TimeoutExpired:
+ sync_status = 'Timeout Expired'
+ return sync_status
+
+ 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 get_user_list(self, zoneName: str):
+ all_users_info = []
+ user_list = []
+ rgw_user_list_cmd = ['user', 'list', '--rgw-zone', zoneName]
+ try:
+ 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
+
@RestClient.api_get('/{bucket_name}?versioning')
def get_bucket_versioning(self, bucket_name, request=None):
"""