]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: add NFS export route from subvolume/subvolume grp 58657/head
authorAvan Thakkar <athakkar@redhat.com>
Fri, 5 Jul 2024 09:29:13 +0000 (14:59 +0530)
committerAvan Thakkar <athakkar@redhat.com>
Thu, 18 Jul 2024 07:17:45 +0000 (12:47 +0530)
Signed-off-by: Avan Thakkar <athakkar@redhat.com>
(cherry picked from commit c0b2db74b2a99eb0ee4e4a37997d6516d8622855)

src/pybind/mgr/dashboard/controllers/cephfs.py
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts
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.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts

index 7183808e19f177cb87d1b9eebed2df47dcf7340c..357909be6881a59cf1105cc88b3c1f5073ec5a70 100644 (file)
@@ -844,8 +844,17 @@ class CephFSSubvolumeGroups(RESTController):
         if error_code != 0:
             raise DashboardException(
                 f'Failed to get info for subvolume group {group_name}: {err}'
+
             )
-        return json.loads(out)
+        group = json.loads(out)
+        error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_getpath', None, {
+            'vol_name': vol_name, 'group_name': group_name})
+        if error_code != 0:
+            raise DashboardException(
+                f'Failed to get path for subvolume group {group_name}: {err}'
+            )
+        group['path'] = out
+        return group
 
     def create(self, vol_name: str, group_name: str, **kwargs):
         error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_create', None, {
