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 zonegroup to enable this feature`,
62 noRealmExists: $localize`No realm exists`,
63 disableExport: $localize`Please create master zonegroup and master zone for each of the realms`
67 permissions: Permissions;
68 selection = new CdTableSelection();
69 createTableActions: CdTableAction[];
70 migrateTableAction: CdTableAction[];
71 multisiteReplicationActions: CdTableAction[];
72 loadingIndicator = true;
74 toNode(values: any): Node[] {
75 return values.map((value: any) => ({
84 children: value?.children ? this.toNode(value.children) : []
88 set nodes(values: any) {
89 this._nodes = this.toNode(values);
90 this.changeDetectionRef.detectChanges();
97 private _nodes: Node[] = [];
99 modalRef: NgbModalRef;
101 realms: RgwRealm[] = [];
102 zonegroups: RgwZonegroup[] = [];
103 zones: RgwZone[] = [];
105 metadataTitle: string;
106 bsModalRef: NgbModalRef;
107 realmIds: string[] = [];
108 zoneIds: string[] = [];
110 defaultZonegroupId = '';
112 multisiteInfo: object[] = [];
113 defaultsInfo: string[] = [];
114 showMigrateAndReplicationActions = false;
115 editTitle: string = 'Edit';
116 deleteTitle: string = 'Delete';
117 disableExport = true;
118 rgwModuleStatus: boolean;
119 restartGatewayMessage = false;
120 rgwModuleData: string | any[] = [];
122 activeNodeId?: string;
124 NAVIGATE_TO = '/rgw/multisite';
127 private modalService: ModalService,
128 private timerService: TimerService,
129 private authStorageService: AuthStorageService,
130 public actionLabels: ActionLabelsI18n,
131 public timerServiceVariable: TimerServiceInterval,
132 public router: Router,
133 public rgwRealmService: RgwRealmService,
134 public rgwZonegroupService: RgwZonegroupService,
135 public rgwZoneService: RgwZoneService,
136 public rgwDaemonService: RgwDaemonService,
137 public mgrModuleService: MgrModuleService,
138 private notificationService: NotificationService,
139 private cdsModalService: ModalCdsService,
140 private rgwMultisiteService: RgwMultisiteService,
141 private changeDetectionRef: ChangeDetectorRef
143 this.permissions = this.authStorageService.getPermissions();
146 openModal(entity: any | string, edit = false) {
147 const entityName = edit ? entity?.data?.type : entity;
148 const action = edit ? 'edit' : 'create';
149 const initialState = {
150 resource: entityName,
153 defaultsInfo: this.defaultsInfo,
154 multisiteInfo: this.multisiteInfo
156 if (entityName === 'realm') {
157 this.bsModalRef = this.cdsModalService.show(RgwMultisiteRealmFormComponent, initialState);
158 } else if (entityName === 'zonegroup') {
159 this.bsModalRef = this.modalService.show(RgwMultisiteZonegroupFormComponent, initialState, {
163 this.bsModalRef = this.modalService.show(RgwMultisiteZoneFormComponent, initialState, {
169 openMultisiteSetupWizard() {
170 this.bsModalRef = this.cdsModalService.show(RgwMultisiteWizardComponent);
174 const initialState = {
175 multisiteInfo: this.multisiteInfo
177 this.bsModalRef = this.modalService.show(RgwMultisiteMigrateComponent, initialState, {
183 const initialState = {
184 multisiteInfo: this.multisiteInfo
186 this.bsModalRef = this.modalService.show(RgwMultisiteImportComponent, initialState, {
192 const initialState = {
193 defaultsInfo: this.defaultsInfo,
194 multisiteInfo: this.multisiteInfo
196 this.bsModalRef = this.modalService.show(RgwMultisiteExportComponent, initialState, {
202 this.realms.forEach((realm: any) => {
203 this.zonegroups.forEach((zonegroup) => {
204 if (realm.id === zonegroup.realm_id) {
205 if (zonegroup.is_master && zonegroup.master_zone !== '') {
206 this.disableExport = false;
211 if (!this.rgwModuleStatus) {
214 if (this.realms.length < 1) {
215 return this.messages.noRealmExists;
216 } else if (this.disableExport) {
217 return this.messages.disableExport;
224 if (!this.rgwModuleStatus) {
232 this.createTableActions = [
234 permission: 'create',
236 name: this.actionLabels.CREATE + ' Realm',
237 click: () => this.openModal('realm'),
238 visible: () => !this.showMigrateAndReplicationActions
241 permission: 'create',
243 name: this.actionLabels.CREATE + ' Zonegroup',
244 click: () => this.openModal('zonegroup'),
245 disable: () => this.getDisable(),
246 visible: () => !this.showMigrateAndReplicationActions
249 permission: 'create',
251 name: this.actionLabels.CREATE + ' Zone',
252 click: () => this.openModal('zone'),
253 visible: () => !this.showMigrateAndReplicationActions
256 permission: 'create',
257 icon: Icons.download,
258 name: this.actionLabels.IMPORT,
259 click: () => this.openImportModal(),
260 disable: () => this.getDisableImport()
263 permission: 'create',
265 name: this.actionLabels.EXPORT,
266 click: () => this.openExportModal(),
267 disable: () => this.getDisableExport()
270 this.migrateTableAction = [
272 permission: 'create',
274 name: this.actionLabels.MIGRATE,
275 click: () => this.openMigrateModal()
278 this.multisiteReplicationActions = [
280 permission: 'create',
282 name: this.actionLabels.SETUP_MULTISITE_REPLICATION,
284 this.router.navigate([BASE_URL, { outlets: { modal: 'setup-multisite-replication' } }])
288 this.startPollingMultisiteInfo();
289 this.mgrModuleService.updateCompleted$.subscribe(() => {
290 this.startPollingMultisiteInfo();
291 this.getRgwModuleStatus();
293 // Only get the module status if you can read from configOpt
294 if (this.permissions.configOpt.read) this.getRgwModuleStatus();
297 startPollingMultisiteInfo(): void {
298 const observables = [
299 this.rgwRealmService.getAllRealmsInfo(),
300 this.rgwZonegroupService.getAllZonegroupsInfo(),
301 this.rgwZoneService.getAllZonesInfo()
305 this.sub.unsubscribe();
308 this.sub = this.timerService
309 .get(() => forkJoin(observables), this.timerServiceVariable.TIMER_SERVICE_PERIOD * 2)
311 (multisiteInfo: [object, object, object]) => {
312 this.multisiteInfo = multisiteInfo;
313 this.loadingIndicator = false;
314 this.nodes = this.abstractTreeData(multisiteInfo);
321 this.sub.unsubscribe();
324 private getRgwModuleStatus() {
325 this.rgwMultisiteService.getRgwModuleStatus().subscribe((status: boolean) => {
326 this.rgwModuleStatus = status;
330 private abstractTreeData(multisiteInfo: [object, object, object]): any[] {
331 let allNodes: object[] = [];
333 let firstChildNodes = {};
334 let allFirstChildNodes = [];
335 let secondChildNodes = {};
336 let allSecondChildNodes: {}[] = [];
337 this.realms = multisiteInfo[0]['realms'];
338 this.zonegroups = multisiteInfo[1]['zonegroups'];
339 this.zones = multisiteInfo[2]['zones'];
340 this.defaultRealmId = multisiteInfo[0]['default_realm'];
341 this.defaultZonegroupId = multisiteInfo[1]['default_zonegroup'];
342 this.defaultZoneId = multisiteInfo[2]['default_zone'];
343 this.defaultsInfo = this.getDefaultsEntities(
345 this.defaultZonegroupId,
348 if (this.realms.length > 0) {
349 // get tree for realm -> zonegroup -> zone
350 for (const realm of this.realms) {
351 const result = this.rgwRealmService.getRealmTree(realm, this.defaultRealmId);
352 rootNodes = result['nodes'];
353 this.realmIds = this.realmIds.concat(result['realmIds']);
354 for (const zonegroup of this.zonegroups) {
355 if (zonegroup.realm_id === realm.id) {
356 firstChildNodes = this.rgwZonegroupService.getZonegroupTree(
358 this.defaultZonegroupId,
361 for (const zone of zonegroup.zones) {
362 const zoneResult = this.rgwZoneService.getZoneTree(
369 secondChildNodes = zoneResult['nodes'];
370 this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
371 allSecondChildNodes.push(secondChildNodes);
372 secondChildNodes = {};
374 allSecondChildNodes = allSecondChildNodes.map((x) => ({
376 parentNode: firstChildNodes
378 firstChildNodes['children'] = allSecondChildNodes;
379 allSecondChildNodes = [];
380 allFirstChildNodes.push(firstChildNodes);
381 firstChildNodes = {};
384 allFirstChildNodes = allFirstChildNodes.map((x) => ({ ...x, parentNode: rootNodes }));
385 rootNodes['children'] = allFirstChildNodes;
386 allNodes.push({ ...rootNodes, label: rootNodes?.['name'] || rootNodes?.['id'] });
387 firstChildNodes = {};
388 secondChildNodes = {};
390 allFirstChildNodes = [];
391 allSecondChildNodes = [];
394 if (this.zonegroups.length > 0) {
395 // get tree for zonegroup -> zone (standalone zonegroups that don't match a realm eg(initial default))
396 for (const zonegroup of this.zonegroups) {
397 if (!this.realmIds.includes(zonegroup.realm_id)) {
398 rootNodes = this.rgwZonegroupService.getZonegroupTree(zonegroup, this.defaultZonegroupId);
399 for (const zone of zonegroup.zones) {
400 const zoneResult = this.rgwZoneService.getZoneTree(
406 firstChildNodes = zoneResult['nodes'];
407 this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
408 allFirstChildNodes.push(firstChildNodes);
409 firstChildNodes = {};
411 allFirstChildNodes = allFirstChildNodes.map((x) => ({ ...x, parentNode: rootNodes }));
412 rootNodes['children'] = allFirstChildNodes;
413 allNodes.push({ ...rootNodes, label: rootNodes?.['name'] || rootNodes?.['id'] });
414 firstChildNodes = {};
416 allFirstChildNodes = [];
420 if (this.zones.length > 0) {
421 // get tree for standalone zones(zones that do not belong to a zonegroup)
422 for (const zone of this.zones) {
423 if (this.zoneIds.length > 0 && !this.zoneIds.includes(zone.id)) {
424 const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId, this.zones);
425 rootNodes = zoneResult['nodes'];
426 allNodes.push({ ...rootNodes, label: rootNodes?.['name'] || rootNodes?.['id'] });
431 if (this.realms.length < 1 && this.zonegroups.length < 1 && this.zones.length < 1) {
441 this.evaluateMigrateAndReplicationActions();
442 this.rgwMultisiteService.restartGatewayMessage$.subscribe((value) => {
443 if (value !== null) {
444 this.restartGatewayMessage = value;
446 this.checkRestartGatewayMessage();
452 checkRestartGatewayMessage() {
453 this.rgwDaemonService.list().subscribe((data: any) => {
454 const realmName = data.map((item: { [x: string]: any }) => item['realm_name']);
456 this.defaultRealmId !== '' &&
457 this.defaultZonegroupId !== '' &&
458 this.defaultZoneId !== '' &&
459 realmName.includes('')
461 this.restartGatewayMessage = true;
463 this.restartGatewayMessage = false;
469 defaultRealmId: string,
470 defaultZonegroupId: string,
471 defaultZoneId: string
473 const defaultRealm = this.realms?.find((x: { id: string }) => x.id === defaultRealmId);
474 const defaultZonegroup = this.zonegroups?.find(
475 (x: { id: string }) => x.id === defaultZonegroupId
477 const defaultZone = this.zones?.find((x: { id: string }) => x.id === defaultZoneId);
480 defaultRealmName: defaultRealm?.name,
481 defaultZonegroupName: defaultZonegroup?.name,
482 defaultZoneName: defaultZone?.name
486 onNodeSelected(node: Node) {
487 this.metadataTitle = node?.value?.name;
488 this.metadata = node?.value?.info;
489 this.activeNodeId = node?.value?.id;
490 node.expanded = true;
494 let isMasterZone = true;
495 if (this.defaultRealmId === '') {
496 return this.messages.noDefaultRealm;
498 this.zonegroups.forEach((zgp: any) => {
499 if (_.isEmpty(zgp.master_zone)) {
500 isMasterZone = false;
506 'Please create a master zone for each existing zonegroup to enable this feature';
508 return this.messages.noMasterZone;
511 this.editTitle = 'Edit';
518 evaluateMigrateAndReplicationActions() {
520 this.realms.length === 0 &&
521 this.zonegroups.length === 1 &&
522 this.zonegroups[0].name === 'default' &&
523 this.zones.length === 1 &&
524 this.zones[0].name === 'default'
526 this.showMigrateAndReplicationActions = true;
528 this.showMigrateAndReplicationActions = false;
530 return this.showMigrateAndReplicationActions;
533 isDeleteDisabled(node: Node): { isDisabled: boolean; deleteTitle: string } {
534 let isDisabled: boolean = false;
535 let deleteTitle: string = this.deleteTitle;
536 let masterZonegroupCount: number = 0;
537 if (node?.value?.type === 'realm' && node?.data?.is_default && this.realms.length < 2) {
541 if (node?.data?.type === 'zonegroup') {
542 if (this.zonegroups.length < 2) {
543 deleteTitle = 'You can not delete the only zonegroup available';
545 } else if (node?.data?.is_default) {
546 deleteTitle = 'You can not delete the default zonegroup';
548 } else if (node?.data?.is_master) {
549 for (let zonegroup of this.zonegroups) {
550 if (zonegroup.is_master === true) {
551 masterZonegroupCount++;
552 if (masterZonegroupCount > 1) break;
555 if (masterZonegroupCount < 2) {
556 deleteTitle = 'You can not delete the only master zonegroup available';
562 if (node?.data?.type === 'zone') {
563 if (this.zones.length < 2) {
564 deleteTitle = 'You can not delete the only zone available';
566 } else if (node?.data?.is_default) {
567 deleteTitle = 'You can not delete the default zone';
569 } else if (node?.data?.is_master && node?.data?.zone_zonegroup.zones.length < 2) {
571 'You can not delete the master zone as there are no more zones in this zonegroup';
577 this.deleteTitle = 'Delete';
580 return { isDisabled, deleteTitle };
584 if (node?.data?.type === 'realm') {
585 const modalRef = this.cdsModalService.show(DeleteConfirmationModalComponent, {
586 itemDescription: $localize`${node?.data?.type} ${node?.data?.name}`,
587 itemNames: [`${node?.data?.name}`],
588 submitAction: () => {
589 this.rgwRealmService.delete(node?.data?.name).subscribe(
591 this.notificationService.show(
592 NotificationType.success,
593 $localize`Realm: '${node?.data?.name}' deleted successfully`
595 this.cdsModalService.dismissAll();
598 this.cdsModalService.stopLoadingSpinner(modalRef.deletionForm);
603 } else if (node?.data?.type === 'zonegroup') {
604 this.modalRef = this.modalService.show(RgwMultisiteZonegroupDeletionFormComponent, {
607 } else if (node?.data?.type === 'zone') {
608 this.modalRef = this.modalService.show(RgwMultisiteZoneDeletionFormComponent, {
615 this.mgrModuleService.updateModuleState(
620 'Enabled RGW Module',