From c7e73eac658d537e28534212c29bc386e3375f6b Mon Sep 17 00:00:00 2001 From: Dnyaneshwari Date: Wed, 7 Aug 2024 12:56:10 +0530 Subject: [PATCH] mgr/dashboard: NFS Export form fixes Fixes: https://tracker.ceph.com/issues/67400 Signed-off-by: Dnyaneshwari Talwekar (cherry picked from commit 28d2fa30c25cb9d089e93921ef3be022c98ac35e) --- .../ceph/nfs/nfs-form/nfs-form.component.html | 22 +++-- .../nfs/nfs-form/nfs-form.component.spec.ts | 4 +- .../ceph/nfs/nfs-form/nfs-form.component.ts | 83 ++++++++++--------- .../app/shared/constants/cephfs.constant.ts | 1 + 4 files changed, 63 insertions(+), 47 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/constants/cephfs.constant.ts 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 ef78407b7e5ee..1e5654b6578bf 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 @@ -126,14 +126,12 @@ - - + [value]="subvol_grp.name" + [selected]="subvol_grp.name === nfsForm.get('subvolume_group').value">{{ subvol_grp.name }} @@ -149,7 +147,9 @@ formControlName="subvolume" name="subvolume" id="subvolume" - (change)="setSubVolPath()"> + (change)="setSubVolPath()" + [invalid]="nfsForm.controls.subvolume.invalid && (nfsForm.controls.subvolume.dirty)" + [invalidText]="subvolumeError"> @@ -160,8 +160,12 @@ value="" i18n>-- Select the CephFS subvolume -- + [value]="subvolume.name" + [selected]="subvolume.name === nfsForm.get('subvolume').value">{{ subvolume.name }} + This field is required. @@ -189,7 +193,9 @@ This field is required. - + Export on CephFS volume "/" not allowed. Path need to start with a '/' and can be followed by a word diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.spec.ts index 7f88c6486843f..5d27c18a4dff0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.spec.ts @@ -92,7 +92,7 @@ describe('NfsFormComponent', () => { clients: [], cluster_id: 'mynfs', fsal: { fs_name: '', name: 'CEPH' }, - path: '/', + path: '', protocolNfsv4: true, protocolNfsv3: true, pseudo: '', @@ -100,7 +100,7 @@ describe('NfsFormComponent', () => { security_label: false, squash: 'no_root_squash', subvolume: '', - subvolume_group: '', + subvolume_group: '_nogroup', transportTCP: true, transportUDP: true }); 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 18d91e2cddfdf..e6f413f326b69 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 @@ -31,6 +31,7 @@ import { NfsFormClientComponent } from '../nfs-form-client/nfs-form-client.compo import { getFsalFromRoute, getPathfromFsal } from '../utils'; import { CephfsSubvolumeService } from '~/app/shared/api/cephfs-subvolume.service'; import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service'; +import { DEFAULT_SUBVOLUME_GROUP } from '~/app/shared/constants/cephfs.constant'; @Component({ selector: 'cd-nfs-form', @@ -71,7 +72,7 @@ export class NfsFormComponent extends CdForm implements OnInit { selectedFsName: string = ''; selectedSubvolGroup: string = ''; selectedSubvol: string = ''; - defaultSubVolGroup = '_nogroup'; + defaultSubVolGroup = DEFAULT_SUBVOLUME_GROUP; pathDataSource = (text$: Observable) => { return text$.pipe( @@ -160,9 +161,7 @@ export class NfsFormComponent extends CdForm implements OnInit { } volumeChangeHandler() { - this.pathChangeHandler(); - const fs_name = this.nfsForm.getValue('fsal').fs_name; - this.getSubVolGrp(fs_name); + this.isDefaultSubvolumeGroup(); } async getSubVol() { @@ -176,6 +175,7 @@ export class NfsFormComponent extends CdForm implements OnInit { ).subscribe((data: any) => { this.allsubvols = data; }); + this.setUpVolumeValidation(); } getSubVolGrp(fs_name: string) { @@ -185,16 +185,15 @@ export class NfsFormComponent extends CdForm implements OnInit { } setSubVolGrpPath(): Promise { - return new Promise((resolve, reject) => { - const subvolGroup = this.nfsForm.getValue('subvolume_group'); - const fs_name = this.nfsForm.getValue('fsal').fs_name; + const fsName = this.nfsForm.getValue('fsal').fs_name; + const subvolGroup = this.nfsForm.getValue('subvolume_group'); - if (subvolGroup === this.defaultSubVolGroup) { + return new Promise((resolve, reject) => { + if (subvolGroup == this.defaultSubVolGroup) { this.updatePath('/volumes/' + this.defaultSubVolGroup); - resolve(); - } else { + } else if (subvolGroup != '') { this.subvolgrpService - .info(fs_name, subvolGroup) + .info(fsName, subvolGroup) .pipe(map((data) => data['path'])) .subscribe( (path) => { @@ -203,10 +202,23 @@ export class NfsFormComponent extends CdForm implements OnInit { }, (error) => reject(error) ); + } else { + this.updatePath(''); + this.setUpVolumeValidation(); } + resolve(); }); } + // Checking if subVolGroup is "_nogroup" and updating path to default as "/volumes/_nogroup else blank." + isDefaultSubvolumeGroup() { + const fsName = this.nfsForm.getValue('fsal').fs_name; + this.getSubVolGrp(fsName); + this.getSubVol(); + this.updatePath('/volumes/' + this.defaultSubVolGroup); + this.setUpVolumeValidation(); + } + setSubVolPath(): Promise { return new Promise((resolve, reject) => { const subvol = this.nfsForm.getValue('subvolume'); @@ -226,9 +238,21 @@ export class NfsFormComponent extends CdForm implements OnInit { }); } + setUpVolumeValidation() { + const subvolumeGroup = this.nfsForm.get('subvolume_group').value; + const subVolumeControl = this.nfsForm.get('subvolume'); + + // SubVolume is required if SubVolume Group is "_nogroup". + if (subvolumeGroup == this.defaultSubVolGroup) { + subVolumeControl?.setValidators([Validators.required]); + } else { + subVolumeControl?.clearValidators(); + } + subVolumeControl?.updateValueAndValidity(); + } + updatePath(path: string) { this.nfsForm.patchValue({ path: path }); - this.pathChangeHandler(); } createForm() { @@ -248,10 +272,13 @@ export class NfsFormComponent extends CdForm implements OnInit { ] }) }), - subvolume_group: new UntypedFormControl(''), + subvolume_group: new UntypedFormControl(this.defaultSubVolGroup), subvolume: new UntypedFormControl(''), - path: new UntypedFormControl('/', { - validators: [Validators.required] + path: new UntypedFormControl('', { + validators: [ + Validators.required, + CdValidators.custom('isIsolatedSlash', this.isolatedSlashCondition) // Path can never be single "/". + ] }), protocolNfsv3: new UntypedFormControl(true, { validators: [ @@ -416,6 +443,10 @@ export class NfsFormComponent extends CdForm implements OnInit { this.defaultAccessType[name] = accessType; } + isolatedSlashCondition(value: string): boolean { + return value === '/'; + } + setPathValidation() { const path = this.nfsForm.get('path'); if (this.storageBackend === SUPPORTED_FSAL.RGW) { @@ -462,14 +493,6 @@ export class NfsFormComponent extends CdForm implements OnInit { ); } - pathChangeHandler() { - if (!this.isEdit) { - this.nfsForm.patchValue({ - pseudo: this.generatePseudo() - }); - } - } - private getBucketTypeahead(path: string): Observable { if (_.isString(path) && path !== '/' && path !== '') { return this.rgwBucketService.list().pipe( @@ -485,20 +508,6 @@ export class NfsFormComponent extends CdForm implements OnInit { } } - private generatePseudo() { - let newPseudo = this.nfsForm.getValue('pseudo'); - if (this.nfsForm.get('pseudo') && !this.nfsForm.get('pseudo').dirty) { - newPseudo = undefined; - if (this.storageBackend === 'CEPH') { - newPseudo = '/cephfs'; - if (_.isString(this.nfsForm.getValue('path'))) { - newPseudo += this.nfsForm.getValue('path'); - } - } - } - return newPseudo; - } - submitAction() { let action: Observable; const requestModel = this.buildRequest(); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/cephfs.constant.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/cephfs.constant.ts new file mode 100644 index 0000000000000..56890ff72140a --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/constants/cephfs.constant.ts @@ -0,0 +1 @@ +export const DEFAULT_SUBVOLUME_GROUP = '_nogroup'; -- 2.39.5