]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
b5b5f9ca2e16886cf3114befae8be1060396c963
[ceph.git] /
1 import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
2 import { AbstractControl, FormControl, Validators } from '@angular/forms';
3 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
4 import _ from 'lodash';
5 import { Subscription } from 'rxjs';
6 import { MultiClusterService } from '~/app/shared/api/multi-cluster.service';
7 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
8 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
9 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
10 import { CdValidators } from '~/app/shared/forms/cd-validators';
11 import { MultiCluster } from '~/app/shared/models/multi-cluster';
12 import { NotificationService } from '~/app/shared/services/notification.service';
13
14 @Component({
15   selector: 'cd-multi-cluster-form',
16   templateUrl: './multi-cluster-form.component.html',
17   styleUrls: ['./multi-cluster-form.component.scss']
18 })
19 export class MultiClusterFormComponent implements OnInit, OnDestroy {
20   @Output()
21   submitAction = new EventEmitter();
22   readonly endpoints = /^((https?:\/\/)|(www.))(?:([a-zA-Z]+)|(\d+\.\d+.\d+.\d+)):\d{2,5}\/?$/;
23   readonly ipv4Rgx = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i;
24   readonly ipv6Rgx = /^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$/i;
25   clusterApiUrlCmd = 'ceph mgr services';
26   prometheusApiUrlCmd = 'ceph config get mgr mgr/dashboard/PROMETHEUS_API_HOST';
27   crossOriginCmd = `ceph dashboard set-cross-origin-url ${window.location.origin}`;
28   remoteClusterForm: CdFormGroup;
29   showToken = false;
30   connectionVerified: boolean;
31   connectionMessage = '';
32   private subs = new Subscription();
33   showCrossOriginError = false;
34   action: string;
35   cluster: MultiCluster;
36   clustersData: MultiCluster[];
37   clusterAliasNames: string[];
38   clusterUrls: string[];
39   clusterUsers: string[];
40   clusterUrlUserMap: Map<string, string>;
41
42   constructor(
43     public activeModal: NgbActiveModal,
44     public actionLabels: ActionLabelsI18n,
45     public notificationService: NotificationService,
46     private multiClusterService: MultiClusterService
47   ) {
48     this.createForm();
49   }
50   ngOnInit(): void {
51     if (this.action === 'edit') {
52       this.remoteClusterForm.get('remoteClusterUrl').setValue(this.cluster.url);
53       this.remoteClusterForm.get('remoteClusterUrl').disable();
54       this.remoteClusterForm.get('clusterAlias').setValue(this.cluster.cluster_alias);
55       this.remoteClusterForm.get('ssl').setValue(this.cluster.ssl_verify);
56       this.remoteClusterForm.get('ssl_cert').setValue(this.cluster.ssl_certificate);
57     }
58     if (this.action === 'reconnect') {
59       this.remoteClusterForm.get('remoteClusterUrl').setValue(this.cluster.url);
60       this.remoteClusterForm.get('remoteClusterUrl').disable();
61       this.remoteClusterForm.get('clusterAlias').setValue(this.cluster.cluster_alias);
62       this.remoteClusterForm.get('clusterAlias').disable();
63       this.remoteClusterForm.get('username').setValue(this.cluster.user);
64       this.remoteClusterForm.get('username').disable();
65       this.remoteClusterForm.get('clusterFsid').setValue(this.cluster.name);
66       this.remoteClusterForm.get('clusterFsid').disable();
67       this.remoteClusterForm.get('ssl').setValue(this.cluster.ssl_verify);
68       this.remoteClusterForm.get('ssl_cert').setValue(this.cluster.ssl_certificate);
69     }
70     [this.clusterAliasNames, this.clusterUrls, this.clusterUsers] = [
71       'cluster_alias',
72       'url',
73       'user'
74     ].map((prop) => this.clustersData?.map((cluster) => cluster[prop]));
75   }
76
77   createForm() {
78     this.remoteClusterForm = new CdFormGroup({
79       showToken: new FormControl(false),
80       username: new FormControl('', [
81         CdValidators.custom('uniqueUrlandUser', (username: string) => {
82           let remoteClusterUrl = '';
83           if (
84             this.remoteClusterForm &&
85             this.remoteClusterForm.getValue('remoteClusterUrl') &&
86             this.remoteClusterForm.getValue('remoteClusterUrl').endsWith('/')
87           ) {
88             remoteClusterUrl = this.remoteClusterForm.getValue('remoteClusterUrl').slice(0, -1);
89           } else if (this.remoteClusterForm) {
90             remoteClusterUrl = this.remoteClusterForm.getValue('remoteClusterUrl');
91           }
92           return (
93             this.remoteClusterForm &&
94             this.clusterUrls?.includes(remoteClusterUrl) &&
95             this.clusterUsers?.includes(username)
96           );
97         })
98       ]),
99       clusterFsid: new FormControl('', [
100         CdValidators.requiredIf({
101           showToken: true
102         })
103       ]),
104       prometheusApiUrl: new FormControl('', [
105         CdValidators.requiredIf({
106           showToken: true
107         })
108       ]),
109       password: new FormControl('', []),
110       remoteClusterUrl: new FormControl(null, {
111         validators: [
112           CdValidators.custom('endpoint', (value: string) => {
113             if (_.isEmpty(value)) {
114               return false;
115             } else {
116               return (
117                 !this.endpoints.test(value) &&
118                 !this.ipv4Rgx.test(value) &&
119                 !this.ipv6Rgx.test(value)
120               );
121             }
122           }),
123           Validators.required
124         ]
125       }),
126       apiToken: new FormControl('', [
127         CdValidators.requiredIf({
128           showToken: true
129         })
130       ]),
131       clusterAlias: new FormControl(null, {
132         validators: [
133           Validators.required,
134           CdValidators.custom('uniqueName', (clusterAlias: string) => {
135             return (
136               (this.action === 'connect' || this.action === 'edit') &&
137               this.clusterAliasNames &&
138               this.clusterAliasNames.indexOf(clusterAlias) !== -1
139             );
140           })
141         ]
142       }),
143       ssl: new FormControl(false),
144       ssl_cert: new FormControl('', {
145         validators: [
146           CdValidators.requiredIf({
147             ssl: true
148           })
149         ]
150       })
151     });
152   }
153
154   ngOnDestroy() {
155     this.subs.unsubscribe();
156   }
157
158   onSubmit() {
159     const url = this.remoteClusterForm.getValue('remoteClusterUrl');
160     const updatedUrl = url.endsWith('/') ? url.slice(0, -1) : url;
161     const clusterAlias = this.remoteClusterForm.getValue('clusterAlias');
162     const prometheusApiUrl = this.remoteClusterForm.getValue('prometheusApiUrl');
163     const username = this.remoteClusterForm.getValue('username');
164     const password = this.remoteClusterForm.getValue('password');
165     const token = this.remoteClusterForm.getValue('apiToken');
166     const clusterFsid = this.remoteClusterForm.getValue('clusterFsid');
167     const ssl = this.remoteClusterForm.getValue('ssl');
168     const ssl_certificate = this.remoteClusterForm.getValue('ssl_cert')?.trim();
169
170     if (this.action === 'edit') {
171       this.subs.add(
172         this.multiClusterService
173           .editCluster(this.cluster.url, clusterAlias, this.cluster.user)
174           .subscribe({
175             error: () => {
176               this.remoteClusterForm.setErrors({ cdSubmitButton: true });
177             },
178             complete: () => {
179               this.notificationService.show(
180                 NotificationType.success,
181                 $localize`Cluster updated successfully`
182               );
183               this.submitAction.emit();
184               this.activeModal.close();
185             }
186           })
187       );
188     }
189
190     if (this.action === 'reconnect') {
191       this.subs.add(
192         this.multiClusterService
193           .reConnectCluster(updatedUrl, username, password, token, ssl, ssl_certificate)
194           .subscribe({
195             error: () => {
196               this.remoteClusterForm.setErrors({ cdSubmitButton: true });
197             },
198             complete: () => {
199               this.notificationService.show(
200                 NotificationType.success,
201                 $localize`Cluster reconnected successfully`
202               );
203               this.submitAction.emit();
204               this.activeModal.close();
205             }
206           })
207       );
208     }
209
210     if (this.action === 'connect') {
211       this.subs.add(
212         this.multiClusterService
213           .addCluster(
214             updatedUrl,
215             clusterAlias,
216             username,
217             password,
218             token,
219             window.location.origin,
220             clusterFsid,
221             prometheusApiUrl,
222             ssl,
223             ssl_certificate
224           )
225           .subscribe({
226             error: () => {
227               this.remoteClusterForm.setErrors({ cdSubmitButton: true });
228             },
229             complete: () => {
230               this.notificationService.show(
231                 NotificationType.success,
232                 $localize`Cluster connected successfully`
233               );
234               this.submitAction.emit();
235               this.activeModal.close();
236             }
237           })
238       );
239     }
240   }
241
242   verifyConnection() {
243     const url = this.remoteClusterForm.getValue('remoteClusterUrl');
244     const username = this.remoteClusterForm.getValue('username');
245     const password = this.remoteClusterForm.getValue('password');
246     const token = this.remoteClusterForm.getValue('apiToken');
247     const ssl = this.remoteClusterForm.getValue('ssl');
248     const ssl_certificate = this.remoteClusterForm.getValue('ssl_cert')?.trim();
249
250     this.subs.add(
251       this.multiClusterService
252         .verifyConnection(url, username, password, token, ssl, ssl_certificate)
253         .subscribe((resp: string) => {
254           switch (resp) {
255             case 'Connection successful':
256               this.connectionVerified = true;
257               this.connectionMessage = 'Connection Verified Successfully';
258               this.notificationService.show(
259                 NotificationType.success,
260                 $localize`Connection Verified Successfully`
261               );
262               break;
263
264             case 'Connection refused':
265               this.connectionVerified = false;
266               this.showCrossOriginError = true;
267               this.connectionMessage = resp;
268               this.notificationService.show(
269                 NotificationType.error,
270                 $localize`Connection to the cluster failed`
271               );
272               break;
273
274             default:
275               this.connectionVerified = false;
276               this.connectionMessage = resp;
277               this.notificationService.show(
278                 NotificationType.error,
279                 $localize`Connection to the cluster failed`
280               );
281               break;
282           }
283         })
284     );
285   }
286
287   toggleToken() {
288     this.showToken = !this.showToken;
289   }
290
291   fileUpload(files: FileList, controlName: string) {
292     const file: File = files[0];
293     const reader = new FileReader();
294     reader.addEventListener('load', (event: ProgressEvent<FileReader>) => {
295       const control: AbstractControl = this.remoteClusterForm.get(controlName);
296       control.setValue(event.target.result);
297       control.markAsDirty();
298       control.markAsTouched();
299       control.updateValueAndValidity();
300     });
301     reader.readAsText(file, 'utf8');
302   }
303 }