index 63a58d8ee203a6fcb9f0e587afcb0bb879245cb2..01529686187216e893b5478a6c643a50c5b403ca 100644 (file)
@@ -420,6 +420,11 @@ const routes: Routes = [
         },
         children: [
           { path: '', component: NfsListComponent },
+          {
+            path: `${URLVerbs.CREATE}/:fs_name/:subvolume_group`,
+            component: NfsFormComponent,
+            data: { breadcrumbs: ActionLabels.CREATE }
+          },
           {
             path: URLVerbs.CREATE,
             component: NfsFormComponent,
index a91daf8cb930d8319d9956e372fb9362920db59d..a8b62556f28bebfd31dcf65310bf4b57ec558719 100644 (file)
@@ -18,6 +18,8 @@ import { CriticalConfirmationModalComponent } from '~/app/shared/components/crit
 import { FinishedTask } from '~/app/shared/models/finished-task';
 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
 import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
+import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import _ from 'lodash';
 
 @Component({
   selector: 'cd-cephfs-subvolume-group',
@@ -54,6 +56,8 @@ export class CephfsSubvolumeGroupComponent implements OnInit, OnChanges {
   subvolumeGroup$: Observable<CephfsSubvolumeGroup[]>;
   subject = new BehaviorSubject<CephfsSubvolumeGroup[]>([]);
 
+  modalRef: NgbModalRef;
+
   constructor(
     private cephfsSubvolumeGroup: CephfsSubvolumeGroupService,
     private actionLabels: ActionLabelsI18n,
@@ -116,6 +120,13 @@ export class CephfsSubvolumeGroupComponent implements OnInit, OnChanges {
         icon: Icons.edit,
         click: () => this.openModal(true)
       },
+      {
+        name: this.actionLabels.NFS_EXPORT,
+        permission: 'create',
+        icon: Icons.nfsExport,
+        routerLink: () => ['/cephfs/nfs/create', this.fsName, this.selection?.first()?.name],
+        disable: () => !this.selection.hasSingleSelection
+      },
       {
         name: this.actionLabels.REMOVE,
         permission: 'delete',
index 58d849c901ef57bad6a3abf55fcea7d329c812f2..2d646f968dc543a4ffe78616cfcfd77d8c4c0bc7 100644 (file)
@@ -33,6 +33,9 @@ import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-g
 import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
 import { CephfsMountDetailsComponent } from '../cephfs-mount-details/cephfs-mount-details.component';
 import { HealthService } from '~/app/shared/api/health.service';
+import _ from 'lodash';
+
+const DEFAULT_SUBVOLUME_GROUP = '_nogroup';
 
 @Component({
   selector: 'cd-cephfs-subvolume-list',
@@ -159,6 +162,18 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
         disable: () => !this.selection?.hasSelection,
         click: () => this.showAttachInfo()
       },
+      {
+        name: this.actionLabels.NFS_EXPORT,
+        permission: 'create',
+        icon: Icons.nfsExport,
+        routerLink: () => [
+          '/cephfs/nfs/create',
+          this.fsName,
+          _.isEmpty(this.activeGroupName) ? DEFAULT_SUBVOLUME_GROUP : this.activeGroupName,
+          { subvolume: this.selection?.first()?.name }
+        ],
+        disable: () => !this.selection?.hasSingleSelection
+      },
       {
         name: this.actionLabels.REMOVE,
         permission: 'delete',
index a83091a2c39a14c26618abf00162ec2df1d174ac..6a3c96de545b984ae2866a8a161e7503c73b99ce 100644 (file)
         </div>
 
         <div class="form-group row"
-             *ngIf="storageBackend === 'CEPH'">
+             *ngIf="storageBackend === 'CEPH' && nfsForm.getValue('fsal').fs_name">
           <label class="cd-col-form-label"
                  for="subvolume_group"
                  i18n>Subvolume Group</label>
                       i18n>-- Select the CephFS subvolume group --</option>
               <option *ngFor="let subvol_grp of allsubvolgrps"
                       [value]="subvol_grp.name">{{ subvol_grp.name }}</option>
+              <option [value]="defaultSubVolGroup">{{ defaultSubVolGroup }}</option>
             </select>
           </div>
         </div>
 
       <div class="form-group row"
-           *ngIf="storageBackend === 'CEPH'">
+           *ngIf="storageBackend === 'CEPH' && nfsForm.getValue('fsal').fs_name">
         <label class="cd-col-form-label"
                for="subvolume"
                i18n>Subvolume</label>
                   formControlName="subvolume"
                   name="subvolume"
                   id="subvolume"
-                  (change)="getPath()">
+                  (change)="setSubVolPath()">
             <option *ngIf="allsubvols === null"
                     value=""
                     i18n>Loading...</option>
index 438c01a7370caf492591bb93a661f03a83d01898..e4a65e595ea0e00a15d24f8aa1e5fa9fe7b54769 100644 (file)
@@ -65,7 +65,11 @@ export class NfsFormComponent extends CdForm implements OnInit {
 
   allsubvolgrps: any[] = [];
   allsubvols: any[] = [];
-  fsPath: string = null;
+
+  selectedFsName: string = '';
+  selectedSubvolGroup: string = '';
+  selectedSubvol: string = '';
+  defaultSubVolGroup = '_nogroup';
 
   pathDataSource = (text$: Observable<string>) => {
     return text$.pipe(
@@ -129,6 +133,14 @@ export class NfsFormComponent extends CdForm implements OnInit {
       this.nfsForm.get('cluster_id').disable();
     } else {
       this.action = this.actionLabels.CREATE;
+      this.route.params.subscribe(
+        (params: { fs_name: string; subvolume_group: string; subvolume?: string }) => {
+          this.selectedFsName = params.fs_name;
+          this.selectedSubvolGroup = params.subvolume_group;
+          if (params.subvolume) this.selectedSubvol = params.subvolume;
+        }
+      );
+
       this.getData(promises);
     }
   }
@@ -152,40 +164,69 @@ export class NfsFormComponent extends CdForm implements OnInit {
     this.getSubVolGrp(fs_name);
   }
 
-  getSubVol() {
-    this.getPath();
+  async getSubVol() {
     const fs_name = this.nfsForm.getValue('fsal').fs_name;
     const subvolgrp = this.nfsForm.getValue('subvolume_group');
-    return this.subvolService.get(fs_name, subvolgrp).subscribe((data: any) => {
+    await this.setSubVolGrpPath();
+
+    (subvolgrp === this.defaultSubVolGroup
+      ? this.subvolService.get(fs_name)
+      : this.subvolService.get(fs_name, subvolgrp)
+    ).subscribe((data: any) => {
       this.allsubvols = data;
     });
   }
 
   getSubVolGrp(fs_name: string) {
-    return this.subvolgrpService.get(fs_name).subscribe((data: any) => {
+    this.subvolgrpService.get(fs_name).subscribe((data: any) => {
       this.allsubvolgrps = data;
     });
   }
 
-  getFsPath(volList: any[], value: string) {
-    const match = volList.find((vol) => vol.name === value);
-    if (match) {
-      return match.info.path;
-    }
+  setSubVolGrpPath(): Promise<void> {
+    return new Promise<void>((resolve, reject) => {
+      const subvolGroup = this.nfsForm.getValue('subvolume_group');
+      const fs_name = this.nfsForm.getValue('fsal').fs_name;
+
+      if (subvolGroup === this.defaultSubVolGroup) {
+        this.updatePath('/volumes/' + this.defaultSubVolGroup);
+        resolve();
+      } else {
+        this.subvolgrpService
+          .info(fs_name, subvolGroup)
+          .pipe(map((data) => data['path']))
+          .subscribe(
+            (path) => {
+              this.updatePath(path);
+              resolve();
+            },
+            (error) => reject(error)
+          );
+      }
+    });
   }
 
-  getPath() {
-    const subvol = this.nfsForm.getValue('subvolume');
-    if (subvol === '') {
+  setSubVolPath(): Promise<void> {
+    return new Promise<void>((resolve, reject) => {
+      const subvol = this.nfsForm.getValue('subvolume');
       const subvolGroup = this.nfsForm.getValue('subvolume_group');
-      this.fsPath = this.getFsPath(this.allsubvolgrps, subvolGroup);
-    } else {
-      this.fsPath = this.getFsPath(this.allsubvols, subvol);
-    }
-    this.nfsForm.patchValue({
-      path: this.fsPath
+      const fs_name = this.nfsForm.getValue('fsal').fs_name;
+
+      this.subvolService
+        .info(fs_name, subvol, subvolGroup === this.defaultSubVolGroup ? '' : subvolGroup)
+        .pipe(map((data) => data['path']))
+        .subscribe(
+          (path) => {
+            this.updatePath(path);
+            resolve();
+          },
+          (error) => reject(error)
+        );
     });
+  }
 
+  updatePath(path: string) {
+    this.nfsForm.patchValue({ path: path });
     this.pathChangeHandler();
   }
 
@@ -306,8 +347,34 @@ export class NfsFormComponent extends CdForm implements OnInit {
     }
   }
 
+  resolveRouteParams() {
+    if (!_.isEmpty(this.selectedFsName)) {
+      this.nfsForm.patchValue({
+        fsal: {
+          fs_name: this.selectedFsName
+        }
+      });
+      this.volumeChangeHandler();
+    }
+    if (!_.isEmpty(this.selectedSubvolGroup)) {
+      this.nfsForm.patchValue({
+        subvolume_group: this.selectedSubvolGroup
+      });
+      this.getSubVol();
+    }
+    if (!_.isEmpty(this.selectedSubvol)) {
+      this.nfsForm.patchValue({
+        subvolume: this.selectedSubvol
+      });
+      this.setSubVolPath();
+    }
+  }
+
   resolveFilesystems(filesystems: any[]) {
     this.allFsNames = filesystems;
+    if (!this.isEdit) {
+      this.resolveRouteParams();
+    }
   }
 
   fsalChangeHandler() {
index 0aa297148617c9da1f1bda1f9b1fb6a6e74f724a..4923b77e150261d127d2280317ad88f72ae162ad 100644 (file)
@@ -145,6 +145,7 @@ export class ActionLabelsI18n {
   DEACTIVATE: string;
   ATTACH: string;
   AUTHORIZE: string;
+  NFS_EXPORT: string;
 
   constructor() {
     /* Create a new item */
@@ -228,6 +229,8 @@ export class ActionLabelsI18n {
     this.DEACTIVATE = $localize`Deactivate`;
 
     this.ATTACH = $localize`Attach`;
+
+    this.NFS_EXPORT = $localize`Create NFS Export`;
   }
 }
 
index be454076b862142df628a4d189fe03b3380ff712..7b2f859f2cd97da79304c469d6251764e469e7d3 100644 (file)
@@ -84,6 +84,7 @@ export enum Icons {
   eye = 'fa fa-eye', // Observability
   calendar = 'fa fa-calendar',
   externalUrl = 'fa fa-external-link', // links to external page
+  nfsExport = 'fa fa-server', // NFS export
 
   /* Icons for special effect */
   large = 'fa fa-lg', // icon becomes 33% larger