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 remoteClusterForm: CdFormGroup;
27 connectionVerified: boolean;
28 connectionMessage = '';
29 private subs = new Subscription();
30 showCrossOriginError = false;
31 crossOriginCmd: string;
33 cluster: MultiCluster;
34 clustersData: MultiCluster[];
35 clusterAliasNames: string[];
36 clusterUrls: string[];
37 clusterUsers: string[];
38 clusterUrlUserMap: Map<string, string>;
41 public activeModal: NgbActiveModal,
42 public actionLabels: ActionLabelsI18n,
43 public notificationService: NotificationService,
44 private multiClusterService: MultiClusterService
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);
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);
68 [this.clusterAliasNames, this.clusterUrls, this.clusterUsers] = [
72 ].map((prop) => this.clustersData?.map((cluster) => cluster[prop]));
76 this.remoteClusterForm = new CdFormGroup({
77 showToken: new FormControl(false),
78 username: new FormControl('', [
79 CdValidators.custom('uniqueUrlandUser', (username: string) => {
80 let remoteClusterUrl = '';
82 this.remoteClusterForm &&
83 this.remoteClusterForm.getValue('remoteClusterUrl') &&
84 this.remoteClusterForm.getValue('remoteClusterUrl').endsWith('/')
86 remoteClusterUrl = this.remoteClusterForm.getValue('remoteClusterUrl').slice(0, -1);
87 } else if (this.remoteClusterForm) {
88 remoteClusterUrl = this.remoteClusterForm.getValue('remoteClusterUrl');
91 this.remoteClusterForm &&
92 this.clusterUrls?.includes(remoteClusterUrl) &&
93 this.clusterUsers?.includes(username)
97 clusterFsid: new FormControl('', [
98 CdValidators.requiredIf({
102 password: new FormControl('', []),
103 remoteClusterUrl: new FormControl(null, {
105 CdValidators.custom('endpoint', (value: string) => {
106 if (_.isEmpty(value)) {
110 !this.endpoints.test(value) &&
111 !this.ipv4Rgx.test(value) &&
112 !this.ipv6Rgx.test(value)
119 apiToken: new FormControl('', [
120 CdValidators.requiredIf({
124 clusterAlias: new FormControl(null, {
127 CdValidators.custom('uniqueName', (clusterAlias: string) => {
129 (this.action === 'connect' || this.action === 'edit') &&
130 this.clusterAliasNames &&
131 this.clusterAliasNames.indexOf(clusterAlias) !== -1
136 ssl: new FormControl(false),
137 ssl_cert: new FormControl('', {
139 CdValidators.requiredIf({
148 this.subs.unsubscribe();
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();
162 if (this.action === 'edit') {
164 this.multiClusterService
165 .editCluster(this.cluster.url, clusterAlias, this.cluster.user)
168 this.remoteClusterForm.setErrors({ cdSubmitButton: true });
171 this.notificationService.show(
172 NotificationType.success,
173 $localize`Cluster updated successfully`
175 this.submitAction.emit();
176 this.activeModal.close();
182 if (this.action === 'reconnect') {
184 this.multiClusterService
185 .reConnectCluster(updatedUrl, username, password, token, ssl, ssl_certificate)
188 this.remoteClusterForm.setErrors({ cdSubmitButton: true });
191 this.notificationService.show(
192 NotificationType.success,
193 $localize`Cluster reconnected successfully`
195 this.submitAction.emit();
196 this.activeModal.close();
202 if (this.action === 'connect') {
204 this.multiClusterService
211 window.location.origin,
218 this.remoteClusterForm.setErrors({ cdSubmitButton: true });
221 this.notificationService.show(
222 NotificationType.success,
223 $localize`Cluster connected successfully`
225 this.submitAction.emit();
226 this.activeModal.close();
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();
242 this.multiClusterService
243 .verifyConnection(url, username, password, token, ssl, ssl_certificate)
244 .subscribe((resp: string) => {
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`
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`
267 this.connectionVerified = false;
268 this.connectionMessage = resp;
269 this.notificationService.show(
270 NotificationType.error,
271 $localize`Connection to the cluster failed`
280 this.showToken = !this.showToken;
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();
293 reader.readAsText(file, 'utf8');