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';
15 selector: 'cd-multi-cluster-form',
16 templateUrl: './multi-cluster-form.component.html',
17 styleUrls: ['./multi-cluster-form.component.scss']
19 export class MultiClusterFormComponent implements OnInit, OnDestroy {
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;
30 connectionVerified: boolean;
31 connectionMessage = '';
32 private subs = new Subscription();
33 showCrossOriginError = false;
35 cluster: MultiCluster;
36 clustersData: MultiCluster[];
37 clusterAliasNames: string[];
38 clusterUrls: string[];
39 clusterUsers: string[];
40 clusterUrlUserMap: Map<string, string>;
43 public activeModal: NgbActiveModal,
44 public actionLabels: ActionLabelsI18n,
45 public notificationService: NotificationService,
46 private multiClusterService: MultiClusterService
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);
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);
70 [this.clusterAliasNames, this.clusterUrls, this.clusterUsers] = [
74 ].map((prop) => this.clustersData?.map((cluster) => cluster[prop]));
78 this.remoteClusterForm = new CdFormGroup({
79 showToken: new FormControl(false),
80 username: new FormControl('', [
81 CdValidators.custom('uniqueUrlandUser', (username: string) => {
82 let remoteClusterUrl = '';
84 this.remoteClusterForm &&
85 this.remoteClusterForm.getValue('remoteClusterUrl') &&
86 this.remoteClusterForm.getValue('remoteClusterUrl').endsWith('/')
88 remoteClusterUrl = this.remoteClusterForm.getValue('remoteClusterUrl').slice(0, -1);
89 } else if (this.remoteClusterForm) {
90 remoteClusterUrl = this.remoteClusterForm.getValue('remoteClusterUrl');
93 this.remoteClusterForm &&
94 this.clusterUrls?.includes(remoteClusterUrl) &&
95 this.clusterUsers?.includes(username)
99 clusterFsid: new FormControl('', [
100 CdValidators.requiredIf({
104 prometheusApiUrl: new FormControl('', [
105 CdValidators.requiredIf({
109 password: new FormControl('', []),
110 remoteClusterUrl: new FormControl(null, {
112 CdValidators.custom('endpoint', (value: string) => {
113 if (_.isEmpty(value)) {
117 !this.endpoints.test(value) &&
118 !this.ipv4Rgx.test(value) &&
119 !this.ipv6Rgx.test(value)
126 apiToken: new FormControl('', [
127 CdValidators.requiredIf({
131 clusterAlias: new FormControl(null, {
134 CdValidators.custom('uniqueName', (clusterAlias: string) => {
136 (this.action === 'connect' || this.action === 'edit') &&
137 this.clusterAliasNames &&
138 this.clusterAliasNames.indexOf(clusterAlias) !== -1
143 ssl: new FormControl(false),
144 ssl_cert: new FormControl('', {
146 CdValidators.requiredIf({
155 this.subs.unsubscribe();
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();
170 if (this.action === 'edit') {
172 this.multiClusterService
173 .editCluster(this.cluster.url, clusterAlias, this.cluster.user)
176 this.remoteClusterForm.setErrors({ cdSubmitButton: true });
179 this.notificationService.show(
180 NotificationType.success,
181 $localize`Cluster updated successfully`
183 this.submitAction.emit();
184 this.activeModal.close();
190 if (this.action === 'reconnect') {
192 this.multiClusterService
193 .reConnectCluster(updatedUrl, username, password, token, ssl, ssl_certificate)
196 this.remoteClusterForm.setErrors({ cdSubmitButton: true });
199 this.notificationService.show(
200 NotificationType.success,
201 $localize`Cluster reconnected successfully`
203 this.submitAction.emit();
204 this.activeModal.close();
210 if (this.action === 'connect') {
212 this.multiClusterService
219 window.location.origin,
227 this.remoteClusterForm.setErrors({ cdSubmitButton: true });
230 this.notificationService.show(
231 NotificationType.success,
232 $localize`Cluster connected successfully`
234 this.submitAction.emit();
235 this.activeModal.close();
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();
251 this.multiClusterService
252 .verifyConnection(url, username, password, token, ssl, ssl_certificate)
253 .subscribe((resp: string) => {
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`
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`
275 this.connectionVerified = false;
276 this.connectionMessage = resp;
277 this.notificationService.show(
278 NotificationType.error,
279 $localize`Connection to the cluster failed`
288 this.showToken = !this.showToken;
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();
301 reader.readAsText(file, 'utf8');