From: Avan Thakkar Date: Fri, 5 Jul 2024 09:29:13 +0000 (+0530) Subject: mgr/dashboard: add NFS export route from subvolume/subvolume grp X-Git-Tag: v18.2.5~520^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=49e8365db4e848493aa71b793ed08616ec915fe9;p=ceph.git mgr/dashboard: add NFS export route from subvolume/subvolume grp Signed-off-by: Avan Thakkar (cherry picked from commit c0b2db74b2a99eb0ee4e4a37997d6516d8622855) --- diff --git a/src/pybind/mgr/dashboard/controllers/cephfs.py b/src/pybind/mgr/dashboard/controllers/cephfs.py index 7183808e19f17..357909be6881a 100644 --- a/src/pybind/mgr/dashboard/controllers/cephfs.py +++ b/src/pybind/mgr/dashboard/controllers/cephfs.py @@ -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, { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts index 63a58d8ee203a..0152968618721 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts @@ -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, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts index a91daf8cb930d..a8b62556f28be 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts @@ -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; subject = new BehaviorSubject([]); + 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', diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts index 58d849c901ef5..2d646f968dc54 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts @@ -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', diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.html index a83091a2c39a1..6a3c96de545b9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.html @@ -146,7 +146,7 @@
+ *ngIf="storageBackend === 'CEPH' && nfsForm.getValue('fsal').fs_name"> @@ -167,12 +167,13 @@ i18n>-- Select the CephFS subvolume group -- +
+ *ngIf="storageBackend === 'CEPH' && nfsForm.getValue('fsal').fs_name"> @@ -181,7 +182,7 @@ formControlName="subvolume" name="subvolume" id="subvolume" - (change)="getPath()"> + (change)="setSubVolPath()"> diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.ts index 438c01a7370ca..e4a65e595ea0e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.ts @@ -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) => { 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 { + return new Promise((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 { + return new Promise((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() { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts index 0aa297148617c..4923b77e15026 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts @@ -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`; } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts index be454076b8621..7b2f859f2cd97 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts @@ -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