]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Subvolume group and subvolume not listed in NFS Share form on edit. 70251/head
authorDnyaneshwari Talwekar <dtalwekar@li-4c4c4544-0038-3510-8056-b5c04f473234.ibm.com>
Thu, 16 Jul 2026 13:00:31 +0000 (18:30 +0530)
committerDnyaneshwari Talwekar <dtalwekar@li-4c4c4544-0038-3510-8056-b5c04f473234.ibm.com>
Wed, 22 Jul 2026 07:01:47 +0000 (12:31 +0530)
Signed-off-by: Dnyaneshwari Talwekar <dtalweka@redhat.com>
Fixes: https://tracker.ceph.com/issues/78281
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
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.ts

index e8a5ada4bfc013add95f604096075e8a167bd791..a9d25ff3640ee9940298a75954e707eca077c7f8 100644 (file)
@@ -220,6 +220,29 @@ describe('NfsFormComponent', () => {
     });
   });
 
+  describe('edit mode CephFS subvolume fields', () => {
+    it('should populate subvolume group and subvolume from export path', () => {
+      component.isEdit = true;
+      spyOn(component['subvolgrpService'], 'get').and.returnValue(of([{ name: 'g1' }]));
+      spyOn(component['subvolService'], 'get').and.returnValue(of([{ name: 'sv1' }]));
+
+      component.resolveModel({
+        fsal: { name: 'CEPH', fs_name: 'testfs' },
+        path: '/volumes/g1/sv1',
+        protocols: [3, 4],
+        transports: ['TCP'],
+        clients: [],
+        squash: 'none'
+      });
+
+      expect(component.nfsForm.get('subvolume_group').value).toBe('g1');
+      expect(component.nfsForm.get('subvolume').value).toBe('sv1');
+      expect(component['subvolgrpService'].get).toHaveBeenCalledWith('testfs');
+      expect(component['subvolService'].get).toHaveBeenCalledWith('testfs', 'g1');
+      expect(component.allsubvols).toEqual([{ name: 'sv1' }]);
+    });
+  });
+
   describe('pathExistence', () => {
     beforeEach(() => {
       component['nfsService']['lsDir'] = jest.fn((): Observable<Directory> =>
index 5b214b873f93d51ac1c443ce47438ceefa1b34b5..b0e8f8f34276e0deeb28cf7523ffe76b52b7a1b7 100644 (file)
@@ -138,7 +138,7 @@ export class NfsFormComponent extends CdForm implements OnInit {
         (params: { cluster_id: string; export_id: string; rgw_export_type?: string }) => {
           this.cluster_id = decodeURIComponent(params.cluster_id);
           this.export_id = decodeURIComponent(params.export_id);
-          if (params.rgw_export_type) {
+          if (params.rgw_export_type && this.storageBackend === SUPPORTED_FSAL.RGW) {
             this.nfsForm.get('rgw_export_type').setValue(params.rgw_export_type);
             if (params.rgw_export_type === RgwExportType.BUCKET) {
               this.setBucket();
@@ -186,11 +186,17 @@ export class NfsFormComponent extends CdForm implements OnInit {
     this.isDefaultSubvolumeGroup();
   }
 
-  async getSubVol() {
-    const fs_name = this.nfsForm.getValue('fsal').fs_name;
+  async getSubVol(fsName?: string) {
+    const fs_name = fsName ?? this.nfsForm.getRawValue()?.fsal?.fs_name;
     const subvolgrp = this.nfsForm.getValue('subvolume_group');
 
-    await this.setSubVolGrpPath();
+    if (!fs_name || !subvolgrp) {
+      return;
+    }
+
+    if (!this.isEdit) {
+      await this.setSubVolGrpPath();
+    }
 
     (subvolgrp === this.defaultSubVolGroup
       ? this.subvolService.get(fs_name)
@@ -422,12 +428,14 @@ export class NfsFormComponent extends CdForm implements OnInit {
 
     this.nfsForm.patchValue({
       fsal: {
-        ...this.nfsForm.get('fsal').value,
+        ...this.nfsForm.getRawValue()?.fsal,
         fs_name: fsName
       },
-      subvolumeGroup,
+      subvolume_group: subvolumeGroup,
       subvolume
     });
+    this.setUpVolumeValidation();
+    this.getSubVol(fsName);
   }
 
   resolveClusters(clusters: string[]) {
@@ -725,7 +733,7 @@ export class NfsFormComponent extends CdForm implements OnInit {
       if (control.pristine || !control.value) {
         return of({ required: true });
       }
-      const fsName = this.nfsForm.getValue('fsal').fs_name;
+      const fsName = this.nfsForm.getRawValue()?.fsal?.fs_name;
       return this.nfsService.lsDir(fsName, control.value).pipe(
         map((directory: Directory) =>
           directory.paths.includes(control.value) === requiredExistenceResult
index 5758325a8e3f9ba2e74777d345916cd30d166ab1..403513dd1d5774238c0c3a77883797341d949e1b 100644 (file)
@@ -16,8 +16,9 @@ import { TaskListService } from '~/app/shared/services/task-list.service';
 import { SharedModule } from '~/app/shared/shared.module';
 import { configureTestBed, expectItemTasks, PermissionHelper } from '~/testing/unit-test-helper';
 import { NfsDetailsComponent } from '../nfs-details/nfs-details.component';
-import { NfsListComponent } from './nfs-list.component';
-import { SUPPORTED_FSAL } from '../models/nfs.fsal';
+import { NfsListComponent, RgwExportType } from './nfs-list.component';
+import { RGW_USER_EXPORT_PATH, SUPPORTED_FSAL } from '../models/nfs.fsal';
+import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
 
 describe('NfsListComponent', () => {
   let component: NfsListComponent;
@@ -55,6 +56,53 @@ describe('NfsListComponent', () => {
     expect(component).toBeTruthy();
   });
 
+  describe('edit action routerLink', () => {
+    let editRouterLink: () => string | [string, { rgw_export_type: RgwExportType }];
+
+    beforeEach(() => {
+      editRouterLink = component.tableActions.find((action) => action.permission === 'update')
+        .routerLink as () => string | [string, { rgw_export_type: RgwExportType }];
+    });
+
+    it('should return a plain string for CephFS edit (no route params)', () => {
+      component.fsal = SUPPORTED_FSAL.CEPH;
+      component.selection = new CdTableSelection([
+        { cluster_id: 'mycluster', export_id: '42', path: '/volumes/g1/sv1' }
+      ]);
+
+      const link = editRouterLink();
+
+      expect(typeof link).toBe('string');
+      expect(link).toBe('/cephfs/nfs/edit/mycluster/42');
+    });
+
+    it('should return array with rgw_export_type for RGW bucket edit', () => {
+      component.fsal = SUPPORTED_FSAL.RGW;
+      component.selection = new CdTableSelection([
+        { cluster_id: 'rgw-cluster', export_id: '7', path: '/my-bucket' }
+      ]);
+
+      const link = editRouterLink();
+
+      expect(Array.isArray(link)).toBe(true);
+      expect(link[0]).toBe('/rgw/nfs/edit/rgw-cluster/7');
+      expect(link[1]).toEqual({ rgw_export_type: RgwExportType.BUCKET });
+    });
+
+    it('should return array with rgw_export_type user for RGW user-level edit', () => {
+      component.fsal = SUPPORTED_FSAL.RGW;
+      component.selection = new CdTableSelection([
+        { cluster_id: 'rgw-cluster', export_id: '8', path: RGW_USER_EXPORT_PATH }
+      ]);
+
+      const link = editRouterLink();
+
+      expect(Array.isArray(link)).toBe(true);
+      expect(link[0]).toBe('/rgw/nfs/edit/rgw-cluster/8');
+      expect(link[1]).toEqual({ rgw_export_type: RgwExportType.USER });
+    });
+  });
+
   describe('after ngOnInit', () => {
     beforeEach(() => {
       fixture.detectChanges();
index 7ddee926273c758537b965be375ac3422ed2b089..594758878691220fec137a42467ae36b75121576 100644 (file)
@@ -109,17 +109,23 @@ export class NfsListComponent extends ListWithDetails implements OnInit, OnDestr
     const editAction: CdTableAction = {
       permission: 'update',
       icon: Icons.edit,
-      routerLink: () => [
-        `/${prefix}/nfs/edit/${getNfsUri()}`,
-        {
-          rgw_export_type:
-            this.fsal === SUPPORTED_FSAL.RGW &&
-            !_.isEmpty(this.selection?.first()?.path) &&
-            this.selection?.first()?.path !== RGW_USER_EXPORT_PATH
-              ? RgwExportType.BUCKET
-              : RgwExportType.USER
+      routerLink: () => {
+        const currentPrefix = getPathfromFsal(this.fsal);
+        const editUrl = `/${currentPrefix}/nfs/edit/${getNfsUri()}`;
+        if (this.fsal !== SUPPORTED_FSAL.RGW) {
+          return editUrl;
         }
-      ],
+        return [
+          editUrl,
+          {
+            rgw_export_type:
+              !_.isEmpty(this.selection?.first()?.path) &&
+              this.selection?.first()?.path !== RGW_USER_EXPORT_PATH
+                ? RgwExportType.BUCKET
+                : RgwExportType.USER
+          }
+        ];
+      },
       name: this.actionLabels.EDIT
     };