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