]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: allow selecting all daemons for Orchestrator NFS clusters 38496/head
authorKiefer Chang <kiefer.chang@suse.com>
Tue, 3 Nov 2020 07:09:42 +0000 (15:09 +0800)
committerKiefer Chang <kiefer.chang@suse.com>
Wed, 9 Dec 2020 03:25:38 +0000 (11:25 +0800)
Allow users to select or deselect all daemons with a single click in NFS
export form when the cluster type is Orchestrator.

Fixes: https://tracker.ceph.com/issues/48176
Signed-off-by: Kiefer Chang <kiefer.chang@suse.com>
(cherry picked from commit 164b29d49173cdc333adc9bbee1e714a20bb9950)

Conflicts:
  src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.spec.ts
  src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.ts
  - Import section: we use absolute paths in master.
  - Optional chaining is not supported in typescript 3.5.3.

src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-type.enum.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.ts

diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-type.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-type.enum.ts
new file mode 100644 (file)
index 0000000..7a775e5
--- /dev/null
@@ -0,0 +1,4 @@
+export enum NFSClusterType {
+  user = 'user',
+  orchestrator = 'orchestrator'
+}
index 70d577b3456ef3440ea39014bcc96bc08046f150..13b94a6cc189f437da787f9c17e5e09eb69ebb88 100644 (file)
@@ -30,7 +30,7 @@
                       value=""
                       i18n>-- Select the cluster --</option>
               <option *ngFor="let cluster of allClusters"
-                      [value]="cluster">{{ cluster }}</option>
+                      [value]="cluster.cluster_id">{{ cluster.cluster_id }}</option>
             </select>
             <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('cluster_id', formDir, 'required')"
@@ -39,7 +39,8 @@
         </div>
 
         <!-- daemons -->
-        <div class="form-group row">
+        <div class="form-group row"
+             *ngIf="clusterType">
           <label class="cd-col-form-label"
                  for="daemons">
             <ng-container i18n>Daemons</ng-container>
@@ -51,7 +52,8 @@
                        type="text"
                        [value]="daemon"
                        disabled />
-                <span class="input-group-append">
+                <span *ngIf="clusterType === 'user'"
+                      class="input-group-append">
                   <button class="btn btn-light"
                           type="button"
                           (click)="removeDaemon(i, daemon)">
@@ -62,7 +64,8 @@
               </div>
             </ng-container>
 
-            <div class="row">
+            <div *ngIf="clusterType === 'user'"
+                 class="row">
               <div class="col-md-12">
                 <cd-select [data]="nfsForm.get('daemons').value"
                            [options]="daemonsSelections"
                 </cd-select>
               </div>
             </div>
+
+            <div *ngIf="clusterType === 'orchestrator'"
+                 class="row">
+              <div class="col-md-12">
+                <button type="button"
+                        class="btn btn-light float-right"
+                        (click)="onToggleAllDaemonsSelection()">
+                  <i [ngClass]="[icons.add]"></i>
+                  <ng-container *ngIf="nfsForm.getValue('daemons').length === 0; else hasDaemons"
+                                i18n>Add all daemons</ng-container>
+                  <ng-template #hasDaemons>
+                    <ng-container i18n>Remove all daemons</ng-container>
+                  </ng-template>
+                </button>
+              </div>
+            </div>
           </div>
         </div>
 
index 7e271d6d4e9f823bd9dcd091f8a0408e55c7b612..45f31530623f184f81d66b9b8d0150db93a5bfdf 100644 (file)
@@ -13,6 +13,7 @@ import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-h
 import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
 import { SummaryService } from '../../../shared/services/summary.service';
 import { SharedModule } from '../../../shared/shared.module';
+import { NFSClusterType } from '../nfs-cluster-type.enum';
 import { NfsFormClientComponent } from '../nfs-form-client/nfs-form-client.component';
 import { NfsFormComponent } from './nfs-form.component';
 
