Skip to content

Commit 28d2fa3

Browse files
author
Dnyaneshwari
committed
mgr/dashboard: NFS Export form fixes
Fixes: https://tracker.ceph.com/issues/67400 Signed-off-by: Dnyaneshwari Talwekar <[email protected]>
1 parent c9bd62a commit 28d2fa3

File tree

4 files changed

+68
-48
lines changed

4 files changed

+68
-48
lines changed

src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.html

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
label="Volume"
7474
cdRequiredField="Volume"
7575
id="fs_name"
76+
(change)="volumeChangeHandler()"
7677
[invalid]="nfsForm.controls.fsal.controls.fs_name.invalid && (nfsForm.controls.fsal.controls.fs_name.dirty)"
7778
[invalidText]="fsNameError"
7879
[skeleton]="allFsNames === null"
@@ -164,12 +165,11 @@
164165
i18n>
165166
<option *ngIf="allsubvolgrps === null"
166167
value="">Loading...</option>
167-
<option *ngIf="allsubvolgrps !== null && allsubvolgrps.length === 0"
168-
value="">-- No CephFS subvolume group available --</option>
169-
<option *ngIf="allsubvolgrps !== null && allsubvolgrps.length > 0"
168+
<option *ngIf="allsubvolgrps !== null && allsubvolgrps.length >= 0"
170169
value="">-- Select the CephFS subvolume group --</option>
171170
<option *ngFor="let subvol_grp of allsubvolgrps"
172-
[value]="subvol_grp.name">{{ subvol_grp.name }}</option>
171+
[value]="subvol_grp.name"
172+
[selected]="subvol_grp.name === nfsForm.get('subvolume_group').value">{{ subvol_grp.name }}</option>
173173
<option [value]="defaultSubVolGroup">{{ defaultSubVolGroup }}</option>
174174
</cds-select>
175175
</div>
@@ -183,6 +183,8 @@
183183
id="subvolume"
184184
[skeleton]="allsubvols === null"
185185
(change)="setSubVolPath()"
186+
[invalid]="nfsForm.controls.subvolume.invalid && (nfsForm.controls.subvolume.dirty)"
187+
[invalidText]="subvolumeError"
186188
i18n>
187189
<option *ngIf="allsubvols === null"
188190
value="">Loading...</option>
@@ -191,8 +193,17 @@
191193
<option *ngIf="allsubvols !== null && allsubvols.length > 0"
192194
value="">-- Select the CephFS subvolume --</option>
193195
<option *ngFor="let subvolume of allsubvols"
194-
[value]="subvolume.name">{{ subvolume.name }}</option>
196+
[value]="subvolume.name"
197+
[selected]="subvolume.name === nfsForm.get('subvolume').value">{{ subvolume.name }}</option>
195198
</cds-select>
199+
<ng-template #subvolumeError>
200+
<span
201+
*ngIf="nfsForm.getValue('subvolume_group') === defaultSubVolGroup && !nfsForm.getValue('subvolume')"
202+
class="invalid-feedback"
203+
i18n>
204+
This field is required.
205+
</span>
206+
</ng-template>
196207
</div>
197208

198209
<!-- Path -->
@@ -220,6 +231,9 @@
220231
<span class="invalid-feedback"
221232
*ngIf="nfsForm.showError('path', formDir, 'required')"
222233
i18n>This field is required.</span>
234+
<span class="invalid-feedback"
235+
*ngIf="nfsForm.get('path').hasError('isIsolatedSlash') && nfsForm.get('path').touched"
236+
i18n>Export on CephFS volume "<code>/</code>" not allowed.</span>
223237
<span class="invalid-feedback"
224238
*ngIf="nfsForm.showError('path', formDir, 'pattern')"
225239
i18n>Path need to start with a '/' and can be followed by a word</span>

