]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
e3174e230816c1d6061067f85a340ecca3659692
[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   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   handleError(error: any): void {
159     if (error.error.code === 'connection_refused') {
160       this.connectionVerified = false;
161       this.showCrossOriginError = true;
162       this.connectionMessage = error.error.detail;
163       this.crossOriginCmd = `ceph config set mgr mgr/dashboard/cross_origin_url ${window.location.origin} `;
164     } else {
165       this.connectionVerified = false;
166       this.connectionMessage = error.error.detail;
167     }
168     this.remoteClusterForm.setErrors({ cdSubmitButton: true });
169     this.notificationService.show(
170       NotificationType.error,
171       $localize`Connection to the cluster failed`
172     );
173   }
174
175   handleSuccess(message?: string): void {
176     this.notificationService.show(NotificationType.success, message);
177     this.submitAction.emit();
178     this.activeModal.close();
179   }
180
181   onSubmit() {
182     const url = this.remoteClusterForm.getValue('remoteClusterUrl');
183     const updatedUrl = url.endsWith('/') ? url.slice(0, -1) : url;
184     const clusterAlias = this.remoteClusterForm.getValue('clusterAlias');
185     const username = this.remoteClusterForm.getValue('username');
186     const password = this.remoteClusterForm.getValue('password');
187     const token = this.remoteClusterForm.getValue('apiToken');
188     const clusterFsid = this.remoteClusterForm.getValue('clusterFsid');
189     const ssl = this.remoteClusterForm.getValue('ssl');
190     const ssl_certificate = this.remoteClusterForm.getValue('ssl_cert')?.trim();
191
192     const commonSubscribtion = {
193       error: (error: any) => this.handleError(error),
194       next: (response: any) => {
195         if (response === true) {
196           this.handleSuccess($localize`Cluster connected successfully`);
197         }
198       }
199     };
200
201     switch (this.action) {
202       case 'edit':
203         this.subs.add(
204           this.multiClusterService
205             .editCluster(this.cluster.url, clusterAlias, this.cluster.user)
206             .subscribe({
207               ...commonSubscribtion,
208               complete: () => this.handleSuccess($localize`Cluster updated successfully`)
209             })
210         );
211         break;
212       case 'reconnect':
213         this.subs.add(
214           this.multiClusterService
215             .reConnectCluster(updatedUrl, username, password, token, ssl, ssl_certificate)
216             .subscribe(commonSubscribtion)
217         );
218         break;
219       case 'connect':
220         this.subs.add(
221           this.multiClusterService
222             .addCluster(
223               updatedUrl,
224               clusterAlias,
225               username,
226               password,
227               token,
228               window.location.origin,
229               clusterFsid,
230               ssl,
231               ssl_certificate
232             )
233             .subscribe(commonSubscribtion)
234         );
235         break;
236       default:
237         break;
238     }
239   }
240
241   toggleToken() {
242     this.showToken = !this.showToken;
243   }
244
245   fileUpload(files: FileList, controlName: string) {
246     const file: File = files[0];
247     const reader = new FileReader();
248     reader.addEventListener('load', (event: ProgressEvent<FileReader>) => {
249       const control: AbstractControl = this.remoteClusterForm.get(controlName);
250       control.setValue(event.target.result);
251       control.markAsDirty();
252       control.markAsTouched();
253       control.updateValueAndValidity();
254     });
255     reader.readAsText(file, 'utf8');
256   }
257 }