return status
+@UIRouter('/rgw/multisite')
+class RgwStatus(BaseController):
+ @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')
+ return status
+
+
@APIRouter('/rgw/daemon', Scope.RGW)
@APIDoc("RGW Daemon Management API", "RgwDaemon")
class RgwDaemon(RESTController):
CreateDate: str
MaxSessionDuration: int
AssumeRolePolicyDocument: str
+
+
+@APIRouter('/rgw/realm', Scope.RGW)
+class RgwRealm(RESTController):
+ @allow_empty_body
+ # pylint: disable=W0613
+ def create(self, realm_name, default, daemon_name=None):
+ try:
+ instance = RgwClient.admin_instance()
+ result = instance.create_realm(realm_name, default)
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def list(self, daemon_name=None):
+ try:
+ instance = RgwClient.admin_instance()
+ result = instance.list_realms()
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def get(self, realm_name, daemon_name=None):
+ try:
+ instance = RgwClient.admin_instance()
+ result = instance.get_realm(realm_name)
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @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')
+
+
+@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()
+ 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')
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def list(self, daemon_name=None):
+ try:
+ instance = RgwClient.admin_instance()
+ result = instance.list_zonegroups()
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def get(self, zonegroup_name, daemon_name=None):
+ try:
+ instance = RgwClient.admin_instance()
+ result = instance.get_zonegroup(zonegroup_name)
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @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')
+
+
+@APIRouter('/rgw/zone', Scope.RGW)
+class RgwZone(RESTController):
+ @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):
+ try:
+ instance = RgwClient.admin_instance()
+ result = instance.create_zone(zone_name, zonegroup_name, default,
+ master, zone_endpoints, user)
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def list(self, daemon_name=None):
+ try:
+ instance = RgwClient.admin_instance()
+ result = instance.list_zones()
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @allow_empty_body
+ # pylint: disable=W0613
+ def get(self, zone_name, daemon_name=None):
+ try:
+ instance = RgwClient.admin_instance()
+ result = instance.get_zone(zone_name)
+ return result
+ except NoRgwDaemonsException as e:
+ raise DashboardException(e, http_status_code=404, component='rgw')
+
+ @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')
// Object Gateway
{
path: 'rgw',
- canActivateChild: [FeatureTogglesGuardService, ModuleStatusGuardService],
+ canActivate: [FeatureTogglesGuardService, ModuleStatusGuardService],
data: {
moduleStatusGuardConfig: {
uiApiPath: 'rgw',
--- /dev/null
+export class RgwRealm {
+ id: string;
+ name: string;
+ current_period: string;
+ epoch: number;
+}
+
+export class RgwZonegroup {
+ id: string;
+ name: string;
+ api_name: string;
+ is_master: boolean;
+ endpoints: string[];
+ hostnames: string[];
+ hostnames_s3website: string[];
+ master_zone: string;
+ zones: RgwZone[];
+ placement_targets: any[];
+ default_placement: string;
+ realm_id: string;
+ sync_policy: object;
+ enabled_features: string[];
+}
+
+export class RgwZone {
+ id: string;
+ name: string;
+ domain_root: string;
+ control_pool: string;
+ gc_pool: string;
+ lc_pool: string;
+ log_pool: string;
+ intent_log_pool: string;
+ usage_log_pool: string;
+ roles_pool: string;
+ reshard_pool: string;
+ user_keys_pool: string;
+ user_email_pool: string;
+ user_swift_pool: string;
+ user_uid_pool: string;
+ otp_pool: string;
+ system_key: object;
+ placement_pools: any[];
+ realm_id: string;
+ notif_pool: string;
+}
--- /dev/null
+<div class="row">
+ <div class="col-sm-12 col-lg-12">
+ <div>
+ <cd-table-actions class="btn-group mb-4"
+ [permission]="permission"
+ [selection]="selection"
+ [tableActions]="createTableActions">
+ </cd-table-actions>
+ </div>
+ <div class="card">
+ <div class="card-header"
+ i18n>Multi-site Topology viewer</div>
+ <div class="card-body">
+ <div class="row">
+ <div class="col-sm-6 col-lg-6 tree-container">
+ <i *ngIf="loadingIndicator"
+ [ngClass]="[icons.large, icons.spinner, icons.spin]"></i>
+ <tree-root #tree
+ [nodes]="nodes"
+ [options]="treeOptions"
+ (updateData)="onUpdateData()">
+ <ng-template #treeNodeTemplate
+ let-node>
+ <span *ngIf="node.data.name"
+ class="me-3">
+ <i [ngClass]="node.data.icon"></i>
+ {{ node.data.name }}
+ </span>
+ <span class="badge badge-success me-2"
+ *ngIf="node.data.is_default">
+ default
+ </span>
+ <span class="badge badge-info me-2"
+ *ngIf="node.data.is_master">
+ master
+ </span>
+ </ng-template>
+ </tree-root>
+ </div>
+ <div class="col-sm-6 col-lg-6 metadata"
+ *ngIf="metadata">
+ <legend>{{ metadataTitle }}</legend>
+ <cd-table-key-value [data]="metadata"></cd-table-key-value>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
--- /dev/null
+@use './src/styles/vendor/variables' as vv;
+
+.tree-container {
+ height: calc(100vh - vv.$tree-container-height);
+}
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { DebugElement } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { TreeModule } from '@circlon/angular-tree-component';
+import { SharedModule } from '~/app/shared/shared.module';
+
+import { RgwMultisiteDetailsComponent } from './rgw-multisite-details.component';
+
+describe('RgwMultisiteDetailsComponent', () => {
+ let component: RgwMultisiteDetailsComponent;
+ let fixture: ComponentFixture<RgwMultisiteDetailsComponent>;
+ let debugElement: DebugElement;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [RgwMultisiteDetailsComponent],
+ imports: [HttpClientTestingModule, TreeModule, SharedModule]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RgwMultisiteDetailsComponent);
+ component = fixture.componentInstance;
+ debugElement = fixture.debugElement;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should display right title', () => {
+ const span = debugElement.nativeElement.querySelector('.card-header');
+ expect(span.textContent).toBe('Multi-site Topology viewer');
+ });
+});
--- /dev/null
+import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
+import {
+ TreeComponent,
+ ITreeOptions,
+ TreeModel,
+ TreeNode,
+ TREE_ACTIONS
+} from '@circlon/angular-tree-component';
+import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { forkJoin, Subscription } 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 { ActionLabelsI18n, TimerServiceInterval } from '~/app/shared/constants/app.constants';
+import { Icons } from '~/app/shared/enum/icons.enum';
+import { CdTableAction } from '~/app/shared/models/cd-table-action';
+import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
+import { Permission } from '~/app/shared/models/permissions';
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
+import { ModalService } from '~/app/shared/services/modal.service';
+import { TimerService } from '~/app/shared/services/timer.service';
+import { RgwRealm, RgwZone, RgwZonegroup } from '../models/rgw-multisite';
+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';
+
+@Component({
+ selector: 'cd-rgw-multisite-details',
+ templateUrl: './rgw-multisite-details.component.html',
+ styleUrls: ['./rgw-multisite-details.component.scss']
+})
+export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
+ private sub = new Subscription();
+
+ @ViewChild('tree') tree: TreeComponent;
+
+ messages = {
+ noDefaultRealm: $localize`Please create a default realm first to enable this feature`
+ };
+
+ icons = Icons;
+ permission: Permission;
+ selection = new CdTableSelection();
+ createTableActions: CdTableAction[];
+ loadingIndicator = true;
+ nodes: object[] = [];
+ treeOptions: ITreeOptions = {
+ useVirtualScroll: true,
+ nodeHeight: 22,
+ levelPadding: 20,
+ actionMapping: {
+ mouse: {
+ click: this.onNodeSelected.bind(this)
+ }
+ }
+ };
+
+ realms: RgwRealm[] = [];
+ zonegroups: RgwZonegroup[] = [];
+ zones: RgwZone[] = [];
+ metadata: any;
+ metadataTitle: string;
+ bsModalRef: NgbModalRef;
+ realmIds: string[] = [];
+ zoneIds: string[] = [];
+ defaultRealmId = '';
+ defaultZonegroupId = '';
+ defaultZoneId = '';
+ multisiteInfo: object[] = [];
+ defaultsInfo: string[] = [];
+
+ constructor(
+ private modalService: ModalService,
+ private timerService: TimerService,
+ private authStorageService: AuthStorageService,
+ public actionLabels: ActionLabelsI18n,
+ public timerServiceVariable: TimerServiceInterval,
+ public rgwRealmService: RgwRealmService,
+ public rgwZonegroupService: RgwZonegroupService,
+ public rgwZoneService: RgwZoneService
+ ) {
+ 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')
+ };
+ this.createTableActions = [createRealmAction, createZonegroupAction, createZoneAction];
+ }
+
+ openModal(entity: any, edit = false) {
+ const entityName = edit ? entity.data.name : entity;
+ const action = edit ? 'edit' : 'create';
+ const initialState = {
+ resource: entityName,
+ action: action,
+ info: entity,
+ defaultsInfo: this.defaultsInfo,
+ multisiteInfo: this.multisiteInfo
+ };
+ if (entityName === 'realm') {
+ this.bsModalRef = this.modalService.show(RgwMultisiteRealmFormComponent, initialState, {
+ size: 'lg'
+ });
+ } else if (entityName === 'zonegroup') {
+ this.bsModalRef = this.modalService.show(RgwMultisiteZonegroupFormComponent, initialState, {
+ size: 'lg'
+ });
+ } else {
+ this.bsModalRef = this.modalService.show(RgwMultisiteZoneFormComponent, initialState, {
+ size: 'lg'
+ });
+ }
+ }
+
+ ngOnInit() {
+ const observables = [
+ this.rgwRealmService.getAllRealmsInfo(),
+ this.rgwZonegroupService.getAllZonegroupsInfo(),
+ this.rgwZoneService.getAllZonesInfo()
+ ];
+ this.sub = this.timerService
+ .get(() => forkJoin(observables), this.timerServiceVariable.TIMER_SERVICE_PERIOD * 2)
+ .subscribe(
+ (multisiteInfo: [object, object, object]) => {
+ this.multisiteInfo = multisiteInfo;
+ this.loadingIndicator = false;
+ this.nodes = this.abstractTreeData(multisiteInfo);
+ },
+ (_error) => {}
+ );
+ }
+
+ ngOnDestroy() {
+ this.sub.unsubscribe();
+ }
+
+ private abstractTreeData(multisiteInfo: [object, object, object]): any[] {
+ let allNodes: object[] = [];
+ let rootNodes = {};
+ let firstChildNodes = {};
+ let allFirstChildNodes = [];
+ let secondChildNodes = {};
+ let allSecondChildNodes: {}[] = [];
+ this.realms = multisiteInfo[0]['realms'];
+ this.zonegroups = multisiteInfo[1]['zonegroups'];
+ this.zones = multisiteInfo[2]['zones'];
+ 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.realms.length > 0) {
+ // get tree for realm -> zonegroup -> zone
+ for (const realm of this.realms) {
+ const result = this.rgwRealmService.getRealmTree(realm, this.defaultRealmId);
+ rootNodes = result['nodes'];
+ this.realmIds = this.realmIds.concat(result['realmIds']);
+ for (const zonegroup of this.zonegroups) {
+ if (zonegroup.realm_id === realm.id) {
+ firstChildNodes = this.rgwZonegroupService.getZonegroupTree(
+ zonegroup,
+ this.defaultZonegroupId,
+ realm
+ );
+ for (const zone of zonegroup.zones) {
+ const zoneResult = this.rgwZoneService.getZoneTree(
+ zone,
+ this.defaultZoneId,
+ zonegroup,
+ realm
+ );
+ secondChildNodes = zoneResult['nodes'];
+ this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
+ allSecondChildNodes.push(secondChildNodes);
+ secondChildNodes = {};
+ }
+ firstChildNodes['children'] = allSecondChildNodes;
+ allSecondChildNodes = [];
+ allFirstChildNodes.push(firstChildNodes);
+ firstChildNodes = {};
+ }
+ }
+ rootNodes['children'] = allFirstChildNodes;
+ allNodes.push(rootNodes);
+ firstChildNodes = {};
+ secondChildNodes = {};
+ rootNodes = {};
+ allFirstChildNodes = [];
+ allSecondChildNodes = [];
+ }
+ }
+ if (this.zonegroups.length > 0) {
+ // get tree for zonegroup -> zone (standalone zonegroups that don't match a realm eg(initial default))
+ for (const zonegroup of this.zonegroups) {
+ 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);
+ firstChildNodes = zoneResult['nodes'];
+ this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
+ allFirstChildNodes.push(firstChildNodes);
+ firstChildNodes = {};
+ }
+ rootNodes['children'] = allFirstChildNodes;
+ allNodes.push(rootNodes);
+ firstChildNodes = {};
+ rootNodes = {};
+ allFirstChildNodes = [];
+ }
+ }
+ }
+ if (this.zones.length > 0) {
+ // 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);
+ rootNodes = zoneResult['nodes'];
+ allNodes.push(rootNodes);
+ rootNodes = {};
+ }
+ }
+ }
+ if (this.realms.length < 1 && this.zonegroups.length < 1 && this.zones.length < 1) {
+ return [
+ {
+ name: 'No nodes!'
+ }
+ ];
+ }
+ this.realmIds = [];
+ this.zoneIds = [];
+ return allNodes;
+ }
+
+ getDefaultsEntities(
+ defaultRealmId: string,
+ defaultZonegroupId: string,
+ defaultZoneId: string
+ ): any {
+ const defaultRealm = this.realms.find((x: { id: string }) => x.id === defaultRealmId);
+ const defaultZonegroup = this.zonegroups.find(
+ (x: { id: string }) => x.id === defaultZonegroupId
+ );
+ const defaultZone = this.zones.find((x: { id: string }) => x.id === defaultZoneId);
+ const defaultRealmName = defaultRealm !== undefined ? defaultRealm.name : null;
+ const defaultZonegroupName = defaultZonegroup !== undefined ? defaultZonegroup.name : null;
+ const defaultZoneName = defaultZone !== undefined ? defaultZone.name : null;
+ return {
+ defaultRealmName: defaultRealmName,
+ defaultZonegroupName: defaultZonegroupName,
+ defaultZoneName: defaultZoneName
+ };
+ }
+
+ onNodeSelected(tree: TreeModel, node: TreeNode) {
+ TREE_ACTIONS.ACTIVATE(tree, node, true);
+ this.metadataTitle = node.data.name;
+ this.metadata = node.data.info;
+ node.data.show = true;
+ }
+
+ onUpdateData() {
+ this.tree.treeModel.expandAll();
+ }
+
+ getDisable() {
+ if (this.defaultRealmId === '') {
+ return this.messages.noDefaultRealm;
+ } else {
+ return false;
+ }
+ }
+}
--- /dev/null
+<cd-modal [modalRef]="activeModal">
+ <ng-container i18n="form title"
+ class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
+
+ <ng-container class="modal-content">
+ <form name="multisiteRealmForm"
+ #formDir="ngForm"
+ [formGroup]="multisiteRealmForm"
+ novalidate>
+ <div class="modal-body">
+ <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="multisiteRealmForm.showError('realmName', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteRealmForm.showError('realmName', formDir, 'uniqueName')"
+ i18n>The chosen realm name is already in use.</span>
+ <div class="custom-control custom-checkbox">
+ <input class="form-check-input"
+ id="default_realm"
+ name="default_realm"
+ formControlName="default_realm"
+ type="checkbox">
+ <label class="form-check-label"
+ for="default_realm"
+ i18n>Default</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="multisiteRealmForm"
+ [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"></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 { RgwMultisiteRealmFormComponent } from './rgw-multisite-realm-form.component';
+
+describe('RgwMultisiteRealmFormComponent', () => {
+ let component: RgwMultisiteRealmFormComponent;
+ let fixture: ComponentFixture<RgwMultisiteRealmFormComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ SharedModule,
+ ReactiveFormsModule,
+ RouterTestingModule,
+ HttpClientTestingModule,
+ ToastrModule.forRoot()
+ ],
+ providers: [NgbActiveModal],
+ declarations: [RgwMultisiteRealmFormComponent]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RgwMultisiteRealmFormComponent);
+ 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 { RgwRealm } from '../models/rgw-multisite';
+
+@Component({
+ selector: 'cd-rgw-multisite-realm-form',
+ templateUrl: './rgw-multisite-realm-form.component.html',
+ styleUrls: ['./rgw-multisite-realm-form.component.scss']
+})
+export class RgwMultisiteRealmFormComponent implements OnInit {
+ action: string;
+ multisiteRealmForm: CdFormGroup;
+ editing = false;
+ resource: string;
+ multisiteInfo: object[] = [];
+ realm: RgwRealm;
+ realmList: RgwRealm[] = [];
+ realmNames: string[];
+
+ constructor(
+ public activeModal: NgbActiveModal,
+ public actionLabels: ActionLabelsI18n,
+ public rgwRealmService: RgwRealmService,
+ public notificationService: NotificationService
+ ) {
+ this.action = this.editing
+ ? this.actionLabels.EDIT + this.resource
+ : this.actionLabels.CREATE + this.resource;
+ this.createForm();
+ }
+
+ createForm() {
+ this.multisiteRealmForm = new CdFormGroup({
+ realmName: new FormControl(null, {
+ validators: [
+ Validators.required,
+ CdValidators.custom('uniqueName', (realmName: string) => {
+ return this.realmNames && this.realmNames.indexOf(realmName) !== -1;
+ })
+ ]
+ }),
+ default_realm: new FormControl(false)
+ });
+ }
+
+ ngOnInit(): void {
+ this.realmList =
+ this.multisiteInfo[0] !== undefined && this.multisiteInfo[0].hasOwnProperty('realms')
+ ? this.multisiteInfo[0]['realms']
+ : [];
+ this.realmNames = this.realmList.map((realm) => {
+ return realm['name'];
+ });
+ }
+
+ submit() {
+ const values = this.multisiteRealmForm.value;
+ this.realm = new RgwRealm();
+ this.realm.name = values['realmName'];
+ this.rgwRealmService.create(this.realm, values['default_realm']).subscribe(
+ () => {
+ this.notificationService.show(
+ NotificationType.success,
+ $localize`Realm: '${values['realmName']}' created successfully`
+ );
+ this.activeModal.close();
+ },
+ () => {
+ this.multisiteRealmForm.setErrors({ cdSubmitButton: true });
+ }
+ );
+ }
+}
--- /dev/null
+<cd-modal [modalRef]="activeModal">
+ <ng-container i18n="form title"
+ class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
+
+ <ng-container class="modal-content">
+ <form name="multisiteZoneForm"
+ #formDir="ngForm"
+ [formGroup]="multisiteZoneForm"
+ novalidate>
+ <div class="modal-body">
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="selectedZonegroup"
+ i18n>Select Zonegroup</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="selectedZonegroup"
+ formControlName="selectedZonegroup"
+ name="selectedZonegroup">
+ <option *ngFor="let zonegroupName of zonegroupList"
+ [value]="zonegroupName.name"
+ [selected]="zonegroupName.name === multisiteZoneForm.getValue('selectedZonegroup')">
+ {{ zonegroupName.name }}
+ </option>
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zonegroupName"
+ i18n>ZoneName</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="multisiteZoneForm.showError('zoneName', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteZoneForm.showError('zoneName', formDir, 'uniqueName')"
+ i18n>The chosen zone name is already in use.</span>
+ <div class="custom-control custom-checkbox">
+ <input class="form-check-input"
+ id="default_zone"
+ name="default_zone"
+ formControlName="default_zone"
+ type="checkbox">
+ <label class="form-check-label"
+ for="default_zone"
+ i18n>Default</label><br>
+ <input class="form-check-input"
+ id="master_zone"
+ name="master_zone"
+ formControlName="master_zone"
+ type="checkbox">
+ <label class="form-check-label"
+ for="master_zone"
+ i18n>Master</label>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zone_endpoints"
+ i18n>Endpoints</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="e.g, http://ceph-node-00.com:80"
+ id="zone_endpoints"
+ name="zone_endpoints"
+ formControlName="zone_endpoints">
+ <span class="invalid-feedback"
+ *ngIf="multisiteZoneForm.showError('zone_endpoints', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteZoneForm.showError('zone_endpoints', formDir, 'endpoint')"
+ i18n>Please enter a valid IP address.</span>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="users"
+ i18n>System User</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>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="multisiteZoneForm"
+ [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"></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 { RgwMultisiteZoneFormComponent } from './rgw-multisite-zone-form.component';
+
+describe('RgwMultisiteZoneFormComponent', () => {
+ let component: RgwMultisiteZoneFormComponent;
+ let fixture: ComponentFixture<RgwMultisiteZoneFormComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ SharedModule,
+ ReactiveFormsModule,
+ RouterTestingModule,
+ HttpClientTestingModule,
+ ToastrModule.forRoot()
+ ],
+ providers: [NgbActiveModal],
+ declarations: [RgwMultisiteZoneFormComponent]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RgwMultisiteZoneFormComponent);
+ 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 _ from 'lodash';
+import { RgwUserService } from '~/app/shared/api/rgw-user.service';
+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 { CdValidators } from '~/app/shared/forms/cd-validators';
+import { NotificationService } from '~/app/shared/services/notification.service';
+import { RgwRealm, RgwZone, RgwZonegroup } from '../models/rgw-multisite';
+
+@Component({
+ selector: 'cd-rgw-multisite-zone-form',
+ templateUrl: './rgw-multisite-zone-form.component.html',
+ styleUrls: ['./rgw-multisite-zone-form.component.scss']
+})
+export class RgwMultisiteZoneFormComponent 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;
+ action: string;
+ multisiteZoneForm: CdFormGroup;
+ editing = false;
+ resource: string;
+ realm: RgwRealm;
+ zonegroup: RgwZonegroup;
+ zone: RgwZone;
+ defaultsInfo: string[] = [];
+ multisiteInfo: object[] = [];
+ zonegroupList: RgwZonegroup[] = [];
+ zoneList: RgwZone[] = [];
+ zoneNames: string[];
+ users: string[];
+
+ constructor(
+ public activeModal: NgbActiveModal,
+ public actionLabels: ActionLabelsI18n,
+ public rgwZoneService: RgwZoneService,
+ public notificationService: NotificationService,
+ public rgwUserService: RgwUserService
+ ) {
+ this.action = this.editing
+ ? this.actionLabels.EDIT + this.resource
+ : this.actionLabels.CREATE + this.resource;
+ this.createForm();
+ }
+
+ createForm() {
+ this.multisiteZoneForm = new CdFormGroup({
+ zoneName: new FormControl(null, {
+ validators: [
+ Validators.required,
+ CdValidators.custom('uniqueName', (zoneName: string) => {
+ return 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, {
+ validators: [
+ CdValidators.custom('endpoint', (value: string) => {
+ if (_.isEmpty(value)) {
+ return false;
+ } else {
+ if (value.includes(',')) {
+ value.split(',').forEach((url: string) => {
+ return (
+ !this.endpoints.test(url) && !this.ipv4Rgx.test(url) && !this.ipv6Rgx.test(url)
+ );
+ });
+ } else {
+ return (
+ !this.endpoints.test(value) &&
+ !this.ipv4Rgx.test(value) &&
+ !this.ipv6Rgx.test(value)
+ );
+ }
+ return false;
+ }
+ })
+ ]
+ }),
+ users: new FormControl(null)
+ });
+ }
+
+ ngOnInit(): void {
+ 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.zoneNames = this.zoneList.map((zone) => {
+ return zone['name'];
+ });
+ if (this.action === 'create') {
+ this.multisiteZoneForm
+ .get('selectedZonegroup')
+ .setValue(this.defaultsInfo['defaultZonegroupName']);
+ }
+ 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'];
+ 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 });
+ }
+ );
+ }
+}
--- /dev/null
+<cd-modal [modalRef]="activeModal">
+ <ng-container i18n="form title"
+ class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
+
+ <ng-container class="modal-content">
+ <form name="multisiteZonegroupForm"
+ #formDir="ngForm"
+ [formGroup]="multisiteZonegroupForm"
+ novalidate>
+ <div class="modal-body">
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="selectedRealm"
+ i18n>Select Realm</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="selectedRealm"
+ formControlName="selectedRealm"
+ name="selectedRealm">
+ <option ngValue=""
+ i18n>-- Select a realm --</option>
+ <option *ngFor="let realmName of realmList"
+ [value]="realmName.name"
+ [selected]="realmName.name === multisiteZonegroupForm.getValue('selectedRealm')">
+ {{ realmName.name }}
+ </option>
+ </select>
+ </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="multisiteZonegroupForm.showError('zonegroupName', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteZonegroupForm.showError('zonegroupName', formDir, 'uniqueName')"
+ i18n>The chosen zonegroup name is already in use.</span>
+ <div class="custom-control custom-checkbox">
+ <input class="form-check-input"
+ id="default_zonegroup"
+ name="default_zonegroup"
+ formControlName="default_zonegroup"
+ type="checkbox">
+ <label class="form-check-label"
+ for="default_zonegroup"
+ i18n>Default</label><br>
+ <input class="form-check-input"
+ id="master_zonegroup"
+ name="master_zonegroup"
+ formControlName="master_zonegroup"
+ type="checkbox">
+ <label class="form-check-label"
+ for="master_zonegroup"
+ i18n>Master</label>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zonegroup_endpoints"
+ i18n>Endpoints</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="e.g, http://ceph-node-00.com:80"
+ id="zonegroup_endpoints"
+ name="zonegroup_endpoints"
+ formControlName="zonegroup_endpoints">
+ <span class="invalid-feedback"
+ *ngIf="multisiteZonegroupForm.showError('zonegroup_endpoints', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteZonegroupForm.showError('zonegroup_endpoints', formDir, 'endpoint')"
+ i18n>Please enter a valid IP address.</span>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="multisiteZonegroupForm"
+ [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"></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 { RgwMultisiteZonegroupFormComponent } from './rgw-multisite-zonegroup-form.component';
+
+describe('RgwMultisiteZonegroupFormComponent', () => {
+ let component: RgwMultisiteZonegroupFormComponent;
+ let fixture: ComponentFixture<RgwMultisiteZonegroupFormComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ SharedModule,
+ ReactiveFormsModule,
+ RouterTestingModule,
+ HttpClientTestingModule,
+ ToastrModule.forRoot()
+ ],
+ providers: [NgbActiveModal],
+ declarations: [RgwMultisiteZonegroupFormComponent]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RgwMultisiteZonegroupFormComponent);
+ 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 _ from 'lodash';
+import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.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 { RgwRealm, RgwZonegroup } from '../models/rgw-multisite';
+
+@Component({
+ selector: 'cd-rgw-multisite-zonegroup-form',
+ templateUrl: './rgw-multisite-zonegroup-form.component.html',
+ styleUrls: ['./rgw-multisite-zonegroup-form.component.scss']
+})
+export class RgwMultisiteZonegroupFormComponent 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;
+ action: string;
+ multisiteZonegroupForm: CdFormGroup;
+ editing = false;
+ resource: string;
+ realm: RgwRealm;
+ zonegroup: RgwZonegroup;
+ defaultsInfo: string[] = [];
+ multisiteInfo: object[] = [];
+ realmList: RgwRealm[] = [];
+ zonegroupList: RgwZonegroup[] = [];
+ zonegroupNames: string[];
+
+ constructor(
+ public activeModal: NgbActiveModal,
+ public actionLabels: ActionLabelsI18n,
+ public rgwZonegroupService: RgwZonegroupService,
+ public notificationService: NotificationService
+ ) {
+ this.action = this.editing
+ ? this.actionLabels.EDIT + this.resource
+ : this.actionLabels.CREATE + this.resource;
+ this.createForm();
+ }
+
+ createForm() {
+ this.multisiteZonegroupForm = new CdFormGroup({
+ default_zonegroup: new FormControl(false),
+ zonegroupName: new FormControl(null, {
+ validators: [
+ Validators.required,
+ CdValidators.custom('uniqueName', (zonegroupName: string) => {
+ return this.zonegroupNames && this.zonegroupNames.indexOf(zonegroupName) !== -1;
+ })
+ ]
+ }),
+ master_zonegroup: new FormControl(false),
+ selectedRealm: new FormControl(null),
+ zonegroup_endpoints: new FormControl(null, [
+ CdValidators.custom('endpoint', (value: string) => {
+ if (_.isEmpty(value)) {
+ return false;
+ } else {
+ if (value.includes(',')) {
+ value.split(',').forEach((url: string) => {
+ return (
+ !this.endpoints.test(url) && !this.ipv4Rgx.test(url) && !this.ipv6Rgx.test(url)
+ );
+ });
+ } else {
+ return (
+ !this.endpoints.test(value) &&
+ !this.ipv4Rgx.test(value) &&
+ !this.ipv6Rgx.test(value)
+ );
+ }
+ return false;
+ }
+ }),
+ Validators.required
+ ])
+ });
+ }
+
+ ngOnInit(): void {
+ 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.zonegroupNames = this.zonegroupList.map((zonegroup) => {
+ return zonegroup['name'];
+ });
+ if (this.action === 'create' && this.defaultsInfo['defaultRealmName'] !== null) {
+ this.multisiteZonegroupForm
+ .get('selectedRealm')
+ .setValue(this.defaultsInfo['defaultRealmName']);
+ }
+ }
+
+ submit() {
+ const values = this.multisiteZonegroupForm.value;
+ this.realm = new RgwRealm();
+ this.realm.name = values['selectedRealm'];
+ this.zonegroup = new RgwZonegroup();
+ this.zonegroup.name = values['zonegroupName'];
+ this.zonegroup.endpoints = this.checkUrlArray(values['zonegroup_endpoints']);
+ this.rgwZonegroupService
+ .create(this.realm, this.zonegroup, values['default_zonegroup'], values['master_zonegroup'])
+ .subscribe(
+ () => {
+ this.notificationService.show(
+ NotificationType.success,
+ $localize`Zonegroup: '${values['zonegroupName']}' created successfully`
+ );
+ this.activeModal.close();
+ },
+ () => {
+ this.multisiteZonegroupForm.setErrors({ cdSubmitButton: true });
+ }
+ );
+ }
+
+ checkUrlArray(endpoints: string) {
+ let endpointsArray = [];
+ if (endpoints.includes(',')) {
+ endpointsArray = endpoints.split(',');
+ } else {
+ endpointsArray.push(endpoints);
+ }
+ return endpointsArray;
+ }
+}
import { ActionLabels, URLVerbs } from '~/app/shared/constants/app.constants';
import { CRUDTableComponent } from '~/app/shared/datatable/crud-table/crud-table.component';
+
import { SharedModule } from '~/app/shared/shared.module';
import { PerformanceCounterModule } from '../performance-counter/performance-counter.module';
import { RgwBucketDetailsComponent } from './rgw-bucket-details/rgw-bucket-details.component';
import { RgwUserSubuserModalComponent } from './rgw-user-subuser-modal/rgw-user-subuser-modal.component';
import { RgwUserSwiftKeyModalComponent } from './rgw-user-swift-key-modal/rgw-user-swift-key-modal.component';
import { RgwUserTabsComponent } from './rgw-user-tabs/rgw-user-tabs.component';
+import { RgwMultisiteDetailsComponent } from './rgw-multisite-details/rgw-multisite-details.component';
+import { TreeModule } from '@circlon/angular-tree-component';
+import { DataTableModule } from '~/app/shared/datatable/datatable.module';
+import { FeatureTogglesGuardService } from '~/app/shared/services/feature-toggles-guard.service';
+import { ModuleStatusGuardService } from '~/app/shared/services/module-status-guard.service';
+import { RgwMultisiteRealmFormComponent } from './rgw-multisite-realm-form/rgw-multisite-realm-form.component';
+import { RgwMultisiteZonegroupFormComponent } from './rgw-multisite-zonegroup-form/rgw-multisite-zonegroup-form.component';
+import { RgwMultisiteZoneFormComponent } from './rgw-multisite-zone-form/rgw-multisite-zone-form.component';
@NgModule({
imports: [
NgbNavModule,
RouterModule,
NgbTooltipModule,
- NgxPipeFunctionModule
+ NgxPipeFunctionModule,
+ TreeModule,
+ DataTableModule
],
exports: [
RgwDaemonListComponent,
RgwUserCapabilityModalComponent,
RgwUserSubuserModalComponent,
RgwConfigModalComponent,
- RgwUserTabsComponent
+ RgwUserTabsComponent,
+ RgwMultisiteDetailsComponent,
+ RgwMultisiteRealmFormComponent,
+ RgwMultisiteZonegroupFormComponent,
+ RgwMultisiteZoneFormComponent
]
})
export class RgwModule {}
data: { breadcrumbs: ActionLabels.EDIT }
}
]
+ },
+ {
+ path: 'multisite',
+ canActivate: [FeatureTogglesGuardService, ModuleStatusGuardService],
+ data: {
+ moduleStatusGuardConfig: {
+ uiApiPath: 'rgw/multisite',
+ redirectTo: 'error',
+ header: 'Multi-site not configured',
+ button_name: 'Add Multi-site Configuration',
+ button_route: '/rgw/multisite/create',
+ button_title: 'Add multi-site configuration (realms/zonegroups/zones)',
+ secondary_button_name: 'Import Multi-site Configuration',
+ secondary_button_route: 'rgw/multisite/import',
+ secondary_button_title:
+ 'Import multi-site configuration (import realm token from a secondary cluster)'
+ },
+ breadcrumbs: 'Multisite'
+ },
+ children: [{ path: '', component: RgwMultisiteDetailsComponent }]
}
];
<div class="mt-4">
<div class="text-center"
*ngIf="(buttonName && buttonRoute) || uiConfig; else dashboardButton">
- <button class="btn btn-primary"
+ <button class="btn btn-primary ms-1"
[routerLink]="buttonRoute"
*ngIf="!uiConfig; else configureButtonTpl"
i18n>{{ buttonName }}</button>
+ <button class="btn btn-light ms-1"
+ [routerLink]="secondaryButtonRoute"
+ *ngIf="secondaryButtonName && secondaryButtonRoute"
+ i18n>{{ secondaryButtonName }}</button>
</div>
</div>
</div>
buttonRoute: string;
buttonName: string;
buttonTitle: string;
+ secondaryButtonRoute: string;
+ secondaryButtonName: string;
+ secondaryButtonTitle: string;
component: string;
constructor(
this.buttonRoute = history.state.button_route;
this.buttonName = history.state.button_name;
this.buttonTitle = history.state.button_title;
+ this.secondaryButtonRoute = history.state.secondary_button_route;
+ this.secondaryButtonName = history.state.secondary_button_name;
+ this.secondaryButtonTitle = history.state.secondary_button_title;
this.component = history.state.component;
this.docUrl = this.docService.urlGenerator(this.section);
} catch (error) {
<a i18n
routerLink="/rgw/bucket">Buckets</a>
</li>
+ <li routerLinkActive="active"
+ class="tc_submenuitem tc_submenuitem_rgw_buckets">
+ <a i18n
+ routerLink="/rgw/multisite">Multisite</a>
+ </li>
</ul>
</li>
</ng-container>
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { configureTestBed } from '~/testing/unit-test-helper';
+
+import { RgwRealmService } from './rgw-realm.service';
+
+describe('RgwRealmService', () => {
+ let service: RgwRealmService;
+
+ configureTestBed({
+ imports: [HttpClientTestingModule]
+ });
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(RgwRealmService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
--- /dev/null
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { RgwRealm } from '~/app/ceph/rgw/models/rgw-multisite';
+import { Icons } from '../enum/icons.enum';
+import { RgwDaemonService } from './rgw-daemon.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class RgwRealmService {
+ private url = 'api/rgw/realm';
+
+ constructor(private http: HttpClient, private 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 });
+ });
+ }
+
+ list(): Observable<object> {
+ return this.rgwDaemonService.request(() => {
+ return this.http.get<object>(`${this.url}`);
+ });
+ }
+
+ get(realm: RgwRealm): Observable<RgwRealm> {
+ return this.rgwDaemonService.request(() => {
+ 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`);
+ });
+ }
+
+ getRealmTree(realm: RgwRealm, defaultRealmId: string) {
+ let nodes = {};
+ let realmIds = [];
+ nodes['id'] = realm.id;
+ realmIds.push(realm.id);
+ nodes['name'] = realm.name + ' (realm)';
+ nodes['info'] = realm;
+ nodes['is_default'] = realm.id === defaultRealmId ? true : false;
+ nodes['icon'] = Icons.reweight;
+ return {
+ nodes: nodes,
+ realmIds: realmIds
+ };
+ }
+}
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { configureTestBed } from '~/testing/unit-test-helper';
+
+import { RgwZoneService } from './rgw-zone.service';
+
+describe('RgwZoneService', () => {
+ let service: RgwZoneService;
+
+ configureTestBed({
+ imports: [HttpClientTestingModule]
+ });
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(RgwZoneService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
--- /dev/null
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+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) {}
+
+ create(
+ zone: RgwZone,
+ zonegroup: RgwZonegroup,
+ defaultZone: boolean,
+ master: boolean,
+ endpoints: Array<string>,
+ user: string
+ ) {
+ 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
+ });
+ return this.http.post(`${this.url}`, null, { params: params });
+ });
+ }
+
+ list(): Observable<object> {
+ return this.rgwDaemonService.request(() => {
+ return this.http.get<object>(`${this.url}`);
+ });
+ }
+
+ get(zone: RgwZone): Observable<RgwZone> {
+ return this.rgwDaemonService.request(() => {
+ 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`);
+ });
+ }
+
+ 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 + ' (zone)';
+ nodes['info'] = zone;
+ nodes['icon'] = Icons.deploy;
+ nodes['parent'] = zonegroup ? zonegroup.name : '';
+ nodes['second_parent'] = realm ? realm.name : '';
+ nodes['is_default'] = zone.id === defaultZoneId ? true : false;
+ nodes['is_master'] = zonegroup && zonegroup.master_zone === zone.id ? true : false;
+ return {
+ nodes: nodes,
+ zoneIds: zoneIds
+ };
+ }
+}
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { configureTestBed } from '~/testing/unit-test-helper';
+
+import { RgwZonegroupService } from './rgw-zonegroup.service';
+
+describe('RgwZonegroupService', () => {
+ let service: RgwZonegroupService;
+
+ configureTestBed({
+ imports: [HttpClientTestingModule]
+ });
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(RgwZonegroupService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
--- /dev/null
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+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) {}
+
+ 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 });
+ });
+ }
+
+ list(): Observable<object> {
+ return this.rgwDaemonService.request(() => {
+ return this.http.get<object>(`${this.url}`);
+ });
+ }
+
+ get(zonegroup: RgwZonegroup): Observable<RgwZonegroup> {
+ return this.rgwDaemonService.request(() => {
+ 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`);
+ });
+ }
+
+ getZonegroupTree(zonegroup: RgwZonegroup, defaultZonegroupId: string, realm?: RgwRealm) {
+ let nodes = {};
+ nodes['id'] = zonegroup.id;
+ nodes['name'] = zonegroup.name + ' (zonegroup)';
+ nodes['info'] = zonegroup;
+ nodes['icon'] = Icons.cubes;
+ nodes['is_master'] = zonegroup.is_master;
+ nodes['parent'] = realm ? realm.name : '';
+ nodes['is_default'] = zonegroup.id === defaultZonegroupId ? true : false;
+ return nodes;
+ }
+}
REDEPLOY: string;
RESTART: string;
RESYNC: string;
+ EXPORT: string;
+ IMPORT: any;
constructor() {
/* Create a new item */
this.CREATE = $localize`Create`;
+ this.EXPORT = $localize`Export`;
+
+ this.IMPORT = $localize`Import`;
+
/* Destroy an existing item */
this.DELETE = $localize`Delete`;
CANCELED: string;
PREVIEWED: string;
MOVED: string;
+ EXPORT: string;
+ IMPORT: string;
COPIED: string;
CLONED: string;
DEEP_SCRUBBED: string;
this.RESTART = $localize`Restart`;
}
}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TimerServiceInterval {
+ TIMER_SERVICE_PERIOD: number;
+
+ constructor() {
+ this.TIMER_SERVICE_PERIOD = 5000;
+ }
+}
exit = 'fa fa-sign-out', // Exit
restart = 'fa fa-history', // Restart
deploy = 'fa fa-cube', // Deploy, Redeploy
+ cubes = 'fa fa-cubes',
/* Icons for special effect */
large = 'fa fa-lg', // icon becomes 33% larger
button_name: config.button_name,
button_route: config.button_route,
button_title: config.button_title,
+ secondary_button_name: config.secondary_button_name,
+ secondary_button_route: config.secondary_button_route,
+ secondary_button_title: config.secondary_button_title,
uiConfig: config.uiConfig,
uiApiPath: config.uiApiPath,
icon: Icons.wrench,
$screen-md-min: 768px !default;
$screen-lg-min: 992px !default;
$screen-xl-min: 1200px !default;
+$tree-container-height: 200px !default;
$screen-xs-max: calc(#{$screen-sm-min} - 1px) !default;
$screen-sm-max: calc(#{$screen-md-min} - 1px) !default;
- jwt: []
tags:
- RgwDaemon
+ /api/rgw/realm:
+ get:
+ parameters:
+ - allowEmptyValue: true
+ in: query
+ name: daemon_name
+ 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:
+ - RgwRealm
+ post:
+ parameters: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ properties:
+ daemon_name:
+ type: string
+ default:
+ type: string
+ realm_name:
+ type: string
+ required:
+ - realm_name
+ - default
+ 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/get_all_realms_info:
+ 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/{realm_name}:
+ get:
+ parameters:
+ - in: path
+ name: realm_name
+ required: true
+ schema:
+ type: string
+ - allowEmptyValue: true
+ in: query
+ name: daemon_name
+ 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:
+ - RgwRealm
/api/rgw/site:
get:
parameters:
- jwt: []
tags:
- RgwUser
+ /api/rgw/zone:
+ get:
+ parameters:
+ - allowEmptyValue: true
+ in: query
+ name: daemon_name
+ 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
+ post:
+ parameters: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ properties:
+ daemon_name:
+ type: string
+ default:
+ default: false
+ type: boolean
+ master:
+ default: false
+ type: boolean
+ user:
+ type: string
+ zone_endpoints:
+ type: string
+ zone_name:
+ type: string
+ zonegroup_name:
+ type: string
+ required:
+ - 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:
+ - RgwZone
+ /api/rgw/zone/get_all_zones_info:
+ 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/{zone_name}:
+ get:
+ parameters:
+ - in: path
+ name: zone_name
+ required: true
+ schema:
+ type: string
+ - allowEmptyValue: true
+ in: query
+ name: daemon_name
+ 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/zonegroup:
+ get:
+ parameters:
+ - allowEmptyValue: true
+ in: query
+ name: daemon_name
+ 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:
+ - RgwZonegroup
+ post:
+ parameters: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ properties:
+ daemon_name:
+ type: string
+ default:
+ type: string
+ master:
+ type: string
+ realm_name:
+ type: string
+ zonegroup_endpoints:
+ type: string
+ zonegroup_name:
+ type: string
+ required:
+ - realm_name
+ - zonegroup_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:
+ - RgwZonegroup
+ /api/rgw/zonegroup/get_all_zonegroups_info:
+ 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:
+ - RgwZonegroup
+ /api/rgw/zonegroup/{zonegroup_name}:
+ get:
+ parameters:
+ - in: path
+ name: zonegroup_name
+ required: true
+ schema:
+ type: string
+ - allowEmptyValue: true
+ in: query
+ name: daemon_name
+ 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:
+ - RgwZonegroup
/api/role:
get:
parameters: []
name: RgwMirrorPerfCounter
- description: Rgw Perf Counters Management API
name: RgwPerfCounter
+- description: '*No description available*'
+ name: RgwRealm
- description: RGW Site Management API
name: RgwSite
- description: RGW User Management API
name: RgwUser
+- description: '*No description available*'
+ name: RgwZone
+- description: '*No description available*'
+ name: RgwZonegroup
- description: Role Management API
name: Role
- description: Service Management API
# -*- coding: utf-8 -*-
+# pylint: disable=C0302
+# pylint: disable=too-many-branches
+# pylint: disable=too-many-lines
import ipaddress
import json
realms_info = self._get_realms_info()
if 'realms' in realms_info and realms_info['realms']:
return realms_info['realms']
-
return []
- def get_default_realm(self) -> str:
+ def get_default_realm(self):
realms_info = self._get_realms_info()
if 'default_info' in realms_info and realms_info['default_info']:
realm_info = self._get_realm_info(realms_info['default_info'])
if 'name' in realm_info and realm_info['name']:
return realm_info['name']
- raise DashboardException(msg='Default realm not found.',
- http_status_code=404,
- component='rgw')
+ 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
+ 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')
+
+ def list_realms(self):
+ rgw_realm_list = {}
+ rgw_realm_list_cmd = ['realm', 'list']
+ 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
+
+ def get_realm(self, realm_name: str):
+ realm_info = {}
+ rgw_realm_info_cmd = ['realm', 'get', '--rgw-realm', realm_name]
+ 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
+
+ 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 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')
+
+ 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, _ = mgr.send_rgwadmin_command(rgw_zonegroup_create_cmd)
+ if exit_code > 0:
+ raise DashboardException('Unable to get realm info',
+ 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 list_zonegroups(self):
+ rgw_zonegroup_list = {}
+ rgw_zonegroup_list_cmd = ['zonegroup', 'list']
+ try:
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zonegroup_list_cmd)
+ if exit_code > 0:
+ raise DashboardException(msg='Unable to fetch zonegroup list',
+ 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]
+ try:
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zonegroup_info_cmd)
+ if exit_code > 0:
+ raise DashboardException('Unable to get zonegroup info',
+ 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 create_zone(self, zone_name, zonegroup_name, default, master, endpoints, user):
+ if user != 'null':
+ access_key, secret_key = _get_user_keys(user)
+ 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
+ try:
+ exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zone_create_cmd)
+ if exit_code > 0:
+ raise DashboardException(msg='Unable to create 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 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 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
@RestClient.api_get('/{bucket_name}?versioning')
def get_bucket_versioning(self, bucket_name, request=None):