2 ChangeDetectionStrategy,
9 } from '@angular/core';
10 import { Node } from 'carbon-components-angular/treeview/tree-node.types';
11 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
12 import _ from 'lodash';
14 import { forkJoin, Subscription } from 'rxjs';
15 import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
16 import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
17 import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
18 import { DeleteConfirmationModalComponent } from '~/app/shared/components/delete-confirmation-modal/delete-confirmation-modal.component';
19 import { ActionLabelsI18n, TimerServiceInterval } from '~/app/shared/constants/app.constants';
20 import { Icons } from '~/app/shared/enum/icons.enum';
21 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
22 import { CdTableAction } from '~/app/shared/models/cd-table-action';
23 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
24 import { Permissions } from '~/app/shared/models/permissions';
25 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
26 import { ModalService } from '~/app/shared/services/modal.service';
27 import { NotificationService } from '~/app/shared/services/notification.service';
28 import { TimerService } from '~/app/shared/services/timer.service';
29 import { RgwRealm, RgwZone, RgwZonegroup } from '../models/rgw-multisite';
30 import { RgwMultisiteMigrateComponent } from '../rgw-multisite-migrate/rgw-multisite-migrate.component';
31 import { RgwMultisiteZoneDeletionFormComponent } from '../models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component';
32 import { RgwMultisiteZonegroupDeletionFormComponent } from '../models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component';
33 import { RgwMultisiteExportComponent } from '../rgw-multisite-export/rgw-multisite-export.component';
34 import { RgwMultisiteImportComponent } from '../rgw-multisite-import/rgw-multisite-import.component';
35 import { RgwMultisiteRealmFormComponent } from '../rgw-multisite-realm-form/rgw-multisite-realm-form.component';
36 import { RgwMultisiteZoneFormComponent } from '../rgw-multisite-zone-form/rgw-multisite-zone-form.component';
37 import { RgwMultisiteZonegroupFormComponent } from '../rgw-multisite-zonegroup-form/rgw-multisite-zonegroup-form.component';
38 import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
39 import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
40 import { Router } from '@angular/router';
41 import { RgwMultisiteWizardComponent } from '../rgw-multisite-wizard/rgw-multisite-wizard.component';
42 import { RgwMultisiteSyncPolicyComponent } from '../rgw-multisite-sync-policy/rgw-multisite-sync-policy.component';
43 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
44 import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
46 const BASE_URL = 'rgw/multisite/configuration';
49 selector: 'cd-rgw-multisite-details',
50 templateUrl: './rgw-multisite-details.component.html',
51 styleUrls: ['./rgw-multisite-details.component.scss'],
52 changeDetection: ChangeDetectionStrategy.OnPush
54 export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
55 private sub = new Subscription();
56 @ViewChild('treeNodeTemplate') labelTpl: TemplateRef<any>;
57 @ViewChild(RgwMultisiteSyncPolicyComponent) syncPolicyComp: RgwMultisiteSyncPolicyComponent;
60 noDefaultRealm: $localize`Please create a default realm first to enable this feature`,
61 noMasterZone: $localize`Please create a master zone for each zone group to enable this feature`,
62 noRealmExists: $localize`No realm exists`,
63 disableExport: $localize`Please create master zone group and master zone for each of the realms`
67 permissions: Permissions;
68 selection = new CdTableSelection();
69 createTableActions: CdTableAction[];
70 migrateTableAction: CdTableAction[];
71 importAction: CdTableAction[];
72 exportAction: CdTableAction[];
73 multisiteReplicationActions: CdTableAction[];
74 loadingIndicator = true;
76 toNode(values: any): Node[] {
77 return values.map((value: any) => ({
86 children: value?.children ? this.toNode(value.children) : []
90 set nodes(values: any) {
91 this._nodes = this.toNode(values);
92 this.changeDetectionRef.detectChanges();
99 private _nodes: Node[] = [];
101 modalRef: NgbModalRef;
103 realms: RgwRealm[] = [];
104 zonegroups: RgwZonegroup[] = [];
105 zones: RgwZone[] = [];
107 metadataTitle: string;
108 bsModalRef: NgbModalRef;
109 realmIds: string[] = [];
110 zoneIds: string[] = [];
112 defaultZonegroupId = '';
114 multisiteInfo: object[] = [];
115 defaultsInfo: string[] = [];
116 showMigrateAndReplicationActions = false;
117 editTitle: string = 'Edit';
118 deleteTitle: string = 'Delete';
119 disableExport = true;
120 rgwModuleStatus: boolean;
121 restartGatewayMessage = false;
122 rgwModuleData: string | any[] = [];
124 activeNodeId?: string;
126 NAVIGATE_TO = '/rgw/multisite';
129 private modalService: ModalService,
130 private timerService: TimerService,
131 private authStorageService: AuthStorageService,
132 public actionLabels: ActionLabelsI18n,
133 public timerServiceVariable: TimerServiceInterval,
134 public router: Router,
135 public rgwRealmService: RgwRealmService,
136 public rgwZonegroupService: RgwZonegroupService,
137 public rgwZoneService: RgwZoneService,
138 public rgwDaemonService: RgwDaemonService,
139 public mgrModuleService: MgrModuleService,
140 private notificationService: NotificationService,
141 private cdsModalService: ModalCdsService,
142 private rgwMultisiteService: RgwMultisiteService,
143 private changeDetectionRef: ChangeDetectorRef
145 this.permissions = this.authStorageService.getPermissions();
148 openModal(entity: any | string, edit = false) {
149 const entityName = edit ? entity?.data?.type : entity;
150 const action = edit ? 'edit' : 'create';
151 const initialState = {
152 resource: entityName,
155 defaultsInfo: this.defaultsInfo,
156 multisiteInfo: this.multisiteInfo
158 if (entityName === 'realm') {
159 this.bsModalRef = this.cdsModalService.show(RgwMultisiteRealmFormComponent, initialState);
160 } else if (entityName === 'zonegroup') {
161 this.bsModalRef = this.modalService.show(RgwMultisiteZonegroupFormComponent, initialState, {
165 this.bsModalRef = this.modalService.show(RgwMultisiteZoneFormComponent, initialState, {
171 openMultisiteSetupWizard() {
172 this.bsModalRef = this.cdsModalService.show(RgwMultisiteWizardComponent);
176 const initialState = {
177 multisiteInfo: this.multisiteInfo
179 this.bsModalRef = this.modalService.show(RgwMultisiteMigrateComponent, initialState, {
185 const initialState = {
186 multisiteInfo: this.multisiteInfo
188 this.bsModalRef = this.modalService.show(RgwMultisiteImportComponent, initialState, {
194 const initialState = {
195 defaultsInfo: this.defaultsInfo,
196 multisiteInfo: this.multisiteInfo
198 this.bsModalRef = this.modalService.show(RgwMultisiteExportComponent, initialState, {
204 this.realms.forEach((realm: any) => {
205 this.zonegroups.forEach((zonegroup) => {
206 if (realm.id === zonegroup.realm_id) {
207 if (zonegroup.is_master && zonegroup.master_zone !== '') {
208 this.disableExport = false;
213 if (!this.rgwModuleStatus) {
216 if (this.realms.length < 1) {
217 return this.messages.noRealmExists;
218 } else if (this.disableExport) {
219 return this.messages.disableExport;
226 if (!this.rgwModuleStatus) {
234 this.createTableActions = [
236 permission: 'create',
238 name: this.actionLabels.CREATE + ' Realm',
239 click: () => this.openModal('realm')
242 permission: 'create',
244 name: this.actionLabels.CREATE + ' Zone Group',
245 click: () => this.openModal('zonegroup'),
246 disable: () => this.getDisable()
249 permission: 'create',
251 name: this.actionLabels.CREATE + ' Zone',
252 click: () => this.openModal('zone')
255 this.migrateTableAction = [
257 permission: 'create',
259 name: this.actionLabels.MIGRATE,
260 click: () => this.openMigrateModal()
263 this.importAction = [
265 permission: 'create',
266 icon: Icons.download,
267 name: this.actionLabels.IMPORT,
268 click: () => this.openImportModal(),
269 disable: () => this.getDisableImport()
272 this.exportAction = [
274 permission: 'create',
276 name: this.actionLabels.EXPORT,
277 click: () => this.openExportModal(),
278 disable: () => this.getDisableExport()
281 this.multisiteReplicationActions = [
283 permission: 'create',
285 name: this.actionLabels.SETUP_MULTISITE_REPLICATION,
287 this.router.navigate([BASE_URL, { outlets: { modal: 'setup-multisite-replication' } }])
291 this.startPollingMultisiteInfo();
292 this.mgrModuleService.updateCompleted$.subscribe(() => {
293 this.startPollingMultisiteInfo();
294 this.getRgwModuleStatus();
296 // Only get the module status if you can read from configOpt
297 if (this.permissions.configOpt.read) this.getRgwModuleStatus();
300 startPollingMultisiteInfo(): void {
301 const observables = [
302 this.rgwRealmService.getAllRealmsInfo(),
303 this.rgwZonegroupService.getAllZonegroupsInfo(),
304 this.rgwZoneService.getAllZonesInfo()
308 this.sub.unsubscribe();
311 this.sub = this.timerService
312 .get(() => forkJoin(observables), this.timerServiceVariable.TIMER_SERVICE_PERIOD * 2)
314 (multisiteInfo: [object, object, object]) => {
315 this.multisiteInfo = multisiteInfo;
316 this.loadingIndicator = false;
317 this.nodes = this.abstractTreeData(multisiteInfo);
324 this.sub.unsubscribe();
327 private getRgwModuleStatus() {
328 this.rgwMultisiteService.getRgwModuleStatus().subscribe((status: boolean) => {
329 this.rgwModuleStatus = status;
333 private abstractTreeData(multisiteInfo: [object, object, object]): any[] {
334 let allNodes: object[] = [];
336 let firstChildNodes = {};
337 let allFirstChildNodes = [];
338 let secondChildNodes = {};
339 let allSecondChildNodes: {}[] = [];
340 this.realms = multisiteInfo[0]['realms'];
341 this.zonegroups = multisiteInfo[1]['zonegroups'];
342 this.zones = multisiteInfo[2]['zones'];
343 this.defaultRealmId = multisiteInfo[0]['default_realm'];
344 this.defaultZonegroupId = multisiteInfo[1]['default_zonegroup'];
345 this.defaultZoneId = multisiteInfo[2]['default_zone'];
346 this.defaultsInfo = this.getDefaultsEntities(
348 this.defaultZonegroupId,
351 if (this.realms.length > 0) {
352 // get tree for realm -> zonegroup -> zone
353 for (const realm of this.realms) {
354 const result = this.rgwRealmService.getRealmTree(realm, this.defaultRealmId);
355 rootNodes = result['nodes'];
356 this.realmIds = this.realmIds.concat(result['realmIds']);
357 for (const zonegroup of this.zonegroups) {
358 if (zonegroup.realm_id === realm.id) {
359 firstChildNodes = this.rgwZonegroupService.getZonegroupTree(
361 this.defaultZonegroupId,
364 for (const zone of zonegroup.zones) {
365 const zoneResult = this.rgwZoneService.getZoneTree(
372 secondChildNodes = zoneResult['nodes'];
373 this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
374 allSecondChildNodes.push(secondChildNodes);
375 secondChildNodes = {};
377 allSecondChildNodes = allSecondChildNodes.map((x) => ({
379 parentNode: firstChildNodes
381 firstChildNodes['children'] = allSecondChildNodes;
382 allSecondChildNodes = [];
383 allFirstChildNodes.push(firstChildNodes);
384 firstChildNodes = {};
387 allFirstChildNodes = allFirstChildNodes.map((x) => ({ ...x, parentNode: rootNodes }));
388 rootNodes['children'] = allFirstChildNodes;
389 allNodes.push({ ...rootNodes, label: rootNodes?.['name'] || rootNodes?.['id'] });
390 firstChildNodes = {};
391 secondChildNodes = {};
393 allFirstChildNodes = [];
394 allSecondChildNodes = [];
397 if (this.zonegroups.length > 0) {
398 // get tree for zonegroup -> zone (standalone zonegroups that don't match a realm eg(initial default))
399 for (const zonegroup of this.zonegroups) {
400 if (!this.realmIds.includes(zonegroup.realm_id)) {
401 rootNodes = this.rgwZonegroupService.getZonegroupTree(zonegroup, this.defaultZonegroupId);
402 for (const zone of zonegroup.zones) {
403 const zoneResult = this.rgwZoneService.getZoneTree(
409 firstChildNodes = zoneResult['nodes'];
410 this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
411 allFirstChildNodes.push(firstChildNodes);
412 firstChildNodes = {};
414 allFirstChildNodes = allFirstChildNodes.map((x) => ({ ...x, parentNode: rootNodes }));
415 rootNodes['children'] = allFirstChildNodes;
416 allNodes.push({ ...rootNodes, label: rootNodes?.['name'] || rootNodes?.['id'] });
417 firstChildNodes = {};
419 allFirstChildNodes = [];
423 if (this.zones.length > 0) {
424 // get tree for standalone zones(zones that do not belong to a zonegroup)
425 for (const zone of this.zones) {
426 if (this.zoneIds.length > 0 && !this.zoneIds.includes(zone.id)) {
427 const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId, this.zones);
428 rootNodes = zoneResult['nodes'];
429 allNodes.push({ ...rootNodes, label: rootNodes?.['name'] || rootNodes?.['id'] });
434 if (this.realms.length < 1 && this.zonegroups.length < 1 && this.zones.length < 1) {
444 this.evaluateMigrateAndReplicationActions();
445 this.rgwMultisiteService.restartGatewayMessage$.subscribe((value) => {
446 if (value !== null) {
447 this.restartGatewayMessage = value;
449 this.checkRestartGatewayMessage();
455 checkRestartGatewayMessage() {
456 this.rgwDaemonService.list().subscribe((data: any) => {
457 const realmName = data.map((item: { [x: string]: any }) => item['realm_name']);
459 this.defaultRealmId !== '' &&
460 this.defaultZonegroupId !== '' &&
461 this.defaultZoneId !== '' &&
462 realmName.includes('')
464 this.restartGatewayMessage = true;
466 this.restartGatewayMessage = false;
472 defaultRealmId: string,
473 defaultZonegroupId: string,
474 defaultZoneId: string
476 const defaultRealm = this.realms?.find((x: { id: string }) => x.id === defaultRealmId);
477 const defaultZonegroup = this.zonegroups?.find(
478 (x: { id: string }) => x.id === defaultZonegroupId
480 const defaultZone = this.zones?.find((x: { id: string }) => x.id === defaultZoneId);
483 defaultRealmName: defaultRealm?.name,
484 defaultZonegroupName: defaultZonegroup?.name,
485 defaultZoneName: defaultZone?.name
489 onNodeSelected(node: Node) {
490 this.metadataTitle = node?.value?.name;
491 this.metadata = node?.value?.info;
492 this.activeNodeId = node?.value?.id;
493 node.expanded = true;
497 let isMasterZone = true;
498 if (this.defaultRealmId === '') {
499 return this.messages.noDefaultRealm;
501 this.zonegroups.forEach((zgp: any) => {
502 if (_.isEmpty(zgp.master_zone)) {
503 isMasterZone = false;
509 'Please create a master zone for each existing zonegroup to enable this feature';
511 return this.messages.noMasterZone;
514 this.editTitle = 'Edit';
521 evaluateMigrateAndReplicationActions() {
523 this.realms.length === 0 &&
524 this.zonegroups.length === 1 &&
525 this.zonegroups[0].name === 'default' &&
526 this.zones.length === 1 &&
527 this.zones[0].name === 'default'
529 this.showMigrateAndReplicationActions = true;
531 this.showMigrateAndReplicationActions = false;
533 return this.showMigrateAndReplicationActions;
536 isDeleteDisabled(node: Node): { isDisabled: boolean; deleteTitle: string } {
537 let isDisabled: boolean = false;
538 let deleteTitle: string = this.deleteTitle;
539 let masterZonegroupCount: number = 0;
540 if (node?.value?.type === 'realm' && node?.data?.is_default && this.realms.length < 2) {
544 if (node?.data?.type === 'zonegroup') {
545 if (this.zonegroups.length < 2) {
546 deleteTitle = 'You can not delete the only zonegroup available';
548 } else if (node?.data?.is_default) {
549 deleteTitle = 'You can not delete the default zonegroup';
551 } else if (node?.data?.is_master) {
552 for (let zonegroup of this.zonegroups) {
553 if (zonegroup.is_master === true) {
554 masterZonegroupCount++;
555 if (masterZonegroupCount > 1) break;
558 if (masterZonegroupCount < 2) {
559 deleteTitle = 'You can not delete the only master zonegroup available';
565 if (node?.data?.type === 'zone') {
566 if (this.zones.length < 2) {
567 deleteTitle = 'You can not delete the only zone available';
569 } else if (node?.data?.is_default) {
570 deleteTitle = 'You can not delete the default zone';
572 } else if (node?.data?.is_master && node?.data?.zone_zonegroup.zones.length < 2) {
574 'You can not delete the master zone as there are no more zones in this zonegroup';
580 this.deleteTitle = 'Delete';
583 return { isDisabled, deleteTitle };
587 if (node?.data?.type === 'realm') {
588 const modalRef = this.cdsModalService.show(DeleteConfirmationModalComponent, {
589 itemDescription: $localize`${node?.data?.type} ${node?.data?.name}`,
590 itemNames: [`${node?.data?.name}`],
591 submitAction: () => {
592 this.rgwRealmService.delete(node?.data?.name).subscribe(
594 this.notificationService.show(
595 NotificationType.success,
596 $localize`Realm: '${node?.data?.name}' deleted successfully`
598 this.cdsModalService.dismissAll();
601 this.cdsModalService.stopLoadingSpinner(modalRef.deletionForm);
606 } else if (node?.data?.type === 'zonegroup') {
607 this.modalRef = this.modalService.show(RgwMultisiteZonegroupDeletionFormComponent, {
610 } else if (node?.data?.type === 'zone') {
611 this.modalRef = this.modalService.show(RgwMultisiteZoneDeletionFormComponent, {
618 this.mgrModuleService.updateModuleState(
623 'Enabled RGW Module',