]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: carbonise multi-cluster forms
authorAashish Sharma <Aashish.Sharma1@ibm.com>
Wed, 10 Dec 2025 07:12:40 +0000 (12:42 +0530)
committerAashish Sharma <aashish@li-e9bf2ecc-2ad7-11b2-a85c-baf05c5182ab.ibm.com>
Tue, 30 Dec 2025 06:20:43 +0000 (11:50 +0530)
Fixes: https://tracker.ceph.com/issues/74171
Signed-off-by: Aashish Sharma <aasharma@redhat.com>
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-form/multi-cluster-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-form/multi-cluster-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-list/multi-cluster-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-list/multi-cluster-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/url-builder.service.ts

index fc2dd0b56109972efb92d360c9606767b7cc9652..0de4ea6404ac1309e0b249407584beb03153ebb0 100644 (file)
@@ -59,6 +59,7 @@ import { SmbClusterListComponent } from './ceph/smb/smb-cluster-list/smb-cluster
 import { SmbJoinAuthListComponent } from './ceph/smb/smb-join-auth-list/smb-join-auth-list.component';
 import { SmbUsersgroupsListComponent } from './ceph/smb/smb-usersgroups-list/smb-usersgroups-list.component';
 import { SmbOverviewComponent } from './ceph/smb/smb-overview/smb-overview.component';
