]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
51136e5d107f6002a2054ed02c7efc1ef350401d
[ceph-ci.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   title: string = 'Edit';
83   showMigrateAction: boolean = false;
84
85   constructor(
86     private modalService: ModalService,
87     private timerService: TimerService,
88     private authStorageService: AuthStorageService,
89     public actionLabels: ActionLabelsI18n,
90     public timerServiceVariable: TimerServiceInterval,
91     public rgwRealmService: RgwRealmService,
92     public rgwZonegroupService: RgwZonegroupService,
93     public rgwZoneService: RgwZoneService,
94     private notificationService: NotificationService
95   ) {
96     this.permission = this.authStorageService.getPermissions().rgw;
97     const createRealmAction: CdTableAction = {
98       permission: 'create',
99       icon: Icons.add,
100       name: this.actionLabels.CREATE + ' Realm',
101       click: () => this.openModal('realm')
102     };
103     const createZonegroupAction: CdTableAction = {
104       permission: 'create',
105       icon: Icons.add,
106       name: this.actionLabels.CREATE + ' Zonegroup',
107       click: () => this.openModal('zonegroup'),
108       disable: () => this.getDisable()
109     };
110     const createZoneAction: CdTableAction = {
111       permission: 'create',
112       icon: Icons.add,
113       name: this.actionLabels.CREATE + ' Zone',
114       click: () => this.openModal('zone')
115     };
116     const migrateMultsiteAction: CdTableAction = {
117       permission: 'read',
118       icon: Icons.exchange,
119       name: this.actionLabels.MIGRATE,
120       click: () => this.openMigrateModal()
121     };
122     this.createTableActions = [createRealmAction, createZonegroupAction, createZoneAction];
123     this.migrateTableAction = [migrateMultsiteAction];
124   }
125
126   openModal(entity: any, edit = false) {
127     const entityName = edit ? entity.data.type : entity;
128     const action = edit ? 'edit' : 'create';
129     const initialState = {
130       resource: entityName,
131       action: action,
132       info: entity,
133       defaultsInfo: this.defaultsInfo,
134       multisiteInfo: this.multisiteInfo
135     };
136     if (entityName === 'realm') {
137       this.bsModalRef = this.modalService.show(RgwMultisiteRealmFormComponent, initialState, {
138         size: 'lg'
139       });
140     } else if (entityName === 'zonegroup') {
141       this.bsModalRef = this.modalService.show(RgwMultisiteZonegroupFormComponent, initialState, {
142         size: 'lg'
143       });
144     } else {
145       this.bsModalRef = this.modalService.show(RgwMultisiteZoneFormComponent, initialState, {
146         size: 'lg'
147       });
148     }
149   }
150
151   openMigrateModal() {
152     const initialState = {
153       multisiteInfo: this.multisiteInfo
154     };
155     this.bsModalRef = this.modalService.show(RgwMultisiteMigrateComponent, initialState, {
156       size: 'lg'
157     });
158   }
159
160   ngOnInit() {
161     const observables = [
162       this.rgwRealmService.getAllRealmsInfo(),
163       this.rgwZonegroupService.getAllZonegroupsInfo(),
164       this.rgwZoneService.getAllZonesInfo()
165     ];
166     this.sub = this.timerService
167       .get(() => forkJoin(observables), this.timerServiceVariable.TIMER_SERVICE_PERIOD * 2)
168       .subscribe(
169         (multisiteInfo: [object, object, object]) => {
170           this.multisiteInfo = multisiteInfo;
171           this.loadingIndicator = false;
172           this.nodes = this.abstractTreeData(multisiteInfo);
173         },
174         (_error) => {}
175       );
176   }
177
178   ngOnDestroy() {
179     this.sub.unsubscribe();
180   }
181
182   private abstractTreeData(multisiteInfo: [object, object, object]): any[] {
183     let allNodes: object[] = [];
184     let rootNodes = {};
185     let firstChildNodes = {};
186     let allFirstChildNodes = [];
187     let secondChildNodes = {};
188     let allSecondChildNodes: {}[] = [];
189     this.realms = multisiteInfo[0]['realms'];
190     this.zonegroups = multisiteInfo[1]['zonegroups'];
191     this.zones = multisiteInfo[2]['zones'];
192     this.defaultRealmId = multisiteInfo[0]['default_realm'];
193     this.defaultZonegroupId = multisiteInfo[1]['default_zonegroup'];
194     this.defaultZoneId = multisiteInfo[2]['default_zone'];
195     this.defaultsInfo = this.getDefaultsEntities(
196       this.defaultRealmId,
197       this.defaultZonegroupId,
198       this.defaultZoneId
199     );
200     if (this.realms.length > 0) {
201       // get tree for realm -> zonegroup -> zone
202       for (const realm of this.realms) {
203         const result = this.rgwRealmService.getRealmTree(realm, this.defaultRealmId);
204         rootNodes = result['nodes'];
205         this.realmIds = this.realmIds.concat(result['realmIds']);
206         for (const zonegroup of this.zonegroups) {
207           if (zonegroup.realm_id === realm.id) {
208             firstChildNodes = this.rgwZonegroupService.getZonegroupTree(
209               zonegroup,
210               this.defaultZonegroupId,
211               realm
212             );
213             for (const zone of zonegroup.zones) {
214               const zoneResult = this.rgwZoneService.getZoneTree(
215                 zone,
216                 this.defaultZoneId,
217                 zonegroup,
218                 realm
219               );
220               secondChildNodes = zoneResult['nodes'];
221               this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
222               allSecondChildNodes.push(secondChildNodes);
223               secondChildNodes = {};
224             }
225             firstChildNodes['children'] = allSecondChildNodes;
226             allSecondChildNodes = [];
227             allFirstChildNodes.push(firstChildNodes);
228             firstChildNodes = {};
229           }
230         }
231         rootNodes['children'] = allFirstChildNodes;
232         allNodes.push(rootNodes);
233         firstChildNodes = {};
234         secondChildNodes = {};
235         rootNodes = {};
236         allFirstChildNodes = [];
237         allSecondChildNodes = [];
238       }
239     }
240     if (this.zonegroups.length > 0) {
241       // get tree for zonegroup -> zone (standalone zonegroups that don't match a realm eg(initial default))
242       for (const zonegroup of this.zonegroups) {
243         if (!this.realmIds.includes(zonegroup.realm_id)) {
244           rootNodes = this.rgwZonegroupService.getZonegroupTree(zonegroup, this.defaultZonegroupId);
245           for (const zone of zonegroup.zones) {
246             const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId, zonegroup);
247             firstChildNodes = zoneResult['nodes'];
248             this.zoneIds = this.zoneIds.concat(zoneResult['zoneIds']);
249             allFirstChildNodes.push(firstChildNodes);
250             firstChildNodes = {};
251           }
252           rootNodes['children'] = allFirstChildNodes;
253           allNodes.push(rootNodes);
254           firstChildNodes = {};
255           rootNodes = {};
256           allFirstChildNodes = [];
257         }
258       }
259     }
260     if (this.zones.length > 0) {
261       // get tree for standalone zones(zones that do not belong to a zonegroup)
262       for (const zone of this.zones) {
263         if (this.zoneIds.length > 0 && !this.zoneIds.includes(zone.id)) {
264           const zoneResult = this.rgwZoneService.getZoneTree(zone, this.defaultZoneId);
265           rootNodes = zoneResult['nodes'];
266           allNodes.push(rootNodes);
267           rootNodes = {};
268         }
269       }
270     }
271     if (this.realms.length < 1 && this.zonegroups.length < 1 && this.zones.length < 1) {
272       return [
273         {
274           name: 'No nodes!'
275         }
276       ];
277     }
278     this.realmIds = [];
279     this.zoneIds = [];
280     this.getDisableMigrate();
281     return allNodes;
282   }
283
284   getDefaultsEntities(
285     defaultRealmId: string,
286     defaultZonegroupId: string,
287     defaultZoneId: string
288   ): any {
289     const defaultRealm = this.realms.find((x: { id: string }) => x.id === defaultRealmId);
290     const defaultZonegroup = this.zonegroups.find(
291       (x: { id: string }) => x.id === defaultZonegroupId
292     );
293     const defaultZone = this.zones.find((x: { id: string }) => x.id === defaultZoneId);
294     const defaultRealmName = defaultRealm !== undefined ? defaultRealm.name : null;
295     const defaultZonegroupName = defaultZonegroup !== undefined ? defaultZonegroup.name : null;
296     const defaultZoneName = defaultZone !== undefined ? defaultZone.name : null;
297     return {
298       defaultRealmName: defaultRealmName,
299       defaultZonegroupName: defaultZonegroupName,
300       defaultZoneName: defaultZoneName
301     };
302   }
303
304   onNodeSelected(tree: TreeModel, node: TreeNode) {
305     TREE_ACTIONS.ACTIVATE(tree, node, true);
306     this.metadataTitle = node.data.name;
307     this.metadata = node.data.info;
308     node.data.show = true;
309   }
310
311   onUpdateData() {
312     this.tree.treeModel.expandAll();
313   }
314
315   getDisable() {
316     let isMasterZone = true;
317     if (this.defaultRealmId === '') {
318       return this.messages.noDefaultRealm;
319     } else {
320       this.zonegroups.forEach((zgp: any) => {
321         if (_.isEmpty(zgp.master_zone)) {
322           isMasterZone = false;
323         }
324       });
325       if (!isMasterZone) {
326         this.title =
327           'Please create a master zone for each existing zonegroup to enable this feature';
328         return this.messages.noMasterZone;
329       } else {
330         this.title = 'Edit';
331         return false;
332       }
333     }
334   }
335
336   getDisableMigrate() {
337     if (
338       this.realms.length === 0 &&
339       this.zonegroups.length === 1 &&
340       this.zonegroups[0].name === 'default' &&
341       this.zones.length === 1 &&
342       this.zones[0].name === 'default'
343     ) {
344       this.showMigrateAction = true;
345     } else {
346       this.showMigrateAction = false;
347     }
348     return this.showMigrateAction;
349   }
350
351   delete(node: TreeNode) {
352     if (node.data.type === 'realm') {
353       this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
354         itemDescription: $localize`${node.data.type} ${node.data.name}`,
355         itemNames: [`${node.data.name}`],
356         submitAction: () => {
357           this.rgwRealmService.delete(node.data.name).subscribe(
358             () => {
359               this.modalRef.close();
360               this.notificationService.show(
361                 NotificationType.success,
362                 $localize`Realm: '${node.data.name}' deleted successfully`
363               );
364             },
365             () => {
366               this.modalRef.componentInstance.stopLoadingSpinner();
367             }
368           );
369         }
370       });
371     } else if (node.data.type === 'zonegroup') {
372       this.modalRef = this.modalService.show(RgwMultisiteZonegroupDeletionFormComponent, {
373         zonegroup: node.data
374       });
375     } else if (node.data.type === 'zone') {
376       this.modalRef = this.modalService.show(RgwMultisiteZoneDeletionFormComponent, {
377         zone: node.data
378       });
379     }
380   }
381 }