src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ describe('NfsFormComponent', () => {
9292
clients: [],
9393
cluster_id: 'mynfs',
9494
fsal: { fs_name: '', name: 'CEPH', user_id: '' },
95-
path: '/',
95+
path: '',
9696
protocolNfsv4: true,
9797
protocolNfsv3: true,
9898
pseudo: '',
@@ -101,7 +101,7 @@ describe('NfsFormComponent', () => {
101101
security_label: false,
102102
squash: 'no_root_squash',
103103
subvolume: '',
104-
subvolume_group: '',
104+
subvolume_group: '_nogroup',
105105
transportTCP: true,
106106
transportUDP: true
107107
});

src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.ts

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { CephfsSubvolumeService } from '~/app/shared/api/cephfs-subvolume.servic
3333
import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
3434
import { RgwUserService } from '~/app/shared/api/rgw-user.service';
3535
import { RgwExportType } from '../nfs-list/nfs-list.component';
36+
import { DEFAULT_SUBVOLUME_GROUP } from '~/app/shared/constants/cephfs.constant';
3637

3738
@Component({
3839
selector: 'cd-nfs-form',
@@ -75,7 +76,7 @@ export class NfsFormComponent extends CdForm implements OnInit {
7576
selectedFsName: string = '';
7677
selectedSubvolGroup: string = '';
7778
selectedSubvol: string = '';
78-
defaultSubVolGroup = '_nogroup';
79+
defaultSubVolGroup = DEFAULT_SUBVOLUME_GROUP;
7980

8081
pathDataSource = (text$: Observable<string>) => {
8182
return text$.pipe(
@@ -181,9 +182,7 @@ export class NfsFormComponent extends CdForm implements OnInit {
181182
}
182183

183184
volumeChangeHandler() {
184-
this.pathChangeHandler();
185-
const fs_name = this.nfsForm.getValue('fsal').fs_name;
186-
this.getSubVolGrp(fs_name);
185+
this.isDefaultSubvolumeGroup();
187186
}
188187

189188
async getSubVol() {
@@ -197,6 +196,7 @@ export class NfsFormComponent extends CdForm implements OnInit {
197196
).subscribe((data: any) => {
198197
this.allsubvols = data;
199198
});
199+
this.setUpVolumeValidation();
200200
}
201201

202202
getSubVolGrp(fs_name: string) {
@@ -206,16 +206,15 @@ export class NfsFormComponent extends CdForm implements OnInit {
206206
}
207207

208208
setSubVolGrpPath(): Promise<void> {
209-
return new Promise<void>((resolve, reject) => {
210-
const subvolGroup = this.nfsForm.getValue('subvolume_group');
211-
const fs_name = this.nfsForm.getValue('fsal').fs_name;
209+
const fsName = this.nfsForm.getValue('fsal').fs_name;
210+
const subvolGroup = this.nfsForm.getValue('subvolume_group');
212211

213-
if (subvolGroup === this.defaultSubVolGroup) {
212+
return new Promise<void>((resolve, reject) => {
213+
if (subvolGroup == this.defaultSubVolGroup) {
214214
this.updatePath('/volumes/' + this.defaultSubVolGroup);
215-
resolve();
216-
} else {
215+
} else if (subvolGroup != '') {
217216
this.subvolgrpService
218-
.info(fs_name, subvolGroup)
217+
.info(fsName, subvolGroup)
219218
.pipe(map((data) => data['path']))
220219
.subscribe(
221220
(path) => {
@@ -224,10 +223,23 @@ export class NfsFormComponent extends CdForm implements OnInit {
224223
},
225224
(error) => reject(error)
226225
);
226+
} else {
227+
this.updatePath('');
228+
this.setUpVolumeValidation();
227229
}
230+
resolve();
228231
});
229232
}
230233

234+
// Checking if subVolGroup is "_nogroup" and updating path to default as "/volumes/_nogroup else blank."
235+
isDefaultSubvolumeGroup() {
236+
const fsName = this.nfsForm.getValue('fsal').fs_name;
237+
this.getSubVolGrp(fsName);
238+
this.getSubVol();
239+
this.updatePath('/volumes/' + this.defaultSubVolGroup);
240+
this.setUpVolumeValidation();
241+
}
242+
231243
setSubVolPath(): Promise<void> {
232244
return new Promise<void>((resolve, reject) => {
233245
const subvol = this.nfsForm.getValue('subvolume');
@@ -247,9 +259,21 @@ export class NfsFormComponent extends CdForm implements OnInit {
247259
});
248260
}
249261

262+
setUpVolumeValidation() {
263+
const subvolumeGroup = this.nfsForm.get('subvolume_group').value;
264+
const subVolumeControl = this.nfsForm.get('subvolume');
265+
266+
// SubVolume is required if SubVolume Group is "_nogroup".
267+
if (subvolumeGroup == this.defaultSubVolGroup) {
268+
subVolumeControl?.setValidators([Validators.required]);
269+
} else {
270+
subVolumeControl?.clearValidators();
271+
}
272+
subVolumeControl?.updateValueAndValidity();
273+
}
274+
250275
updatePath(path: string) {
251276
this.nfsForm.patchValue({ path: path });
252-
this.pathChangeHandler();
253277
}
254278

255279
createForm() {
@@ -258,8 +282,11 @@ export class NfsFormComponent extends CdForm implements OnInit {
258282
cluster_id: new UntypedFormControl('', {
259283
validators: [Validators.required]
260284
}),
261-
path: new UntypedFormControl('/', {
262-
validators: [Validators.required]
285+
path: new UntypedFormControl('', {
286+
validators: [
287+
Validators.required,
288+
CdValidators.custom('isIsolatedSlash', this.isolatedSlashCondition) // Path can never be single "/".
289+
]
263290
}),
264291
protocolNfsv3: new UntypedFormControl(true, {
265292
validators: [
@@ -324,7 +351,7 @@ export class NfsFormComponent extends CdForm implements OnInit {
324351
}),
325352

326353
// CephFS-specific fields
327-
subvolume_group: new UntypedFormControl(''),
354+
subvolume_group: new UntypedFormControl(this.defaultSubVolGroup),
328355
subvolume: new UntypedFormControl(''),
329356
sec_label_xattr: new UntypedFormControl(
330357
'security.selinux',
@@ -481,6 +508,10 @@ export class NfsFormComponent extends CdForm implements OnInit {
481508
this.defaultAccessType[name] = accessType;
482509
}
483510

511+
isolatedSlashCondition(value: string): boolean {
512+
return value === '/';
513+
}
514+
484515
setPathValidation() {
485516
const path = this.nfsForm.get('path');
486517
if (this.storageBackend === SUPPORTED_FSAL.RGW) {
@@ -527,14 +558,6 @@ export class NfsFormComponent extends CdForm implements OnInit {
527558
);
528559
}
529560

530-
pathChangeHandler() {
531-
if (!this.isEdit) {
532-
this.nfsForm.patchValue({
533-
pseudo: this.generatePseudo()
534-
});
535-
}
536-
}
537-
538561
private getBucketTypeahead(path: string): Observable<any> {
539562
if (_.isString(path) && path !== '/' && path !== '') {
540563
return this.rgwBucketService.list().pipe(
@@ -550,24 +573,6 @@ export class NfsFormComponent extends CdForm implements OnInit {
550573
}
551574
}
552575

553-
private generatePseudo() {
554-
const pseudoControl = this.nfsForm.get('pseudo');
555-
let newPseudo = pseudoControl?.dirty && this.nfsForm.getValue('pseudo');
556-
557-
if (!newPseudo) {
558-
const path = this.nfsForm.getValue('path');
559-
newPseudo = `/${getPathfromFsal(this.storageBackend)}`;
560-
561-
if (_.isString(path) && !_.isEmpty(path)) {
562-
newPseudo += '/' + path;
563-
} else if (!_.isEmpty(this.nfsForm.getValue('fsal').user_id)) {
564-
newPseudo += '/' + this.nfsForm.getValue('fsal').user_id;
565-
}
566-
}
567-
568-
return newPseudo;
569-
}
570-
571576
submitAction() {
572577
let action: Observable<any>;
573578
const requestModel = this.buildRequest();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const DEFAULT_SUBVOLUME_GROUP = '_nogroup';

0 commit comments

Comments
 (0)