1 import { ChangeDetectorRef, Component, NgZone, OnInit } from '@angular/core';
2 import { Location } from '@angular/common';
3 import { UntypedFormControl, Validators } from '@angular/forms';
4 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
5 import { Observable, Subscription, forkJoin } from 'rxjs';
6 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
7 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
8 import { WizardStepModel } from '~/app/shared/models/wizard-steps';
9 import { WizardStepsService } from '~/app/shared/services/wizard-steps.service';
10 import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
11 import { RgwDaemon } from '../models/rgw-daemon';
12 import { MultiClusterService } from '~/app/shared/api/multi-cluster.service';
13 import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
14 import { Icons } from '~/app/shared/enum/icons.enum';
15 import { SelectOption } from '~/app/shared/components/select/select-option.model';
16 import _ from 'lodash';
17 import { SelectMessages } from '~/app/shared/components/select/select-messages.model';
18 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
19 import { NotificationService } from '~/app/shared/services/notification.service';
20 import { ActivatedRoute } from '@angular/router';
21 import { map, switchMap } from 'rxjs/operators';
22 import { BaseModal, Step } from 'carbon-components-angular';
23 import { SummaryService } from '~/app/shared/services/summary.service';
24 import { ExecutingTask } from '~/app/shared/models/executing-task';
26 STEP_TITLES_EXISTING_REALM,
27 STEP_TITLES_MULTI_CLUSTER_CONFIGURED,
28 STEP_TITLES_SINGLE_CLUSTER
29 } from './multisite-wizard-steps.enum';
30 import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
31 import { MultiCluster, MultiClusterConfig } from '~/app/shared/models/multi-cluster';
32 import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
34 interface DaemonStats {
36 [key: string]: string;
40 interface EndpointInfo {
43 frontendConfig: string;
52 NewRealm = 'newRealm',
53 ExistingRealm = 'existingRealm'
57 selector: 'cd-rgw-multisite-wizard',
58 templateUrl: './rgw-multisite-wizard.component.html',
59 styleUrls: ['./rgw-multisite-wizard.component.scss']
61 export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
62 multisiteSetupForm: CdFormGroup;
63 currentStep: WizardStepModel;
64 currentStepSub: Subscription;
65 permissions: Permissions;
66 stepTitles: Step[] = STEP_TITLES_MULTI_CLUSTER_CONFIGURED.map((title) => ({
69 stepsToSkip: { [steps: string]: boolean } = {};
70 daemons: RgwDaemon[] = [];
72 clusterDetailsArray: MultiCluster[] = [];
73 isMultiClusterConfigured = false;
74 exportTokenForm: CdFormGroup;
79 rgwEndpoints: { value: any[]; options: any[]; messages: any };
80 executingTask: ExecutingTask;
81 setupCompleted = false;
82 showConfigType = false;
83 realmList: string[] = [];
84 rgwModuleStatus: boolean;
87 private wizardStepsService: WizardStepsService,
88 public activeModal: NgbActiveModal,
89 public actionLabels: ActionLabelsI18n,
90 private rgwDaemonService: RgwDaemonService,
91 private multiClusterService: MultiClusterService,
92 private rgwMultisiteService: RgwMultisiteService,
93 private rgwRealmService: RgwRealmService,
94 public notificationService: NotificationService,
95 private route: ActivatedRoute,
96 private summaryService: SummaryService,
97 private location: Location,
98 private cdr: ChangeDetectorRef,
99 private mgrModuleService: MgrModuleService,
103 this.pageURL = 'rgw/multisite/configuration';
104 this.currentStepSub = this.wizardStepsService
106 .subscribe((step: WizardStepModel) => {
107 this.currentStep = step;
109 this.currentStep.stepIndex = 0;
111 this.rgwEndpoints = {
114 messages: new SelectMessages({
115 empty: $localize`There are no endpoints.`,
116 filter: $localize`Select endpoints`
122 this.open = this.route.outlet === 'modal';
123 this.loadRGWEndpoints();
124 this.multiClusterService.getCluster().subscribe((clusters: MultiClusterConfig) => {
125 const currentUrl = clusters['current_url'];
126 this.clusterDetailsArray = Object.values(clusters['config'])
128 .filter((cluster) => cluster['url'] !== currentUrl);
129 this.isMultiClusterConfigured = this.clusterDetailsArray.length > 0;
130 this.stepTitles = (this.isMultiClusterConfigured
131 ? STEP_TITLES_MULTI_CLUSTER_CONFIGURED
132 : STEP_TITLES_SINGLE_CLUSTER
133 ).map((label, index) => ({
135 onClick: () => (this.currentStep.stepIndex = index)
137 this.wizardStepsService.setTotalSteps(this.stepTitles.length);
138 this.selectedCluster = this.isMultiClusterConfigured
139 ? this.clusterDetailsArray[0]['name']
143 this.summaryService.subscribe((summary) => {
144 this.zone.run(() => {
145 this.executingTask = summary.executing_tasks.find((task) =>
146 task.name.includes('progress/Multisite-Setup')
148 this.cdr.detectChanges();
152 this.stepTitles.forEach((step) => {
153 this.stepsToSkip[step.label] = false;
156 this.rgwRealmService.list().subscribe((realms: string[]) => {
157 this.realmList = realms;
158 this.showConfigType = this.realmList.length > 0;
159 if (this.showConfigType) {
160 this.multisiteSetupForm.get('selectedRealm')?.setValue(this.realmList[0]);
161 this.cdr.detectChanges();
165 this.rgwMultisiteService.getRgwModuleStatus().subscribe((status: boolean) => {
166 this.rgwModuleStatus = status;
170 private loadRGWEndpoints(): void {
171 this.rgwDaemonService
174 switchMap((daemons: RgwDaemon[]) => {
175 this.daemons = daemons;
176 return this.fetchDaemonStats(daemons);
179 .subscribe((daemonStatsArray: EndpointInfo[]) => {
180 this.populateRGWEndpoints(daemonStatsArray);
184 private fetchDaemonStats(daemons: RgwDaemon[]): Observable<EndpointInfo[]> {
185 const observables = daemons.map((daemon) =>
186 this.rgwDaemonService.get(daemon.id).pipe(
187 map((daemonStats: DaemonStats) => ({
188 hostname: daemon.server_hostname,
190 frontendConfig: daemonStats?.rgw_metadata?.['frontend_config#0'] || ''
194 return forkJoin(observables);
197 private populateRGWEndpoints(statsArray: EndpointInfo[]): void {
198 this.rgwEndpoints.value = statsArray.map((stats: EndpointInfo) => {
199 const protocol = stats.frontendConfig.includes('ssl_port') ? Protocol.HTTPS : Protocol.HTTP;
200 return `${protocol}://${stats.hostname}:${stats.port}`;
202 this.rgwEndpoints.options = this.rgwEndpoints.value.map(
203 (endpoint) => new SelectOption(false, endpoint, '')
205 this.cdr.detectChanges();
209 this.multisiteSetupForm = new CdFormGroup({
210 realmName: new UntypedFormControl('default_realm', {
211 validators: [Validators.required]
213 zonegroupName: new UntypedFormControl('default_zonegroup', {
214 validators: [Validators.required]
216 zonegroup_endpoints: new UntypedFormControl(null, [Validators.required]),
217 zoneName: new UntypedFormControl('default_zone', {
218 validators: [Validators.required]
220 zone_endpoints: new UntypedFormControl(null, {
221 validators: [Validators.required]
223 username: new UntypedFormControl('default_system_user', {
224 validators: [Validators.required]
226 cluster: new UntypedFormControl(null, {
227 validators: [Validators.required]
229 replicationZoneName: new UntypedFormControl('new_replicated_zone', {
230 validators: [Validators.required]
232 configType: new UntypedFormControl(ConfigType.NewRealm, {}),
233 selectedRealm: new UntypedFormControl(null, {})
236 if (!this.isMultiClusterConfigured) {
237 this.exportTokenForm = new CdFormGroup({});
241 showSubmitButtonLabel() {
242 if (this.wizardStepsService.isLastStep()) {
243 if (!this.setupCompleted) {
244 if (this.isMultiClusterConfigured) {
245 return $localize`Configure Multi-Site`;
247 return $localize`Export Multi-Site token`;
250 return $localize`Close`;
253 return $localize`Next`;
257 showCancelButtonLabel() {
258 return !this.wizardStepsService.isFirstStep()
259 ? this.actionLabels.BACK
260 : this.actionLabels.CANCEL;
264 if (!this.wizardStepsService.isLastStep()) {
265 this.wizardStepsService.moveToNextStep();
267 if (this.setupCompleted) {
273 this.wizardStepsService.getCurrentStep().subscribe((step: WizardStepModel) => {
274 this.currentStep = step;
275 if (this.currentStep.stepIndex === 2 && this.isMultiClusterConfigured) {
276 this.stepsToSkip['Select Cluster'] = false;
284 const proceedWithSetup = () => {
285 this.cdr.detectChanges();
286 const values = this.multisiteSetupForm.getRawValue();
287 const realmName = values['realmName'];
288 const zonegroupName = values['zonegroupName'];
289 const zonegroupEndpoints = this.rgwEndpoints.value.join(',');
290 const zoneName = values['zoneName'];
291 const zoneEndpoints = this.rgwEndpoints.value.join(',');
292 const username = values['username'];
294 if (!this.isMultiClusterConfigured || this.stepsToSkip['Select Cluster']) {
295 this.rgwMultisiteService
296 .setUpMultisiteReplication(
304 .subscribe((data: object[]) => {
305 this.setupCompleted = true;
306 this.rgwMultisiteService.setRestartGatewayMessage(false);
307 this.loading = false;
309 this.showSuccessNotification();
312 const cluster = values['cluster'];
313 const replicationZoneName = values['replicationZoneName'];
314 let selectedRealmName = '';
316 if (this.multisiteSetupForm.get('configType').value === ConfigType.ExistingRealm) {
317 selectedRealmName = this.multisiteSetupForm.get('selectedRealm').value;
320 this.rgwMultisiteService
321 .setUpMultisiteReplication(
330 this.clusterDetailsArray,
335 this.setupCompleted = true;
336 this.rgwMultisiteService.setRestartGatewayMessage(false);
337 this.loading = false;
338 this.showSuccessNotification();
341 this.multisiteSetupForm.setErrors({ cdSubmitButton: true });
347 if (!this.rgwModuleStatus) {
348 this.mgrModuleService.updateModuleState(
355 $localize`RGW module is being enabled. Waiting for the system to reconnect...`
357 const subscription = this.mgrModuleService.updateCompleted$.subscribe(() => {
358 subscription.unsubscribe();
366 showSuccessNotification() {
367 this.notificationService.show(
368 NotificationType.success,
369 $localize`Multi-site setup completed successfully.`
374 if (!this.wizardStepsService.isFirstStep()) {
375 this.wizardStepsService.moveToPreviousStep();
377 this.location.back();
382 const stepTitle = this.stepTitles[this.currentStep.stepIndex];
383 this.stepsToSkip[stepTitle.label] = true;
388 this.location.back();
391 onConfigTypeChange() {
392 const configType = this.multisiteSetupForm.get('configType')?.value;
393 if (configType === ConfigType.ExistingRealm) {
394 this.stepTitles = STEP_TITLES_EXISTING_REALM.map((title) => ({
397 this.stepTitles.forEach((steps, index) => {
398 steps.onClick = () => (this.currentStep.stepIndex = index);
400 } else if (this.isMultiClusterConfigured) {
401 this.stepTitles = STEP_TITLES_MULTI_CLUSTER_CONFIGURED.map((title) => ({
405 this.stepTitles = STEP_TITLES_SINGLE_CLUSTER.map((title) => ({
409 this.wizardStepsService.setTotalSteps(this.stepTitles.length);