+import { MultiClusterFormComponent } from './ceph/cluster/multi-cluster/multi-cluster-form/multi-cluster-form.component';
 
 @Injectable()
 export class PerformanceCounterBreadcrumbsResolver extends BreadcrumbsResolver {
@@ -209,11 +210,25 @@ const routes: Routes = [
           },
           {
             path: 'manage-clusters',
-            component: MultiClusterListComponent,
-            data: {
-              breadcrumbs: 'Multi-Cluster/Manage Clusters'
-            },
+            data: { breadcrumbs: 'Multi-Cluster/Manage Clusters' },
+
             children: [
+              { path: '', component: MultiClusterListComponent },
+              {
+                path: URLVerbs.CONNECT,
+                component: MultiClusterFormComponent,
+                data: { breadcrumbs: ActionLabels.CONNECT }
+              },
+              {
+                path: `${URLVerbs.EDIT}/:name`,
+                component: MultiClusterFormComponent,
+                data: { breadcrumbs: ActionLabels.EDIT }
+              },
+              {
+                path: `${URLVerbs.RECONNECT}/:name`,
+                component: MultiClusterFormComponent,
+                data: { breadcrumbs: ActionLabels.RECONNECT }
+              },
               {
                 path: 'performance-details',
                 component: MultiClusterDetailsComponent
index d5c73717eba24a3df0c3f25d878af4b901d14507..5fe7ac88d81fac16379a6938cd496b86d536369e 100644 (file)
@@ -18,7 +18,8 @@ import {
   IconModule,
   IconService,
   TagModule,
-  SelectModule
+  SelectModule,
+  LayoutModule
 } from 'carbon-components-angular';
 import Analytics from '@carbon/icons/es/analytics/16';
 import CloseFilled from '@carbon/icons/es/close--filled/16';
@@ -121,7 +122,8 @@ import { TextLabelListComponent } from '~/app/shared/components/text-label-list/
     IconModule,
     TagModule,
     TextLabelListComponent,
-    SelectModule
+    SelectModule,
+    LayoutModule
   ],
   declarations: [
     MonitorComponent,
index 4bdb2ad45c99130de4f6c04c21e48875416c84c8..ba249eacc9b0e61ce48d1cfff3e706327eb7ec0c 100644 (file)
-<cd-modal [modalRef]="activeModal">
-  <ng-container i18n="form title"
-                class="modal-title">{{ action | titlecase }} Cluster
-  </ng-container>
-  <ng-container class="modal-content">
+<div cdsCol [columnNumbers]="{md: 6}">
+  <ng-container>
     <form name="remoteClusterForm"
           #frm="ngForm"
-          [formGroup]="remoteClusterForm">
-      <div class="modal-body">
-        <cd-alert-panel *ngIf="connectionVerified !== undefined && !connectionVerified && connectionMessage !== 'Connection refused'"
-                        type="error"
-                        spacingClass="mb-3"
-                        i18n>{{ connectionMessage }}
-        </cd-alert-panel>
-        <cd-alert-panel *ngIf="connectionVerified !== undefined && connectionVerified"
-                        type="success"
-                        spacingClass="mb-3"
-                        i18n>{{ connectionMessage }}
-        </cd-alert-panel>
-        <div class="form-group row">
-          <label class="cd-col-form-label required"
-                 for="remoteClusterUrl"
-                 i18n>Cluster API URL
-            <cd-helper>
-              <span>
-                <p>Enter the Dashboard API URL. You can retrieve it from the CLI with: <b>{{ clusterApiUrlCmd }} </b>
-                  <cd-copy-2-clipboard-button [source]="clusterApiUrlCmd"
-                                              [byId]="false"></cd-copy-2-clipboard-button>
-                </p>
-              </span>
-            </cd-helper>
-          </label>
-          <div class="cd-col-form-input">
-            <input class="form-control"
-                   type="text"
-                   placeholder="https://localhost:4202"
-                   id="remoteClusterUrl"
-                   name="remoteClusterUrl"
-                   formControlName="remoteClusterUrl">
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('remoteClusterUrl', frm, 'required')"
-                  i18n>This field is required.
-            </span>
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('remoteClusterUrl', frm, 'endpoint')"
-                  i18n>Please enter a valid URL.
-            </span>
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('remoteClusterUrl', frm, 'hubUrlCheck')"
-                  i18n>The hub cluster cannot be connected.
-            </span>
-          </div>
-        </div>
-        <div class="form-group row">
-          <label class="cd-col-form-label required"
-                 for="clusterAlias"
-                 i18n>Alias Name
-          </label>
-          <div class="cd-col-form-input">
-            <input id="clusterAlias"
-                   name="clusterAlias"
-                   class="form-control"
-                   type="text"
-                   placeholder="Name/Text to uniquely identify cluster"
-                   formControlName="clusterAlias">
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('clusterAlias', frm, 'required')"
-                  i18n>This field is required.
-            </span>
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('clusterAlias', frm, 'uniqueName')"
-                  i18n>The chosen alias name is already in use.
-            </span>
-          </div>
-        </div>
-        <div class="form-group row"
-             *ngIf="action !== 'edit'">
-          <label class="cd-col-form-label required"
-                 for="username"
-                 i18n>Username
-          </label>
-          <div class="cd-col-form-input">
-            <input id="username"
-                   name="username"
-                   class="form-control"
-                   type="text"
-                   formControlName="username">
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('username', frm, 'required')"
-                  i18n>This field is required.
-            </span>
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('username', frm, 'uniqueUrlandUser')"
-                  i18n>A cluster with the chosen user is already connected.
-            </span>
-          </div>
-        </div>
-        <div class="form-group row"
-             *ngIf="action !== 'edit'">
-          <label class="cd-col-form-label required"
-                 for="password"
-                 i18n>Password
-          </label>
-          <div class="cd-col-form-input">
-            <div class="input-group">
-              <input id="password"
-                     name="password"
-                     class="form-control"
-                     type="password"
-                     formControlName="password">
-              <span class="input-group-button">
-                <button type="button"
-                        class="btn btn-light"
-                        cdPasswordButton="password">
-                </button>
-                <cd-copy-2-clipboard-button source="password">
-                </cd-copy-2-clipboard-button>
-              </span>
-              <span class="invalid-feedback"
-                    *ngIf="remoteClusterForm.showError('password', frm, 'requiredNotEdit')"
-                    i18n>This field is required.
-              </span>
-            </div>
-          </div>
-        </div>
-        <div class="form-group row"
-             *ngIf="action !== 'edit'">
-          <label class="cd-col-form-label"
-                 for="ttl"
-                 i18n>Login Expiration</label>
-          <div class="cd-col-form-input">
-            <select class="form-select"
-                    id="ttl"
-                    formControlName="ttl"
-                    name="ttl">
-              <option value="1">1 day</option>
-              <option value="7">1 week</option>
-              <option value="15"
-                      [selected]="true">15 days</option>
-              <option value="30">30 days</option>
-            </select>
-          </div>
-        </div>
-        <!--
-        <div class="form-group row"
-             *ngIf="action !== 'edit'">
-          <label class="cd-col-form-label required"
-                 for="apiToken"
-                 i18n>Token
-          </label>
-          <div class="cd-col-form-input">
-            <input id="apiToken"
-                   name="apiToken"
-                   class="form-control"
-                   type="text"
-                   formControlName="apiToken">
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('apiToken', frm, 'required')"
-                  i18n>This field is required.</span>
-          </div>
-        </div>
-        <div class="form-group row"
-             *ngIf="action !== 'edit'">
-          <div class="cd-col-form-offset">
-            <div class="custom-control custom-checkbox">
-              <input class="custom-control-input"
-                     id="showToken"
-                     type="checkbox"
-                     [checked]="showToken"
-                     (change)="toggleToken()"
-                     formControlName="showToken">
-              <label class="custom-control-label"
-                     for="showToken"
-                     i18n>Auth with token</label>
-            </div>
-          </div>
-        </div> -->
-        <!-- ssl -->
-        <div class="form-group row">
-          <div class="cd-col-form-offset">
-            <div class="custom-control custom-checkbox">
-              <input class="custom-control-input"
-                     id="ssl"
-                     type="checkbox"
-                     formControlName="ssl">
-              <label class="custom-control-label"
-                     for="ssl"
-                     i18n>SSL</label>
-            </div>
-          </div>
-        </div>
-
-        <!-- ssl_cert -->
-        <div *ngIf="remoteClusterForm.controls.ssl.value"
-             class="form-group row">
-          <label class="cd-col-form-label"
-                 for="ssl_cert">
-            <span i18n>Certificate</span>
-            <cd-helper i18n>The SSL certificate in PEM format.</cd-helper>
-          </label>
-          <div class="cd-col-form-input">
-            <textarea id="ssl_cert"
-                      class="form-control resize-vertical text-monospace text-pre"
-                      formControlName="ssl_cert"
-                      rows="5">
-            </textarea>
-            <input type="file"
-                   (change)="fileUpload($event.target.files, 'ssl_cert')">
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('ssl_cert', frm, 'required')"
-                  i18n>This field is required.</span>
-            <span class="invalid-feedback"
-                  *ngIf="remoteClusterForm.showError('ssl_cert', frm, 'pattern')"
-                  i18n>Invalid SSL certificate.</span>
-          </div>
-        </div>
+          [formGroup]="remoteClusterForm"
+          novalidate>
+
+      <div i18n="form title"
+           class="form-header">{{ action | titlecase }} Cluster</div>
+
+      <cds-inline-notification
+        *ngIf="connectionVerified !== undefined && !connectionVerified && connectionMessage !== 'Connection refused'"
+        kind="error"
+        title=""
+        [subtitle]="connectionMessage">
+      </cds-inline-notification>
+
+      <cds-inline-notification
+        *ngIf="connectionVerified !== undefined && connectionVerified"
+        kind="success"
+        title=""
+        [subtitle]="connectionMessage">
+      </cds-inline-notification>
+
+      <div class="form-item">
+        <cds-text-label
+          cdRequiredField="Cluster API URL"
+          [invalid]="remoteClusterForm.controls.remoteClusterUrl.invalid && remoteClusterForm.controls.remoteClusterUrl.dirty"
+          [invalidText]="urlErrorTpl"
+          for="remoteClusterUrl"
+          i18n>
+          Cluster API URL
+         
+          <input cdsText
+                 type="text"
+                 id="remoteClusterUrl"
+                 name="remoteClusterUrl"
+                 placeholder="https://localhost:4202"
+                 formControlName="remoteClusterUrl"
+                 [invalid]="remoteClusterForm.controls.remoteClusterUrl.invalid && remoteClusterForm.controls.remoteClusterUrl.dirty">
+        </cds-text-label>
+        <ng-template #urlErrorTpl>
+          <span
+            class="invalid-feedback"
+            *ngIf="remoteClusterForm.showError('remoteClusterUrl', frm, 'required')"
+            i18n
+            >This field is required.</span
+          >
+          <span
+            class="invalid-feedback"
+            *ngIf="remoteClusterForm.showError('remoteClusterUrl', frm, 'invalidURL')"
+            i18n
+            >Please enter a valid URL.</span
+          >
+          <span
+            class="invalid-feedback"
+            *ngIf="remoteClusterForm.showError('remoteClusterUrl', frm, 'hubUrlCheck')"
+            i18n
+            >The hub cluster cannot be connected.</span
+          >
+        </ng-template>
+         <cd-help-text>
+            Enter the Dashboard API URL. CLI: <b>{{ clusterApiUrlCmd }}</b>
+         </cd-help-text>
       </div>
-      <div class="modal-footer">
-        <cd-form-button-panel (submitActionEvent)="onSubmit()"
-                              [submitText]="(action | titlecase) + ' ' + 'Cluster'"
-                              [form]="remoteClusterForm">
-        </cd-form-button-panel>
+
+      <div class="form-item">
+        <cds-text-label
+          cdRequiredField="Alias Name"
+          [invalid]="remoteClusterForm.showError('clusterAlias', frm)"
+          [invalidText]="aliasErrorTpl"
+          for="clusterAlias"
+          i18n>
+          Alias Name
+          <input cdsText
+                 id="clusterAlias"
+                 name="clusterAlias"
+                 placeholder="Name/Text to uniquely identify cluster"
+                 formControlName="clusterAlias">
+        </cds-text-label>
+
+        <ng-template #aliasErrorTpl>
+          <span *ngIf="remoteClusterForm.showError('clusterAlias', frm, 'required')" i18n>
+            This field is required.
+          </span>
+          <span *ngIf="remoteClusterForm.showError('clusterAlias', frm, 'uniqueName')" i18n>
+            The chosen alias name is already in use.
+          </span>
+        </ng-template>
       </div>
+
+      <div class="form-item" *ngIf="action !== 'edit'">
+        <cds-text-label
+          cdRequiredField="Username"
+          [invalid]="remoteClusterForm.showError('username', frm)"
+          [invalidText]="usernameErrorTpl"
+          for="username"
+          i18n>
+          Username
+          <input cdsText
+                 id="username"
+                 name="username"
+                 formControlName="username">
+        </cds-text-label>
+
+        <ng-template #usernameErrorTpl>
+          <span *ngIf="remoteClusterForm.showError('username', frm, 'required')" i18n>
+            This field is required.
+          </span>
+          <span *ngIf="remoteClusterForm.showError('username', frm, 'uniqueUrlandUser')" i18n>
+            A cluster with the chosen user is already connected.
+          </span>
+        </ng-template>
+      </div>
+
+      <div class="form-item" *ngIf="action !== 'edit'">
+        <cds-password-label labelInputID="password"
+                            label="Password..."
+                            i18n>Password
+        <input cdsPassword
+               type="password"
+               placeholder="Password..."
+               id="password"
+               autocomplete="new-password"
+               formControlName="password"
+               >
+        </cds-password-label>
+      </div>
+
+      <div class="form-item" *ngIf="action !== 'edit'">
+        <cds-select   formControlName="ttl"
+                      name="ttl"
+                      for="ttl"
+                      label="Login Expiration"
+                      id="ttl"
+                      i18n>
+          <option value="1">1 day</option>
+          <option value="7">1 week</option>
+          <option value="15">15 days</option>
+          <option value="30">30 days</option>
+        </cds-select>
+      </div>
+
+      <div class="form-item">
+        <cds-checkbox
+          formControlName="ssl"
+          id="ssl"
+          i18n>
+          SSL
+        </cds-checkbox>
+      </div>
+
+      <div class="form-item" *ngIf="remoteClusterForm.get('ssl').value">
+        <cds-text-label
+          label="Certificate"
+          [helperText]="sslHelpTpl"
+          [invalid]="remoteClusterForm.showError('ssl_cert', frm)"
+          [invalidText]="sslCertErrorTpl"
+          i18n>
+          <div cdsStack="vertical" gap="3">
+          <textarea cdsTextArea
+                    id="ssl_cert"
+                    rows="5"
+                    formControlName="ssl_cert"></textarea>
+
+          <input type="file" (change)="fileUpload($event.target.files, 'ssl_cert')">
+          </div>
+        </cds-text-label>
+
+        <ng-template #sslHelpTpl>
+          <span i18n>The SSL certificate in PEM format.</span>
+        </ng-template>
+
+        <ng-template #sslCertErrorTpl>
+          <span *ngIf="remoteClusterForm.showError('ssl_cert', frm, 'required')" i18n>
+            This field is required.
+          </span>
+          <span *ngIf="remoteClusterForm.showError('ssl_cert', frm, 'pattern')" i18n>
+            Invalid SSL certificate.
+          </span>
+        </ng-template>
+      </div>
+
+      <cd-form-button-panel (submitActionEvent)="onSubmit()"
+                            [form]="frm"
+                            [submitText]="(action | titlecase) + ' ' + 'Cluster'"
+                            wrappingClass="text-right"></cd-form-button-panel>
+
     </form>
   </ng-container>
-</cd-modal>
+</div>
index 98e68fc971b473b9fbe5da5bea5b3314dd250767..fd0dd8ef80ac93fc23a3e09468fa2089a1233009 100644 (file)
@@ -1,5 +1,6 @@
 import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
 import { AbstractControl, FormControl, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 import _ from 'lodash';
 import { Subscription } from 'rxjs';
@@ -33,47 +34,101 @@ export class MultiClusterFormComponent implements OnInit, OnDestroy {
   clusterUsers: string[];
   clusterUrlUserMap: Map<string, string>;
   hubUrl: string;
+  loading = false;
+  formPatched = false;
+  clusterTokenStatus: { [key: string]: any } = {};
 
   constructor(
     public activeModal: NgbActiveModal,
     public actionLabels: ActionLabelsI18n,
     public notificationService: NotificationService,
-    private multiClusterService: MultiClusterService
+    private multiClusterService: MultiClusterService,
+    private router: Router
   ) {
     this.subs.add(
       this.multiClusterService.subscribe((resp: any) => {
         this.hubUrl = resp['hub_url'];
       })
     );
-    this.createForm();
+    this.prepareFormForAction();
   }
+
   ngOnInit(): void {
-    if (this.action === 'edit') {
-      this.remoteClusterForm.get('remoteClusterUrl').setValue(this.cluster.url);
-      this.remoteClusterForm.get('clusterAlias').setValue(this.cluster.cluster_alias);
-      this.remoteClusterForm.get('ssl').setValue(this.cluster.ssl_verify);
-      this.remoteClusterForm.get('ssl_cert').setValue(this.cluster.ssl_certificate);
+    this.createForm();
+    const clusterName = this.router.url.split('/').pop();
+    this.subs.add(
+      this.multiClusterService.subscribe((resp: any) => {
+        if (resp && resp['config']) {
+          this.clustersData = Object.values(resp['config']).flat() as MultiCluster[];
+          this.cluster = this.clustersData.find((c) => c.name === clusterName);
+          [this.clusterAliasNames, this.clusterUrls, this.clusterUsers] = [
+            'cluster_alias',
+            'url',
+            USER
+          ].map((prop) => this.clustersData.map((c) => (c as any)[prop]));
+          if (!this.formPatched && this.cluster) {
+            this.formPatched = true;
+            this.prepareEditForm(this.cluster);
+            this.prepareReconnectForm(this.cluster);
+          }
+        }
+      })
+    );
+
+    this.subs.add(
+      this.multiClusterService.subscribeClusterTokenStatus((resp: object) => {
+        this.clusterTokenStatus = resp;
+        this.checkClusterConnectionStatus();
+      })
+    );
+  }
+
+  checkClusterConnectionStatus() {
+    if (this.clusterTokenStatus && this.clustersData) {
+      this.clustersData.forEach((cluster: MultiCluster) => {
+        const clusterStatus = this.clusterTokenStatus[cluster.name];
+        if (clusterStatus !== undefined) {
+          cluster.cluster_connection_status = clusterStatus.status;
+          cluster.ttl = clusterStatus.time_left;
+        } else {
+          cluster.cluster_connection_status = 2;
+        }
+        if (cluster.cluster_alias === 'local-cluster') {
+          cluster.cluster_connection_status = 0;
+        }
+      });
     }
-    if (this.action === 'reconnect') {
-      this.remoteClusterForm.get('remoteClusterUrl').setValue(this.cluster.url);
+  }
+
+  prepareEditForm(cluster: MultiCluster) {
+    if (this.action === 'Edit') {
+      this.remoteClusterForm.get('remoteClusterUrl').setValue(cluster.url);
       this.remoteClusterForm.get('remoteClusterUrl').disable();
-      this.remoteClusterForm.get('clusterAlias').setValue(this.cluster.cluster_alias);
+      this.remoteClusterForm.get('clusterAlias').setValue(cluster.cluster_alias);
+      this.remoteClusterForm.get('username').setValue(cluster.user);
+      this.remoteClusterForm.get('username').disable();
+      this.remoteClusterForm.get('ssl').setValue(cluster.ssl_verify);
+      this.remoteClusterForm.get('ssl_cert').setValue(cluster.ssl_certificate);
+    }
+  }
+
+  prepareReconnectForm(cluster: MultiCluster) {
+    if (this.action === 'Reconnect') {
+      this.remoteClusterForm.get('remoteClusterUrl').setValue(cluster.url);
+      this.remoteClusterForm.get('remoteClusterUrl').disable();
+      this.remoteClusterForm.get('clusterAlias').setValue(cluster.cluster_alias);
       this.remoteClusterForm.get('clusterAlias').disable();
-      this.remoteClusterForm.get('username').setValue(this.cluster.user);
+      this.remoteClusterForm.get('username').setValue(cluster.user);
       this.remoteClusterForm.get('username').disable();
-      this.remoteClusterForm.get('ssl').setValue(this.cluster.ssl_verify);
-      this.remoteClusterForm.get('ssl_cert').setValue(this.cluster.ssl_certificate);
+      this.remoteClusterForm.get('ssl').setValue(cluster.ssl_verify);
+      this.remoteClusterForm.get('ssl_cert').setValue(cluster.ssl_certificate);
     }
-    [this.clusterAliasNames, this.clusterUrls, this.clusterUsers] = [
-      'cluster_alias',
-      'url',
-      USER
-    ].map((prop) => this.clustersData?.map((cluster) => cluster[prop]));
   }
 
   createForm() {
     this.remoteClusterForm = new CdFormGroup({
       username: new FormControl('', [
+        Validators.required,
         CdValidators.custom('uniqueUrlandUser', (username: string) => {
           let remoteClusterUrl = '';
           if (
@@ -86,23 +141,29 @@ export class MultiClusterFormComponent implements OnInit, OnDestroy {
             remoteClusterUrl = this.remoteClusterForm.getValue('remoteClusterUrl');
           }
           return (
+            this.action !== 'Edit' &&
             this.remoteClusterForm &&
             this.clusterUrls?.includes(remoteClusterUrl) &&
             this.clusterUsers?.includes(username)
           );
         })
       ]),
-      password: new FormControl(
-        null,
-        CdValidators.custom('requiredNotEdit', (value: string) => {
-          return this.action !== 'edit' && !value;
-        })
-      ),
+      password: new FormControl(null, {
+        validators: [
+          Validators.required,
+          CdValidators.custom('requiredNotEdit', (value: string) => {
+            return this.action !== 'Edit' && !value;
+          })
+        ]
+      }),
       remoteClusterUrl: new FormControl(null, {
         validators: [
           CdValidators.url,
           CdValidators.custom('hubUrlCheck', (remoteClusterUrl: string) => {
-            return this.action === 'connect' && remoteClusterUrl?.includes(this.hubUrl);
+            if (this.action === 'Connect' && remoteClusterUrl?.includes(this.hubUrl)) {
+              return true;
+            }
+            return false;
           }),
           Validators.required
         ]
@@ -112,7 +173,7 @@ export class MultiClusterFormComponent implements OnInit, OnDestroy {
           Validators.required,
           CdValidators.custom('uniqueName', (clusterAlias: string) => {
             return (
-              (this.action === 'connect' || this.action === 'edit') &&
+              (this.action === 'Connect' || this.action === 'Edit') &&
               this.clusterAliasNames &&
               this.clusterAliasNames.indexOf(clusterAlias) !== -1 &&
               this.cluster?.cluster_alias &&
@@ -121,8 +182,8 @@ export class MultiClusterFormComponent implements OnInit, OnDestroy {
           })
         ]
       }),
-      ssl: new FormControl(false),
       ttl: new FormControl(15),
+      ssl: new FormControl(false),
       ssl_cert: new FormControl('', {
         validators: [
           CdValidators.requiredIf({
@@ -137,6 +198,17 @@ export class MultiClusterFormComponent implements OnInit, OnDestroy {
     this.subs.unsubscribe();
   }
 
+  prepareFormForAction() {
+    const url = this.router.url;
+    if (url.startsWith('/multi-cluster/manage-clusters/connect')) {
+      this.action = this.actionLabels.CONNECT;
+    } else if (url.startsWith('/multi-cluster/manage-clusters/reconnect')) {
+      this.action = this.actionLabels.RECONNECT;
+    } else if (url.startsWith('/multi-cluster/manage-clusters/edit')) {
+      this.action = this.actionLabels.EDIT;
+    }
+  }
+
   handleError(error: any): void {
     if (error.error.code === 'connection_refused') {
       this.connectionVerified = false;
@@ -155,7 +227,10 @@ export class MultiClusterFormComponent implements OnInit, OnDestroy {
   handleSuccess(message?: string): void {
     this.notificationService.show(NotificationType.success, message);
     this.submitAction.emit();
-    this.activeModal.close();
+    const currentRoute = '/multi-cluster/manage-clusters';
+    this.multiClusterService.refreshMultiCluster(currentRoute);
+    this.checkClusterConnectionStatus();
+    this.multiClusterService.isClusterAdded(true);
   }
 
   convertToHours(value: number): number {
@@ -182,7 +257,7 @@ export class MultiClusterFormComponent implements OnInit, OnDestroy {
     };
 
     switch (this.action) {
-      case 'edit':
+      case 'Edit':
         this.subs.add(
           this.multiClusterService
             .editCluster(
@@ -199,14 +274,14 @@ export class MultiClusterFormComponent implements OnInit, OnDestroy {
             })
         );
         break;
-      case 'reconnect':
+      case 'Reconnect':
         this.subs.add(
           this.multiClusterService
             .reConnectCluster(updatedUrl, username, password, ssl, ssl_certificate, ttl)
             .subscribe(commonSubscribtion)
         );
         break;
-      case 'connect':
+      case 'Connect':
         this.subs.add(
           this.multiClusterService
             .addCluster(
index 2a0c2d628b49c5120ed8d7e57bf46334fc47b346..7abe686107d8fe270ec47172ac8f5ba142583bb4 100644 (file)
@@ -22,12 +22,15 @@ import { Observable, Subscription } from 'rxjs';
 import { SettingsService } from '~/app/shared/api/settings.service';
 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
 import { ListWithDetails } from '~/app/shared/classes/list-with-details.class';
+import { URLBuilderService } from '~/app/shared/services/url-builder.service';
+const BASE_URL = 'multi-cluster/manage-clusters';
 
 @Component({
   selector: 'cd-multi-cluster-list',
   templateUrl: './multi-cluster-list.component.html',
   styleUrls: ['./multi-cluster-list.component.scss'],
-  standalone: false
+  standalone: false,
+  providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
 })
 export class MultiClusterListComponent extends ListWithDetails implements OnInit, OnDestroy {
   @ViewChild(TableComponent)
@@ -39,7 +42,7 @@ export class MultiClusterListComponent extends ListWithDetails implements OnInit
   private subs = new Subscription();
   permissions: Permissions;
   tableActions: CdTableAction[];
-  clusterTokenStatus: object = {};
+  clusterTokenStatus: { [key: string]: any } = {};
   columns: Array<CdTableColumn> = [];
   data: any;
   selection = new CdTableSelection();
@@ -63,7 +66,8 @@ export class MultiClusterListComponent extends ListWithDetails implements OnInit
     private cookieService: CookiesService,
     private settingsService: SettingsService,
     private cdsModalService: ModalCdsService,
-    private route: ActivatedRoute
+    private route: ActivatedRoute,
+    private urlBuilder: URLBuilderService
   ) {
     super();
     this.tableActions = [
@@ -71,22 +75,22 @@ export class MultiClusterListComponent extends ListWithDetails implements OnInit
         permission: 'create',
         icon: Icons.add,
         name: this.actionLabels.CONNECT,
-        disable: (selection: CdTableSelection) => this.getDisable('connect', selection),
-        click: () => this.openRemoteClusterInfoModal('connect')
+        disable: (selection: CdTableSelection) => this.getDisable('Connect', selection),
+        routerLink: () => this.urlBuilder.getConnect()
       },
       {
         permission: 'update',
         icon: Icons.edit,
         name: this.actionLabels.EDIT,
         disable: (selection: CdTableSelection) => this.getDisable('edit', selection),
-        click: () => this.openRemoteClusterInfoModal('edit')
+        routerLink: () => this.urlBuilder.getEdit(this.selection.first().name)
       },
       {
         permission: 'update',
         icon: Icons.refresh,
         name: this.actionLabels.RECONNECT,
         disable: (selection: CdTableSelection) => this.getDisable('reconnect', selection),
-        click: () => this.openRemoteClusterInfoModal('reconnect')
+        routerLink: () => this.urlBuilder.getReconnect(this.selection.first().name)
       },
       {
         permission: 'delete',
@@ -101,7 +105,7 @@ export class MultiClusterListComponent extends ListWithDetails implements OnInit
 
   ngOnInit(): void {
     this.subs.add(
-      this.multiClusterService.subscribe((resp: object) => {
+      this.multiClusterService.subscribe((resp: any) => {
         if (resp && resp['config']) {
           this.hubUrl = resp['hub_url'];
           this.currentUrl = resp['current_url'];
@@ -255,12 +259,12 @@ export class MultiClusterListComponent extends ListWithDetails implements OnInit
     if (this.hubUrl !== this.currentUrl) {
       return $localize`Please switch to the local-cluster to ${action} a remote cluster`;
     }
-    if (!selection.hasSelection && action !== 'connect') {
+    if (!selection.hasSelection && action !== 'Connect') {
       return $localize`Please select one or more clusters to ${action}`;
     }
     if (selection.hasSingleSelection) {
       const cluster = selection.first();
-      if (cluster['cluster_alias'] === 'local-cluster' && action !== 'connect') {
+      if (cluster['cluster_alias'] === 'local-cluster' && action !== 'Connect') {
         return $localize`Cannot ${action} local cluster`;
       }
     }
index 2514f1f87cd632da8a6d727bdb5be530b2e0f375..c487d8cc291efdff0a7419d3b8c3208124f91463 100644 (file)
@@ -39,7 +39,11 @@ export enum URLVerbs {
   EXPIRE = 'expire',
 
   /* Daemons */
-  RESTART = 'Restart'
+  RESTART = 'Restart',
+
+  /* Multi-cluster */
+  CONNECT = 'connect',
+  RECONNECT = 'reconnect'
 }
 
 export enum ActionLabels {
@@ -78,7 +82,11 @@ export enum ActionLabels {
   START = 'Start',
   STOP = 'Stop',
   REDEPLOY = 'Redeploy',
-  RESTART = 'Restart'
+  RESTART = 'Restart',
+
+  /* Multi-cluster */
+  CONNECT = 'connect',
+  RECONNECT = 'reconnect'
 }
 
 @Injectable({
index b06f307ad2e077045875e3f55985514a208461c5..b63489b8bcecdce83f7d8a89accc62b67a549936 100644 (file)
@@ -47,4 +47,12 @@ export class URLBuilderService {
   getRecreate(item: string, absolute = true): string {
     return this.getURL(URLVerbs.RECREATE, absolute, item);
   }
+
+  getConnect(absolute = true): string {
+    return this.getURL(URLVerbs.CONNECT, absolute);
+  }
+
+  getReconnect(item: string, absolute = true): string {
+    return this.getURL(URLVerbs.RECONNECT, absolute, item);
+  }
 }