]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
29f9b9d9e9ceead2363f1d0b12ca55d9c37ec257
[ceph.git] /
1 import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
2 import {
3   TreeComponent,
4   ITreeOptions,
5   TreeModel,
6   TreeNode,
7   TREE_ACTIONS
8 } from '@circlon/angular-tree-component';
9 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
10 import _ from 'lodash';
11 import { forkJoin, Subscription } from 'rxjs';
12 import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
13 import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
14 import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
15 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
16 import { ActionLabelsI18n, TimerServiceInterval } from '~/app/shared/constants/app.constants';
17 import { Icons } from '~/app/shared/enum/icons.enum';
18 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
19 import { CdTableAction } from '~/app/shared/models/cd-table-action';
20 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
21 import { Permission } from '~/app/shared/models/permissions';
22 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
23 import { ModalService } from '~/app/shared/services/modal.service';
24 import { NotificationService } from '~/app/shared/services/notification.service';
25 import { TimerService } from '~/app/shared/services/timer.service';
26 import { RgwRealm, RgwZone, RgwZonegroup } from '../models/rgw-multisite';
27 import { RgwMultisiteMigrateComponent } from '../rgw-multisite-migrate/rgw-multisite-migrate.component';
28 import { RgwMultisiteZoneDeletionFormComponent } from '../models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component';
29 import { RgwMultisiteZonegroupDeletionFormComponent } from '../models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component';
30 import { RgwMultisiteRealmFormComponent } from '../rgw-multisite-realm-form/rgw-multisite-realm-form.component';
31 import { RgwMultisiteZoneFormComponent } from '../rgw-multisite-zone-form/rgw-multisite-zone-form.component';
32 import { RgwMultisiteZonegroupFormComponent } from '../rgw-multisite-zonegroup-form/rgw-multisite-zonegroup-form.component';
33
34 @Component({
35   selector: 'cd-rgw-multisite-details',
36   templateUrl: './rgw-multisite-details.component.html',
37   styleUrls: ['./rgw-multisite-details.component.scss']
38 })
39 export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
40   private sub = new Subscription();
41
42   @ViewChild('tree') tree: TreeComponent;
43
44   messages = {
45     noDefaultRealm: $localize`Please create a default realm first to enable this feature`,
46     noMasterZone: $localize`Please create a master zone for each zonegroup to enable this feature`,
47     disableMigrate: $localize`Deployment is already migrated to multi-site system.`
48   };
49
50   icons = Icons;
51   permission: Permission;
52   selection = new CdTableSelection();
53   createTableActions: CdTableAction[];
54   migrateTableAction: CdTableAction[];
55   loadingIndicator = true;
56   nodes: object[] = [];
57   treeOptions: ITreeOptions = {
58     useVirtualScroll: true,
59     nodeHeight: 22,
60     levelPadding: 20,
61     actionMapping: {
62       mouse: {
63         click: this.onNodeSelected.bind(this)
64       }
65     }
66   };
67   modalRef: NgbModalRef;
68
69   realms: RgwRealm[] = [];
70   zonegroups: RgwZonegroup[] = [];
71   zones: RgwZone[] = [];
72   metadata: any;
73   metadataTitle: string;
74   bsModalRef: NgbModalRef;
75   realmIds: string[] = [];
76   zoneIds: string[] = [];
77   defaultRealmId = '';
78   defaultZonegroupId = '';
79   defaultZoneId = '';
80   multisiteInfo: object[] = [];
81   defaultsInfo: string[] = [];
82   showMigrateAction: boolean = false;
83   editTitle: string = 'Edit';
84   deleteTitle: string = 'Delete';
85
86   constructor(
87     private modalService: ModalService,
88     private timerService: TimerService,
89     private authStorageService: AuthStorageService,
90     public actionLabels: ActionLabelsI18n,
91     public timerServiceVariable: TimerServiceInterval,
92     public rgwRealmService: RgwRealmService,
93     public rgwZonegroupService: RgwZonegroupService,
94     public rgwZoneService: RgwZoneService,
95     private notificationService: NotificationService
96   ) {
97     this.permission = this.authStorageService.getPermissions().rgw;
98     const createRealmAction: CdTableAction = {
99       permission: 'create',
100       icon: Icons.add,
101       name: this.actionLabels.CREATE + ' Realm',
102       click: () => this.openModal('realm')
103     };
104     const createZonegroupAction: CdTableAction = {
105       permission: 'create',
106       icon: Icons.add,
107       name: this.actionLabels.CREATE + ' Zonegroup',
108       click: () => this.openModal('zonegroup'),
109       disable: () => this.getDisable()
110     };
111     const createZoneAction: CdTableAction = {
112       permission: 'create',
113       icon: Icons.add,
114       name: this.actionLabels.CREATE + ' Zone',
115       click: () => this.openModal('zone')
116     };
117     const migrateMultsiteAction: CdTableAction = {
118       permission: 'read',
119       icon: Icons.exchange,
120       name: this.actionLabels.MIGRATE,
121       click: () => this.openMigrateModal()
122     };
123     this.createTableActions = [createRealmAction, createZonegroupAction, createZoneAction];
124     this.migrateTableAction = [migrateMultsiteAction];
125   }
126
127   openModal(entity: any, edit = false) {
128     const entityName = edit ? entity.data.type : entity;
129     const action = edit ? 'edit' : 'create';
130     const initialState = {
131       resource: entityName,
132       action: action,
133       info: entity,
134       defaultsInfo: this.defaultsInfo,
135       multisiteInfo: this.multisiteInfo
136     };
137     if (entityName === 'realm') {
138       this.bsModalRef = this.modalService.show(RgwMultisiteRealmFormComponent, initialState, {
139         size: 'lg'
140       });
141     } else if (entityName === 'zonegroup') {
142       this.bsModalRef = this.modalService.show(RgwMultisiteZonegroupFormComponent, initialState, {
143         size: 'lg'
144       });
145     } else {
146       this.bsModalRef = this.modalService.show(RgwMultisiteZoneFormComponent, initialState, {
147         size: 'lg'
148       });
149     }
150   }
151
152   openMigrateModal() {
153     const initialState = {
154       multisiteInfo: this.multisiteInfo
155     };
156     this.bsModalRef = this.modalService.show(RgwMultisiteMigrateComponent, initialState, {
157       size: 'lg'
158     });
159   }
160
161   ngOnInit() {
162     const observables = [
163       this.rgwRealmService.getAllRealmsInfo(),
164       this.rgwZonegroupService.getAllZonegroupsInfo(),
165       this.rgwZoneService.getAllZonesInfo()
166     ];
167     this.sub = this.timerService
168       .get(() => forkJoin(observables), this.timerServiceVariable.TIMER_SERVICE_PERIOD * 2)
169       .subscribe(
170         (multisiteInfo: [object, object, object]) => {
171           this.multisiteInfo = multisiteInfo;
172           this.loadingIndicator = false;
173           this.nodes = this.abstractTreeData(multisiteInfo);
174         },
175         (_error) => {}
176       );
177   }
178
179   ngOnDestroy() {
180     this.sub.unsubscribe();
181   }
182
183   private abstractTreeData(multisiteInfo: [object, object, object]): any[] {
184     let allNodes: object[] = [];
185     let rootNodes = {};
186     let firstChildNodes = {};
187     let allFirstChildNodes = [];
188     let secondChildNodes = {};
189     let allSecondChildNodes: {}[] = [];
190     this.realms = multisiteInfo[0]['realms'];
191     this.zonegroups = multisiteInfo[1]['zonegroups'];
192     this.zones = multisiteInfo[2]['zones'];
193     this.defaultRealmId = multisiteInfo[0]['default_realm'];
194     this.defaultZonegroupId = multisiteInfo[1]['default_zonegroup'];
195     this.defaultZoneId = multisiteInfo[2]['default_zone'];
196     this.defaultsInfo = this.getDefaultsEntities(
197       this.defaultRealmId,
198       this.defaultZonegroupId,
199       this.defaultZoneId
200     );
201     if (this.realms.length > 0) {
202       // get tree for realm -> zonegroup -> zone
203       for (const realm of this.realms) {
204         const result = this.rgwRealmService.getRealmTree(realm, this.defaultRealmId);
205         rootNodes = result['nodes'];
206         this.realmIds = this.realmIds.concat(result['realmIds']);
207         for (const zonegroup of this.zonegroups) {
208           if (zonegroup.realm_id === realm.id) {
209             firstChildNodes = this.rgwZonegroupService.getZonegroupTree(
210               zonegroup,
211               this.defaultZonegroupId,
212               realm
213             );
214             for (const zone of zonegroup.zones) {
215               const zoneResult = this.rgwZoneService.getZoneTree(
216                 zone,
217                 this.defaultZoneId,
218                 zonegroup,
219                 realm
220               );
221               secondChildNodes = zoneResult['nodes'];
222               this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
223               allSecondChildNodes.push(secondChildNodes);
224               secondChildNodes = {};
225             }
226             firstChildNodes['children'] = allSecondChildNodes;
227             allSecondChildNodes = [];
228             allFirstChildNodes.push(firstChildNodes);
229             firstChildNodes = {};
230           }
231         }
232         rootNodes['children'] = allFirstChildNodes;
233         allNodes.push(rootNodes);
234         firstChildNodes = {};
235         secondChildNodes = {};
236         rootNodes = {};
237         allFirstChildNodes = [];
238         allSecondChildNodes = [];
239       }
240     }
241     if (this.zonegroups.length > 0) {
242       // get tree for zonegroup -> zone (standalone zonegroups that don't match a realm eg(initial default))
243       for (const zonegroup of this.zonegroups) {
244         if (!this.realmIds.includes(zonegroup.realm_id)) {
245           rootNodes = this.rgwZonegroupService.getZonegroupTree(zonegroup, this.defaultZonegroupId);
246           for (const zone of zonegroup.zones) {
247             const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId, zonegroup);
248             firstChildNodes = zoneResult['nodes'];
249             this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
250             allFirstChildNodes.push(firstChildNodes);
251             firstChildNodes = {};
252           }
253           rootNodes['children'] = allFirstChildNodes;
254           allNodes.push(rootNodes);
255           firstChildNodes = {};
256           rootNodes = {};
257           allFirstChildNodes = [];
258         }
259       }
260     }
261     if (this.zones.length > 0) {
262       // get tree for standalone zones(zones that do not belong to a zonegroup)
263       for (const zone of this.zones) {
264         if (this.zoneIds.length > 0 && !this.zoneIds.includes(zone.id)) {
265           const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId);
266           rootNodes = zoneResult['nodes'];
267           allNodes.push(rootNodes);
268           rootNodes = {};
269         }
270       }
271     }
272     if (this.realms.length < 1 && this.zonegroups.length < 1 && this.zones.length < 1) {
273       return [
274         {
275           name: 'No nodes!'
276         }
277       ];
278     }
279     this.realmIds = [];
280     this.zoneIds = [];
281     this.getDisableMigrate();
282     return allNodes;
283   }
284
285   getDefaultsEntities(
286     defaultRealmId: string,
287     defaultZonegroupId: string,
288     defaultZoneId: string
289   ): any {
290     const defaultRealm = this.realms.find((x: { id: string }) => x.id === defaultRealmId);
291     const defaultZonegroup = this.zonegroups.find(
292       (x: { id: string }) => x.id === defaultZonegroupId
293     );
294     const defaultZone = this.zones.find((x: { id: string }) => x.id === defaultZoneId);
295     const defaultRealmName = defaultRealm !== undefined ? defaultRealm.name : null;
296     const defaultZonegroupName = defaultZonegroup !== undefined ? defaultZonegroup.name : null;
297     const defaultZoneName = defaultZone !== undefined ? defaultZone.name : null;
298     return {
299       defaultRealmName: defaultRealmName,
300       defaultZonegroupName: defaultZonegroupName,
301       defaultZoneName: defaultZoneName
302     };
303   }
304
305   onNodeSelected(tree: TreeModel, node: TreeNode) {
306     TREE_ACTIONS.ACTIVATE(tree, node, true);
307     this.metadataTitle = node.data.name;
308     this.metadata = node.data.info;
309     node.data.show = true;
310   }
311
312   onUpdateData() {
313     this.tree.treeModel.expandAll();
314   }
315
316   getDisable() {
317     let isMasterZone = true;
318     if (this.defaultRealmId === '') {
319       return this.messages.noDefaultRealm;
320     } else {
321       this.zonegroups.forEach((zgp: any) => {
322         if (_.isEmpty(zgp.master_zone)) {
323           isMasterZone = false;
324         }
325       });
326       if (!isMasterZone) {
327         this.editTitle =
328           'Please create a master zone for each existing zonegroup to enable this feature';
329         return this.messages.noMasterZone;
330       } else {
331         this.editTitle = 'Edit';
332         return false;
333       }
334     }
335   }
336
337   getDisableMigrate() {
338     if (
339       this.realms.length === 0 &&
340       this.zonegroups.length === 1 &&
341       this.zonegroups[0].name === 'default' &&
342       this.zones.length === 1 &&
343       this.zones[0].name === 'default'
344     ) {
345       this.showMigrateAction = true;
346     } else {
347       this.showMigrateAction = false;
348     }
349     return this.showMigrateAction;
350   }
351
352   isDeleteDisabled(node: TreeNode): boolean {
353     let disable: boolean = false;
354     let masterZonegroupCount: number = 0;
355     if (node.data.type === 'realm' && node.data.is_default && this.realms.length < 2) {
356       disable = true;
357     }
358
359     if (node.data.type === 'zonegroup') {
360       if (this.zonegroups.length < 2) {
361         this.deleteTitle = 'You can not delete the only zonegroup available';
362         disable = true;
363       } else if (node.data.is_default) {
364         this.deleteTitle = 'You can not delete the default zonegroup';
365         disable = true;
366       } else if (node.data.is_master) {
367         for (let zonegroup of this.zonegroups) {
368           if (zonegroup.is_master === true) {
369             masterZonegroupCount++;
370             if (masterZonegroupCount > 1) break;
371           }
372         }
373         if (masterZonegroupCount < 2) {
374           this.deleteTitle = 'You can not delete the only master zonegroup available';
375           disable = true;
376         }
377       }
378     }
379
380     if (node.data.type === 'zone') {
381       if (this.zones.length < 2) {
382         this.deleteTitle = 'You can not delete the only zone available';
383         disable = true;
384       } else if (node.data.is_default) {
385         this.deleteTitle = 'You can not delete the default zone';
386         disable = true;
387       } else if (node.data.is_master && node.data.zone_zonegroup.zones.length < 2) {
388         this.deleteTitle =
389           'You can not delete the master zone as there are no more zones in this zonegroup';
390         disable = true;
391       }
392     }
393
394     if (!disable) {
395       this.deleteTitle = 'Delete';
396     }
397
398     return disable;
399   }
400
401   delete(node: TreeNode) {
402     if (node.data.type === 'realm') {
403       this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
404         itemDescription: $localize`${node.data.type} ${node.data.name}`,
405         itemNames: [`${node.data.name}`],
406         submitAction: () => {
407           this.rgwRealmService.delete(node.data.name).subscribe(
408             () => {
409               this.modalRef.close();
410               this.notificationService.show(
411                 NotificationType.success,
412                 $localize`Realm: '${node.data.name}' deleted successfully`
413               );
414             },
415             () => {
416               this.modalRef.componentInstance.stopLoadingSpinner();
417             }
418           );
419         }
420       });
421     } else if (node.data.type === 'zonegroup') {
422       this.modalRef = this.modalService.show(RgwMultisiteZonegroupDeletionFormComponent, {
423         zonegroup: node.data
424       });
425     } else if (node.data.type === 'zone') {
426       this.modalRef = this.modalService.show(RgwMultisiteZoneDeletionFormComponent, {
427         zone: node.data
428       });
429     }
430   }
431 }