@@ -59,9 +60,9 @@ describe('NfsFormComponent', () => {
     fixture.detectChanges();
 
     httpTesting.expectOne('api/nfs-ganesha/daemon').flush([
-      { daemon_id: 'node1', cluster_id: 'cluster1' },
-      { daemon_id: 'node2', cluster_id: 'cluster1' },
-      { daemon_id: 'node5', cluster_id: 'cluster2' }
+      { daemon_id: 'node1', cluster_id: 'cluster1', cluster_type: NFSClusterType.user },
+      { daemon_id: 'node2', cluster_id: 'cluster1', cluster_type: NFSClusterType.user },
+      { daemon_id: 'node5', cluster_id: 'cluster2', cluster_type: NFSClusterType.orchestrator }
     ]);
     httpTesting.expectOne('ui-api/nfs-ganesha/fsals').flush(['CEPH', 'RGW']);
     httpTesting.expectOne('ui-api/nfs-ganesha/cephx/clients').flush(['admin', 'fs', 'rgw']);
@@ -121,6 +122,7 @@ describe('NfsFormComponent', () => {
   it('should prepare data when selecting an cluster', () => {
     expect(component.allDaemons).toEqual({ cluster1: ['node1', 'node2'], cluster2: ['node5'] });
     expect(component.daemonsSelections).toEqual([]);
+    expect(component.clusterType).toBeNull();
 
     component.nfsForm.patchValue({ cluster_id: 'cluster1' });
     component.onClusterChange();
@@ -129,6 +131,12 @@ describe('NfsFormComponent', () => {
       { description: '', name: 'node1', selected: false, enabled: true },
       { description: '', name: 'node2', selected: false, enabled: true }
     ]);
+    expect(component.clusterType).toBe(NFSClusterType.user);
+
+    component.nfsForm.patchValue({ cluster_id: 'cluster2' });
+    component.onClusterChange();
+    expect(component.clusterType).toBe(NFSClusterType.orchestrator);
+    expect(component.daemonsSelections).toEqual([]);
   });
 
   it('should clean data when changing cluster', () => {
index a85a32fc6f62499d5c35dae3cfeb2b6b4064759b..79492fa765e3abb21ad356a7784d4fbff471abff 100644 (file)
@@ -20,6 +20,7 @@ import { FinishedTask } from '../../../shared/models/finished-task';
 import { Permission } from '../../../shared/models/permissions';
 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
+import { NFSClusterType } from '../nfs-cluster-type.enum';
 import { NfsFormClientComponent } from '../nfs-form-client/nfs-form-client.component';
 
 @Component({
@@ -36,13 +37,14 @@ export class NfsFormComponent implements OnInit {
   isEdit = false;
 
   cluster_id: string = null;
+  clusterType: string = null;
   export_id: string = null;
 
   isNewDirectory = false;
   isNewBucket = false;
   isDefaultCluster = false;
 
-  allClusters: string[] = null;
+  allClusters: { cluster_id: string; cluster_type: string }[] = null;
   allDaemons = {};
   icons = Icons;
 
@@ -219,11 +221,13 @@ export class NfsFormComponent implements OnInit {
       res.sec_label_xattr = res.fsal.sec_label_xattr;
     }
 
-    this.daemonsSelections = _.map(
-      this.allDaemons[res.cluster_id],
-      (daemon) => new SelectOption(res.daemons.indexOf(daemon) !== -1, daemon, '')
-    );
-    this.daemonsSelections = [...this.daemonsSelections];
+    if (this.clusterType === NFSClusterType.user) {
+      this.daemonsSelections = _.map(
+        this.allDaemons[res.cluster_id],
+        (daemon) => new SelectOption(res.daemons.indexOf(daemon) !== -1, daemon, '')
+      );
+      this.daemonsSelections = [...this.daemonsSelections];
+    }
 
     res.protocolNfsv3 = res.protocols.indexOf(3) !== -1;
     res.protocolNfsv4 = res.protocols.indexOf(4) !== -1;
@@ -251,25 +255,28 @@ export class NfsFormComponent implements OnInit {
 
   resolveDaemons(daemons: Record<string, any>) {
     daemons = _.sortBy(daemons, ['daemon_id']);
+    const clusters = _.groupBy(daemons, 'cluster_id');
 
-    this.allClusters = _(daemons)
-      .map((daemon) => daemon.cluster_id)
-      .sortedUniq()
-      .value();
-
-    _.forEach(this.allClusters, (cluster) => {
-      this.allDaemons[cluster] = [];
+    this.allClusters = [];
+    _.forIn(clusters, (cluster, cluster_id) => {
+      this.allClusters.push({ cluster_id: cluster_id, cluster_type: cluster[0].cluster_type });
+      this.allDaemons[cluster_id] = [];
     });
 
     _.forEach(daemons, (daemon) => {
       this.allDaemons[daemon.cluster_id].push(daemon.daemon_id);
     });
 
+    if (this.isEdit) {
+      const cluster = _.find(this.allClusters, { cluster_id: this.cluster_id });
+      this.clusterType = cluster ? cluster.cluster_type : null;
+    }
+
     const hasOneCluster = _.isArray(this.allClusters) && this.allClusters.length === 1;
-    this.isDefaultCluster = hasOneCluster && this.allClusters[0] === '_default_';
+    this.isDefaultCluster = hasOneCluster && this.allClusters[0].cluster_id === '_default_';
     if (hasOneCluster) {
       this.nfsForm.patchValue({
-        cluster_id: this.allClusters[0]
+        cluster_id: this.allClusters[0].cluster_id
       });
       this.onClusterChange();
     }
@@ -459,11 +466,17 @@ export class NfsFormComponent implements OnInit {
 
   onClusterChange() {
     const cluster_id = this.nfsForm.getValue('cluster_id');
-    this.daemonsSelections = _.map(
-      this.allDaemons[cluster_id],
-      (daemon) => new SelectOption(false, daemon, '')
-    );
-    this.daemonsSelections = [...this.daemonsSelections];
+    const cluster = _.find(this.allClusters, { cluster_id: cluster_id });
+    this.clusterType = cluster ? cluster.cluster_type : null;
+    if (this.clusterType === NFSClusterType.user) {
+      this.daemonsSelections = _.map(
+        this.allDaemons[cluster_id],
+        (daemon) => new SelectOption(false, daemon, '')
+      );
+      this.daemonsSelections = [...this.daemonsSelections];
+    } else {
+      this.daemonsSelections = [];
+    }
     this.nfsForm.patchValue({ daemons: [] });
   }
 
@@ -485,6 +498,13 @@ export class NfsFormComponent implements OnInit {
     this.nfsForm.get('daemons').setValue(this.nfsForm.getValue('daemons'));
   }
 
+  onToggleAllDaemonsSelection() {
+    const cluster_id = this.nfsForm.getValue('cluster_id');
+    const daemons =
+      this.nfsForm.getValue('daemons').length === 0 ? this.allDaemons[cluster_id] : [];
+    this.nfsForm.patchValue({ daemons: daemons });
+  }
+
   submitAction() {
     let action: Observable<any>;
     const requestModel = this._buildRequest();