1 import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
8 } from '@circlon/angular-tree-component';
9 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
10 import _ from 'lodash';
12 import { forkJoin, Subscription, timer as observableTimer } from 'rxjs';
13 import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
14 import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
15 import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
16 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
17 import { ActionLabelsI18n, TimerServiceInterval } from '~/app/shared/constants/app.constants';
18 import { Icons } from '~/app/shared/enum/icons.enum';
19 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
20 import { CdTableAction } from '~/app/shared/models/cd-table-action';
21 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
22 import { Permission } from '~/app/shared/models/permissions';
23 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
24 import { ModalService } from '~/app/shared/services/modal.service';
25 import { NotificationService } from '~/app/shared/services/notification.service';
26 import { TimerService } from '~/app/shared/services/timer.service';
27 import { RgwRealm, RgwZone, RgwZonegroup } from '../models/rgw-multisite';
28 import { RgwMultisiteMigrateComponent } from '../rgw-multisite-migrate/rgw-multisite-migrate.component';
29 import { RgwMultisiteZoneDeletionFormComponent } from '../models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component';
30 import { RgwMultisiteZonegroupDeletionFormComponent } from '../models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component';
31 import { RgwMultisiteExportComponent } from '../rgw-multisite-export/rgw-multisite-export.component';
32 import { RgwMultisiteImportComponent } from '../rgw-multisite-import/rgw-multisite-import.component';
33 import { RgwMultisiteRealmFormComponent } from '../rgw-multisite-realm-form/rgw-multisite-realm-form.component';
34 import { RgwMultisiteZoneFormComponent } from '../rgw-multisite-zone-form/rgw-multisite-zone-form.component';
35 import { RgwMultisiteZonegroupFormComponent } from '../rgw-multisite-zonegroup-form/rgw-multisite-zonegroup-form.component';
36 import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
37 import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
38 import { BlockUI, NgBlockUI } from 'ng-block-ui';
39 import { Router } from '@angular/router';
42 selector: 'cd-rgw-multisite-details',
43 templateUrl: './rgw-multisite-details.component.html',
44 styleUrls: ['./rgw-multisite-details.component.scss']
46 export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
47 private sub = new Subscription();
49 @ViewChild('tree') tree: TreeComponent;
52 noDefaultRealm: $localize`Please create a default realm first to enable this feature`,
53 noMasterZone: $localize`Please create a master zone for each zone group to enable this feature`,
54 noRealmExists: $localize`No realm exists`,
55 disableExport: $localize`Please create master zone group and master zone for each of the realms`
62 permission: Permission;
63 selection = new CdTableSelection();
64 createTableActions: CdTableAction[];
65 migrateTableAction: CdTableAction[];
66 importAction: CdTableAction[];
67 exportAction: CdTableAction[];
68 loadingIndicator = true;
70 treeOptions: ITreeOptions = {
71 useVirtualScroll: true,
76 click: this.onNodeSelected.bind(this)
80 modalRef: NgbModalRef;
82 realms: RgwRealm[] = [];
83 zonegroups: RgwZonegroup[] = [];
84 zones: RgwZone[] = [];
86 metadataTitle: string;
87 bsModalRef: NgbModalRef;
88 realmIds: string[] = [];
89 zoneIds: string[] = [];
91 defaultZonegroupId = '';
93 multisiteInfo: object[] = [];
94 defaultsInfo: string[] = [];
95 showMigrateAction: boolean = false;
96 editTitle: string = 'Edit';
97 deleteTitle: string = 'Delete';
99 rgwModuleStatus: boolean;
100 restartGatewayMessage = false;
101 rgwModuleData: string | any[] = [];
104 private modalService: ModalService,
105 private timerService: TimerService,
106 private authStorageService: AuthStorageService,
107 public actionLabels: ActionLabelsI18n,
108 public timerServiceVariable: TimerServiceInterval,
109 public router: Router,
110 public rgwRealmService: RgwRealmService,
111 public rgwZonegroupService: RgwZonegroupService,
112 public rgwZoneService: RgwZoneService,
113 public rgwDaemonService: RgwDaemonService,
114 public mgrModuleService: MgrModuleService,
115 private notificationService: NotificationService
117 this.permission = this.authStorageService.getPermissions().rgw;
120 openModal(entity: any, edit = false) {
121 const entityName = edit ? entity.data.type : entity;
122 const action = edit ? 'edit' : 'create';
123 const initialState = {
124 resource: entityName,
127 defaultsInfo: this.defaultsInfo,
128 multisiteInfo: this.multisiteInfo
130 if (entityName === 'realm') {
131 this.bsModalRef = this.modalService.show(RgwMultisiteRealmFormComponent, initialState, {
134 } else if (entityName === 'zonegroup') {
135 this.bsModalRef = this.modalService.show(RgwMultisiteZonegroupFormComponent, initialState, {
139 this.bsModalRef = this.modalService.show(RgwMultisiteZoneFormComponent, initialState, {
146 const initialState = {
147 multisiteInfo: this.multisiteInfo
149 this.bsModalRef = this.modalService.show(RgwMultisiteMigrateComponent, initialState, {
155 const initialState = {
156 multisiteInfo: this.multisiteInfo
158 this.bsModalRef = this.modalService.show(RgwMultisiteImportComponent, initialState, {
164 const initialState = {
165 defaultsInfo: this.defaultsInfo,
166 multisiteInfo: this.multisiteInfo
168 this.bsModalRef = this.modalService.show(RgwMultisiteExportComponent, initialState, {
174 this.realms.forEach((realm: any) => {
175 this.zonegroups.forEach((zonegroup) => {
176 if (realm.id === zonegroup.realm_id) {
177 if (zonegroup.is_master && zonegroup.master_zone !== '') {
178 this.disableExport = false;
183 if (!this.rgwModuleStatus) {
186 if (this.realms.length < 1) {
187 return this.messages.noRealmExists;
188 } else if (this.disableExport) {
189 return this.messages.disableExport;
196 if (!this.rgwModuleStatus) {
204 const createRealmAction: CdTableAction = {
205 permission: 'create',
207 name: this.actionLabels.CREATE + ' Realm',
208 click: () => this.openModal('realm')
210 const createZonegroupAction: CdTableAction = {
211 permission: 'create',
213 name: this.actionLabels.CREATE + ' Zone Group',
214 click: () => this.openModal('zonegroup'),
215 disable: () => this.getDisable()
217 const createZoneAction: CdTableAction = {
218 permission: 'create',
220 name: this.actionLabels.CREATE + ' Zone',
221 click: () => this.openModal('zone')
223 const migrateMultsiteAction: CdTableAction = {
225 icon: Icons.exchange,
226 name: this.actionLabels.MIGRATE,
227 click: () => this.openMigrateModal()
229 const importMultsiteAction: CdTableAction = {
231 icon: Icons.download,
232 name: this.actionLabels.IMPORT,
233 click: () => this.openImportModal(),
234 disable: () => this.getDisableImport()
236 const exportMultsiteAction: CdTableAction = {
239 name: this.actionLabels.EXPORT,
240 click: () => this.openExportModal(),
241 disable: () => this.getDisableExport()
243 this.createTableActions = [createRealmAction, createZonegroupAction, createZoneAction];
244 this.migrateTableAction = [migrateMultsiteAction];
245 this.importAction = [importMultsiteAction];
246 this.exportAction = [exportMultsiteAction];
248 const observables = [
249 this.rgwRealmService.getAllRealmsInfo(),
250 this.rgwZonegroupService.getAllZonegroupsInfo(),
251 this.rgwZoneService.getAllZonesInfo()
253 this.sub = this.timerService
254 .get(() => forkJoin(observables), this.timerServiceVariable.TIMER_SERVICE_PERIOD * 2)
256 (multisiteInfo: [object, object, object]) => {
257 this.multisiteInfo = multisiteInfo;
258 this.loadingIndicator = false;
259 this.nodes = this.abstractTreeData(multisiteInfo);
263 this.mgrModuleService.list().subscribe((moduleData: any) => {
264 this.rgwModuleData = moduleData.filter((module: object) => module['name'] === 'rgw');
265 if (this.rgwModuleData.length > 0) {
266 this.rgwModuleStatus = this.rgwModuleData[0].enabled;
271 /* setConfigValues() {
272 this.rgwDaemonService
274 this.defaultsInfo['defaultRealmName'],
275 this.defaultsInfo['defaultZonegroupName'],
276 this.defaultsInfo['defaultZoneName']
278 .subscribe(() => {});
282 this.sub.unsubscribe();
285 private abstractTreeData(multisiteInfo: [object, object, object]): any[] {
286 let allNodes: object[] = [];
288 let firstChildNodes = {};
289 let allFirstChildNodes = [];
290 let secondChildNodes = {};
291 let allSecondChildNodes: {}[] = [];
292 this.realms = multisiteInfo[0]['realms'];
293 this.zonegroups = multisiteInfo[1]['zonegroups'];
294 this.zones = multisiteInfo[2]['zones'];
295 this.defaultRealmId = multisiteInfo[0]['default_realm'];
296 this.defaultZonegroupId = multisiteInfo[1]['default_zonegroup'];
297 this.defaultZoneId = multisiteInfo[2]['default_zone'];
298 this.defaultsInfo = this.getDefaultsEntities(
300 this.defaultZonegroupId,
303 if (this.realms.length > 0) {
304 // get tree for realm -> zonegroup -> zone
305 for (const realm of this.realms) {
306 const result = this.rgwRealmService.getRealmTree(realm, this.defaultRealmId);
307 rootNodes = result['nodes'];
308 this.realmIds = this.realmIds.concat(result['realmIds']);
309 for (const zonegroup of this.zonegroups) {
310 if (zonegroup.realm_id === realm.id) {
311 firstChildNodes = this.rgwZonegroupService.getZonegroupTree(
313 this.defaultZonegroupId,
316 for (const zone of zonegroup.zones) {
317 const zoneResult = this.rgwZoneService.getZoneTree(
324 secondChildNodes = zoneResult['nodes'];
325 this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
326 allSecondChildNodes.push(secondChildNodes);
327 secondChildNodes = {};
329 firstChildNodes['children'] = allSecondChildNodes;
330 allSecondChildNodes = [];
331 allFirstChildNodes.push(firstChildNodes);
332 firstChildNodes = {};
335 rootNodes['children'] = allFirstChildNodes;
336 allNodes.push(rootNodes);
337 firstChildNodes = {};
338 secondChildNodes = {};
340 allFirstChildNodes = [];
341 allSecondChildNodes = [];
344 if (this.zonegroups.length > 0) {
345 // get tree for zonegroup -> zone (standalone zonegroups that don't match a realm eg(initial default))
346 for (const zonegroup of this.zonegroups) {
347 if (!this.realmIds.includes(zonegroup.realm_id)) {
348 rootNodes = this.rgwZonegroupService.getZonegroupTree(zonegroup, this.defaultZonegroupId);
349 for (const zone of zonegroup.zones) {
350 const zoneResult = this.rgwZoneService.getZoneTree(
356 firstChildNodes = zoneResult['nodes'];
357 this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
358 allFirstChildNodes.push(firstChildNodes);
359 firstChildNodes = {};
361 rootNodes['children'] = allFirstChildNodes;
362 allNodes.push(rootNodes);
363 firstChildNodes = {};
365 allFirstChildNodes = [];
369 if (this.zones.length > 0) {
370 // get tree for standalone zones(zones that do not belong to a zonegroup)
371 for (const zone of this.zones) {
372 if (this.zoneIds.length > 0 && !this.zoneIds.includes(zone.id)) {
373 const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId, this.zones);
374 rootNodes = zoneResult['nodes'];
375 allNodes.push(rootNodes);
380 if (this.realms.length < 1 && this.zonegroups.length < 1 && this.zones.length < 1) {
389 this.getDisableMigrate();
390 this.rgwDaemonService.list().subscribe((data: any) => {
391 const realmName = data.map((item: { [x: string]: any }) => item['realm_name']);
393 this.defaultRealmId != '' &&
394 this.defaultZonegroupId != '' &&
395 this.defaultZoneId != '' &&
396 realmName.includes('')
398 this.restartGatewayMessage = true;
405 defaultRealmId: string,
406 defaultZonegroupId: string,
407 defaultZoneId: string
409 const defaultRealm = this.realms.find((x: { id: string }) => x.id === defaultRealmId);
410 const defaultZonegroup = this.zonegroups.find(
411 (x: { id: string }) => x.id === defaultZonegroupId
413 const defaultZone = this.zones.find((x: { id: string }) => x.id === defaultZoneId);
414 const defaultRealmName = defaultRealm !== undefined ? defaultRealm.name : null;
415 const defaultZonegroupName = defaultZonegroup !== undefined ? defaultZonegroup.name : null;
416 const defaultZoneName = defaultZone !== undefined ? defaultZone.name : null;
418 defaultRealmName: defaultRealmName,
419 defaultZonegroupName: defaultZonegroupName,
420 defaultZoneName: defaultZoneName
424 onNodeSelected(tree: TreeModel, node: TreeNode) {
425 TREE_ACTIONS.ACTIVATE(tree, node, true);
426 this.metadataTitle = node.data.name;
427 this.metadata = node.data.info;
428 node.data.show = true;
432 this.tree.treeModel.expandAll();
436 let isMasterZone = true;
437 if (this.defaultRealmId === '') {
438 return this.messages.noDefaultRealm;
440 this.zonegroups.forEach((zgp: any) => {
441 if (_.isEmpty(zgp.master_zone)) {
442 isMasterZone = false;
447 'Please create a master zone for each existing zonegroup to enable this feature';
448 return this.messages.noMasterZone;
450 this.editTitle = 'Edit';
456 getDisableMigrate() {
458 this.realms.length === 0 &&
459 this.zonegroups.length === 1 &&
460 this.zonegroups[0].name === 'default' &&
461 this.zones.length === 1 &&
462 this.zones[0].name === 'default'
464 this.showMigrateAction = true;
466 this.showMigrateAction = false;
468 return this.showMigrateAction;
471 isDeleteDisabled(node: TreeNode): boolean {
472 let disable: boolean = false;
473 let masterZonegroupCount: number = 0;
474 if (node.data.type === 'realm' && node.data.is_default && this.realms.length < 2) {
478 if (node.data.type === 'zonegroup') {
479 if (this.zonegroups.length < 2) {
480 this.deleteTitle = 'You can not delete the only zonegroup available';
482 } else if (node.data.is_default) {
483 this.deleteTitle = 'You can not delete the default zonegroup';
485 } else if (node.data.is_master) {
486 for (let zonegroup of this.zonegroups) {
487 if (zonegroup.is_master === true) {
488 masterZonegroupCount++;
489 if (masterZonegroupCount > 1) break;
492 if (masterZonegroupCount < 2) {
493 this.deleteTitle = 'You can not delete the only master zonegroup available';
499 if (node.data.type === 'zone') {
500 if (this.zones.length < 2) {
501 this.deleteTitle = 'You can not delete the only zone available';
503 } else if (node.data.is_default) {
504 this.deleteTitle = 'You can not delete the default zone';
506 } else if (node.data.is_master && node.data.zone_zonegroup.zones.length < 2) {
508 'You can not delete the master zone as there are no more zones in this zonegroup';
514 this.deleteTitle = 'Delete';
520 delete(node: TreeNode) {
521 if (node.data.type === 'realm') {
522 this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
523 itemDescription: $localize`${node.data.type} ${node.data.name}`,
524 itemNames: [`${node.data.name}`],
525 submitAction: () => {
526 this.rgwRealmService.delete(node.data.name).subscribe(
528 this.modalRef.close();
529 this.notificationService.show(
530 NotificationType.success,
531 $localize`Realm: '${node.data.name}' deleted successfully`
535 this.modalRef.componentInstance.stopLoadingSpinner();
540 } else if (node.data.type === 'zonegroup') {
541 this.modalRef = this.modalService.show(RgwMultisiteZonegroupDeletionFormComponent, {
544 } else if (node.data.type === 'zone') {
545 this.modalRef = this.modalService.show(RgwMultisiteZoneDeletionFormComponent, {
553 const fnWaitUntilReconnected = () => {
554 observableTimer(2000).subscribe(() => {
555 // Trigger an API request to check if the connection is
557 this.mgrModuleService.list().subscribe(
559 // Resume showing the notification toasties.
560 this.notificationService.suspendToasties(false);
561 // Unblock the whole UI.
563 // Reload the data table content.
564 this.notificationService.show(NotificationType.success, $localize`Enabled RGW Module`);
565 this.router.navigateByUrl('/', { skipLocationChange: true }).then(() => {
566 this.router.navigate(['/rgw/multisite']);
568 // Reload the data table content.
571 fnWaitUntilReconnected();
577 if (!this.rgwModuleStatus) {
578 $obs = this.mgrModuleService.enable('rgw');
583 // Suspend showing the notification toasties.
584 this.notificationService.suspendToasties(true);
585 // Block the whole UI to prevent user interactions until
586 // the connection to the backend is reestablished
587 this.blockUI.start($localize`Reconnecting, please wait ...`);
588 fnWaitUntilReconnected();