]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
623826b73639a177660130f4fc450a0566462b91
[ceph.git] /
1 import { Component, OnInit } from '@angular/core';
2 import { UntypedFormControl, Validators } from '@angular/forms';
3 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
4 import { Subscription, forkJoin } from 'rxjs';
5 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
6 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
7 import { WizardStepModel } from '~/app/shared/models/wizard-steps';
8 import { WizardStepsService } from '~/app/shared/services/wizard-steps.service';
9 import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
10 import { RgwDaemon } from '../models/rgw-daemon';
11 import { MultiClusterService } from '~/app/shared/api/multi-cluster.service';
12 import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
13 import { Icons } from '~/app/shared/enum/icons.enum';
14 import { SelectOption } from '~/app/shared/components/select/select-option.model';
15 import _ from 'lodash';
16 import { SelectMessages } from '~/app/shared/components/select/select-messages.model';
17 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
18 import { NotificationService } from '~/app/shared/services/notification.service';
19 import { ActivatedRoute, Router } from '@angular/router';
20 import { map, switchMap } from 'rxjs/operators';
21 import { BaseModal, Step } from 'carbon-components-angular';
22 import { Location } from '@angular/common';
23
24 @Component({
25   selector: 'cd-rgw-multisite-wizard',
26   templateUrl: './rgw-multisite-wizard.component.html',
27   styleUrls: ['./rgw-multisite-wizard.component.scss']
28 })
29 export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
30   multisiteSetupForm: CdFormGroup;
31   currentStep: WizardStepModel;
32   currentStepSub: Subscription;
33   permissions: Permissions;
34   stepTitles: Step[] = [
35     {
36       label: 'Create Realm & Zonegroup'
37     },
38     {
39       label: 'Create Zone'
40     },
41     {
42       label: 'Select Cluster'
43     }
44   ];
45   stepsToSkip: { [steps: string]: boolean } = {};
46   daemons: RgwDaemon[] = [];
47   selectedCluster = '';
48   clusterDetailsArray: any;
49   isMultiClusterConfigured = false;
50   exportTokenForm: CdFormGroup;
51   realms: any;
52   loading = false;
53   pageURL: string;
54   icons = Icons;
55   rgwEndpoints: { value: any[]; options: any[]; messages: any };
56
57   constructor(
58     private wizardStepsService: WizardStepsService,
59     public activeModal: NgbActiveModal,
60     public actionLabels: ActionLabelsI18n,
61     private rgwDaemonService: RgwDaemonService,
62     private multiClusterService: MultiClusterService,
63     private rgwMultisiteService: RgwMultisiteService,
64     public notificationService: NotificationService,
65     private router: Router,
66     private route: ActivatedRoute,
67     private location: Location
68   ) {
69     super();
70     this.pageURL = 'rgw/multisite/configuration';
71     this.currentStepSub = this.wizardStepsService
72       .getCurrentStep()
73       .subscribe((step: WizardStepModel) => {
74         this.currentStep = step;
75       });
76     this.currentStep.stepIndex = 0;
77     this.createForm();
78     this.rgwEndpoints = {
79       value: [],
80       options: [],
81       messages: new SelectMessages({
82         empty: $localize`There are no endpoints.`,
83         filter: $localize`Select endpoints`
84       })
85     };
86   }
87
88   ngOnInit(): void {
89     this.open = this.route.outlet === 'modal';
90     this.rgwDaemonService
91       .list()
92       .pipe(
93         switchMap((daemons) => {
94           this.daemons = daemons;
95           const daemonStatsObservables = daemons.map((daemon) =>
96             this.rgwDaemonService.get(daemon.id).pipe(
97               map((daemonStats) => ({
98                 hostname: daemon.server_hostname,
99                 port: daemon.port,
100                 frontendConfig: daemonStats['rgw_metadata']['frontend_config#0']
101               }))
102             )
103           );
104           return forkJoin(daemonStatsObservables);
105         })
106       )
107       .subscribe((daemonStatsArray) => {
108         this.rgwEndpoints.value = daemonStatsArray.map((daemonStats) => {
109           const protocol = daemonStats.frontendConfig.includes('ssl_port') ? 'https' : 'http';
110           return `${protocol}://${daemonStats.hostname}:${daemonStats.port}`;
111         });
112         const options: SelectOption[] = this.rgwEndpoints.value.map(
113           (endpoint: string) => new SelectOption(false, endpoint, '')
114         );
115         this.rgwEndpoints.options = [...options];
116       });
117
118     this.multiClusterService.getCluster().subscribe((clusters) => {
119       this.clusterDetailsArray = Object.values(clusters['config'])
120         .flat()
121         .filter((cluster) => cluster['cluster_alias'] !== 'local-cluster');
122       this.isMultiClusterConfigured = this.clusterDetailsArray.length > 0;
123       if (!this.isMultiClusterConfigured) {
124         this.stepTitles = [
125           {
126             label: 'Create Realm & Zonegroup'
127           },
128           {
129             label: 'Create Zone'
130           },
131           {
132             label: 'Export Multi-site token'
133           }
134         ];
135         this.stepTitles.forEach((steps, index) => {
136           steps.onClick = () => (this.currentStep.stepIndex = index);
137         });
138       } else {
139         this.selectedCluster = this.clusterDetailsArray[0]['name'];
140       }
141     });
142   }
143
144   createForm() {
145     this.multisiteSetupForm = new CdFormGroup({
146       realmName: new UntypedFormControl('default_realm', {
147         validators: [Validators.required]
148       }),
149       zonegroupName: new UntypedFormControl('default_zonegroup', {
150         validators: [Validators.required]
151       }),
152       zonegroup_endpoints: new UntypedFormControl(null, [Validators.required]),
153       zoneName: new UntypedFormControl('default_zone', {
154         validators: [Validators.required]
155       }),
156       zone_endpoints: new UntypedFormControl(null, {
157         validators: [Validators.required]
158       }),
159       username: new UntypedFormControl('default_system_user', {
160         validators: [Validators.required]
161       }),
162       cluster: new UntypedFormControl(null, {
163         validators: [Validators.required]
164       })
165     });
166
167     if (!this.isMultiClusterConfigured) {
168       this.exportTokenForm = new CdFormGroup({});
169     }
170   }
171
172   showSubmitButtonLabel() {
173     if (this.isMultiClusterConfigured) {
174       return !this.wizardStepsService.isLastStep()
175         ? this.actionLabels.NEXT
176         : $localize`Configure Multi-site`;
177     } else {
178       return !this.wizardStepsService.isLastStep() ? this.actionLabels.NEXT : $localize`Close`;
179     }
180   }
181
182   showCancelButtonLabel() {
183     return !this.wizardStepsService.isFirstStep()
184       ? this.actionLabels.BACK
185       : this.actionLabels.CANCEL;
186   }
187
188   onNextStep() {
189     if (!this.wizardStepsService.isLastStep()) {
190       this.wizardStepsService.getCurrentStep().subscribe((step: WizardStepModel) => {
191         this.currentStep = step;
192       });
193       if (this.currentStep.stepIndex === 2 && !this.isMultiClusterConfigured) {
194         this.onSubmit();
195       } else {
196         this.wizardStepsService.moveToNextStep();
197       }
198     } else {
199       this.onSubmit();
200     }
201   }
202
203   onSubmit() {
204     this.loading = true;
205     const values = this.multisiteSetupForm.value;
206     const realmName = values['realmName'];
207     const zonegroupName = values['zonegroupName'];
208     const zonegroupEndpoints = this.rgwEndpoints.value.join(',');
209     const zoneName = values['zoneName'];
210     const zoneEndpoints = this.rgwEndpoints.value.join(',');
211     const username = values['username'];
212     if (!this.isMultiClusterConfigured) {
213       if (this.wizardStepsService.isLastStep()) {
214         this.activeModal.close();
215         this.refreshMultisitePage();
216       } else {
217         this.rgwMultisiteService
218           .setUpMultisiteReplication(
219             realmName,
220             zonegroupName,
221             zonegroupEndpoints,
222             zoneName,
223             zoneEndpoints,
224             username
225           )
226           .subscribe((data: object[]) => {
227             this.loading = false;
228             this.realms = data;
229             this.wizardStepsService.moveToNextStep();
230             this.showSuccessNotification();
231           });
232       }
233     } else {
234       const cluster = values['cluster'];
235       this.rgwMultisiteService
236         .setUpMultisiteReplication(
237           realmName,
238           zonegroupName,
239           zonegroupEndpoints,
240           zoneName,
241           zoneEndpoints,
242           username,
243           cluster
244         )
245         .subscribe(
246           () => {
247             this.showSuccessNotification();
248             this.activeModal.close();
249             this.refreshMultisitePage();
250           },
251           () => {
252             this.multisiteSetupForm.setErrors({ cdSubmitButton: true });
253           }
254         );
255     }
256   }
257
258   showSuccessNotification() {
259     this.notificationService.show(
260       NotificationType.success,
261       $localize`Multi-site setup completed successfully.`
262     );
263   }
264
265   refreshMultisitePage() {
266     const currentRoute = this.router.url.split('?')[0];
267     const navigateTo = currentRoute.includes('multisite') ? '/pool' : '/';
268     this.router.navigateByUrl(navigateTo, { skipLocationChange: true }).then(() => {
269       this.router.navigate([currentRoute]);
270     });
271   }
272
273   onPreviousStep() {
274     if (!this.wizardStepsService.isFirstStep()) {
275       this.wizardStepsService.moveToPreviousStep();
276     } else {
277       this.location.back();
278     }
279   }
280
281   onSkip() {
282     const stepTitle = this.stepTitles[this.currentStep.stepIndex];
283     this.stepsToSkip[stepTitle.label] = true;
284     this.onNextStep();
285   }
286 }