Skip to content

Commit 3756cc1

Browse files
authored
Merge pull request ceph#58441 from rhcs-dashboard/add-nfs-export-route
mgr/dashboard: add NFS export button for subvolume/ grp Reviewed-by: afreen23 <NOT@FOUND> Reviewed-by: Ankush Behl <[email protected]> Reviewed-by: Nizamudeen A <[email protected]>
2 parents 58b3ae4 + c0b2db7 commit 3756cc1

File tree

8 files changed

+136
-24
lines changed

8 files changed

+136
-24
lines changed

src/pybind/mgr/dashboard/controllers/cephfs.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,8 +871,17 @@ def info(self, vol_name: str, group_name: str):
871871
if error_code != 0:
872872
raise DashboardException(
873873
f'Failed to get info for subvolume group {group_name}: {err}'
874+
874875
)
875-
return json.loads(out)
876+
group = json.loads(out)
877+
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_getpath', None, {
878+
'vol_name': vol_name, 'group_name': group_name})
879+
if error_code != 0:
880+
raise DashboardException(
881+
f'Failed to get path for subvolume group {group_name}: {err}'
882+
)
883+
group['path'] = out
884+
return group
876885

877886
def create(self, vol_name: str, group_name: str, **kwargs):
878887
error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_create', None, {

src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,12 @@ const routes: Routes = [
407407
children: [
408408
{ path: '', component: NfsListComponent },
409409
{
410-
path: URLVerbs.CREATE,
410+
path: `${URLVerbs.CREATE}/:fs_name/:subvolume_group`,
411+
component: NfsFormComponent,
412+
data: { breadcrumbs: ActionLabels.CREATE }
413+
},
414+
{
415+
path: `${URLVerbs.CREATE}`,
411416
component: NfsFormComponent,
412417
data: { breadcrumbs: ActionLabels.CREATE }
413418
},

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import { CriticalConfirmationModalComponent } from '~/app/shared/components/crit
1818
import { FinishedTask } from '~/app/shared/models/finished-task';
1919
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
2020
import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
21+
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
22+
import _ from 'lodash';
2123

2224
@Component({
2325
selector: 'cd-cephfs-subvolume-group',
@@ -54,6 +56,8 @@ export class CephfsSubvolumeGroupComponent implements OnInit, OnChanges {
5456
subvolumeGroup$: Observable<CephfsSubvolumeGroup[]>;
5557
subject = new BehaviorSubject<CephfsSubvolumeGroup[]>([]);
5658

59+
modalRef: NgbModalRef;
60+
5761
constructor(
5862
private cephfsSubvolumeGroup: CephfsSubvolumeGroupService,
5963
private actionLabels: ActionLabelsI18n,
@@ -116,6 +120,13 @@ export class CephfsSubvolumeGroupComponent implements OnInit, OnChanges {
116120
icon: Icons.edit,
117121
click: () => this.openModal(true)
118122
},
123+
{
124+
name: this.actionLabels.NFS_EXPORT,
125+
permission: 'create',
126+
icon: Icons.nfsExport,
127+
routerLink: () => ['/cephfs/nfs/create', this.fsName, this.selection?.first()?.name],
128+
disable: () => !this.selection.hasSingleSelection
129+
},
119130
{
120131
name: this.actionLabels.REMOVE,
121132
permission: 'delete',

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-g
3333
import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
3434
import { CephfsMountDetailsComponent } from '../cephfs-mount-details/cephfs-mount-details.component';
3535
import { HealthService } from '~/app/shared/api/health.service';
36+
import _ from 'lodash';
37+
38+
const DEFAULT_SUBVOLUME_GROUP = '_nogroup';
3639

3740
@Component({
3841
selector: 'cd-cephfs-subvolume-list',
@@ -159,6 +162,18 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
159162
disable: () => !this.selection?.hasSelection,
160163
click: () => this.showAttachInfo()
161164
},
165+
{
166+
name: this.actionLabels.NFS_EXPORT,
167+
permission: 'create',
168+
icon: Icons.nfsExport,
169+
routerLink: () => [
170+
'/cephfs/nfs/create',
171+
this.fsName,
172+
_.isEmpty(this.activeGroupName) ? DEFAULT_SUBVOLUME_GROUP : this.activeGroupName,
173+
{ subvolume: this.selection?.first()?.name }
174+
],
175+
disable: () => !this.selection?.hasSingleSelection
176+
},
162177
{
163178
name: this.actionLabels.REMOVE,
164179
permission: 'delete',

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
</div>
114114

115115
<div class="form-group row"
116-
*ngIf="storageBackend === 'CEPH'">
116+
*ngIf="storageBackend === 'CEPH' && nfsForm.getValue('fsal').fs_name">
117117
<label class="cd-col-form-label"
118118
for="subvolume_group"
119119
i18n>Subvolume Group</label>
@@ -134,12 +134,13 @@
134134
i18n>-- Select the CephFS subvolume group --</option>
135135
<option *ngFor="let subvol_grp of allsubvolgrps"
136136
[value]="subvol_grp.name">{{ subvol_grp.name }}</option>
137+
<option [value]="defaultSubVolGroup">{{ defaultSubVolGroup }}</option>
137138
</select>
138139
</div>
139140
</div>
140141

141142
<div class="form-group row"
142-
*ngIf="storageBackend === 'CEPH'">
143+
*ngIf="storageBackend === 'CEPH' && nfsForm.getValue('fsal').fs_name">
143144
<label class="cd-col-form-label"
144145
for="subvolume"
145146
i18n>Subvolume</label>
@@ -148,7 +149,7 @@
148149
formControlName="subvolume"
149150
name="subvolume"
150151
id="subvolume"
151-
(change)="getPath()">
152+
(change)="setSubVolPath()">
152153
<option *ngIf="allsubvols === null"
153154
value=""
154155
i18n>Loading...</option>

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

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ export class NfsFormComponent extends CdForm implements OnInit {
6767

6868
allsubvolgrps: any[] = [];
6969
allsubvols: any[] = [];
70-
fsPath: string = null;
70+
71+
selectedFsName: string = '';
72+
selectedSubvolGroup: string = '';
73+
selectedSubvol: string = '';
74+
defaultSubVolGroup = '_nogroup';
7175

7276
pathDataSource = (text$: Observable<string>) => {
7377
return text$.pipe(
@@ -132,6 +136,14 @@ export class NfsFormComponent extends CdForm implements OnInit {
132136
this.nfsForm.get('cluster_id').disable();
133137
} else {
134138
this.action = this.actionLabels.CREATE;
139+
this.route.params.subscribe(
140+
(params: { fs_name: string; subvolume_group: string; subvolume?: string }) => {
141+
this.selectedFsName = params.fs_name;
142+
this.selectedSubvolGroup = params.subvolume_group;
143+
if (params.subvolume) this.selectedSubvol = params.subvolume;
144+
}
145+
);
146+
135147
this.getData(promises);
136148
}
137149
}
@@ -153,40 +165,69 @@ export class NfsFormComponent extends CdForm implements OnInit {
153165
this.getSubVolGrp(fs_name);
154166
}
155167

156-
getSubVol() {
157-
this.getPath();
168+
async getSubVol() {
158169
const fs_name = this.nfsForm.getValue('fsal').fs_name;
159170
const subvolgrp = this.nfsForm.getValue('subvolume_group');
160-
return this.subvolService.get(fs_name, subvolgrp).subscribe((data: any) => {
171+
await this.setSubVolGrpPath();
172+
173+
(subvolgrp === this.defaultSubVolGroup
174+
? this.subvolService.get(fs_name)
175+
: this.subvolService.get(fs_name, subvolgrp)
176+
).subscribe((data: any) => {
161177
this.allsubvols = data;
162178
});
163179
}
164180

165181
getSubVolGrp(fs_name: string) {
166-
return this.subvolgrpService.get(fs_name).subscribe((data: any) => {
182+
this.subvolgrpService.get(fs_name).subscribe((data: any) => {
167183
this.allsubvolgrps = data;
168184
});
169185
}
170186

171-
getFsPath(volList: any[], value: string) {
172-
const match = volList.find((vol) => vol.name === value);
173-
if (match) {
174-
return match.info.path;
175-
}
187+
setSubVolGrpPath(): Promise<void> {
188+
return new Promise<void>((resolve, reject) => {
189+
const subvolGroup = this.nfsForm.getValue('subvolume_group');
190+
const fs_name = this.nfsForm.getValue('fsal').fs_name;
191+
192+
if (subvolGroup === this.defaultSubVolGroup) {
193+
this.updatePath('/volumes/' + this.defaultSubVolGroup);
194+
resolve();
195+
} else {
196+
this.subvolgrpService
197+
.info(fs_name, subvolGroup)
198+
.pipe(map((data) => data['path']))
199+
.subscribe(
200+
(path) => {
201+
this.updatePath(path);
202+
resolve();
203+
},
204+
(error) => reject(error)
205+
);
206+
}
207+
});
176208
}
177209

178-
getPath() {
179-
const subvol = this.nfsForm.getValue('subvolume');
180-
if (subvol === '') {
210+
setSubVolPath(): Promise<void> {
211+
return new Promise<void>((resolve, reject) => {
212+
const subvol = this.nfsForm.getValue('subvolume');
181213
const subvolGroup = this.nfsForm.getValue('subvolume_group');
182-
this.fsPath = this.getFsPath(this.allsubvolgrps, subvolGroup);
183-
} else {
184-
this.fsPath = this.getFsPath(this.allsubvols, subvol);
185-
}
186-
this.nfsForm.patchValue({
187-
path: this.fsPath
214+
const fs_name = this.nfsForm.getValue('fsal').fs_name;
215+
216+
this.subvolService
217+
.info(fs_name, subvol, subvolGroup === this.defaultSubVolGroup ? '' : subvolGroup)
218+
.pipe(map((data) => data['path']))
219+
.subscribe(
220+
(path) => {
221+
this.updatePath(path);
222+
resolve();
223+
},
224+
(error) => reject(error)
225+
);
188226
});
227+
}
189228

229+
updatePath(path: string) {
230+
this.nfsForm.patchValue({ path: path });
190231
this.pathChangeHandler();
191232
}
192233

@@ -317,8 +358,34 @@ export class NfsFormComponent extends CdForm implements OnInit {
317358
}
318359
}
319360

361+
resolveRouteParams() {
362+
if (!_.isEmpty(this.selectedFsName)) {
363+
this.nfsForm.patchValue({
364+
fsal: {
365+
fs_name: this.selectedFsName
366+
}
367+
});
368+
this.volumeChangeHandler();
369+
}
370+
if (!_.isEmpty(this.selectedSubvolGroup)) {
371+
this.nfsForm.patchValue({
372+
subvolume_group: this.selectedSubvolGroup
373+
});
374+
this.getSubVol();
375+
}
376+
if (!_.isEmpty(this.selectedSubvol)) {
377+
this.nfsForm.patchValue({
378+
subvolume: this.selectedSubvol
379+
});
380+
this.setSubVolPath();
381+
}
382+
}
383+
320384
resolveFilesystems(filesystems: any[]) {
321385
this.allFsNames = filesystems;
386+
if (!this.isEdit) {
387+
this.resolveRouteParams();
388+
}
322389
}
323390

324391
resolveRealms(realms: string[]) {

src/pybind/mgr/dashboard/frontend/src/app/shared/constants/app.constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ export class ActionLabelsI18n {
150150
AUTHORIZE: string;
151151
EXPAND_CLUSTER: string;
152152
SETUP_MULTISITE_REPLICATION: string;
153+
NFS_EXPORT: string;
153154

154155
constructor() {
155156
/* Create a new item */
@@ -239,6 +240,8 @@ export class ActionLabelsI18n {
239240
this.DISCONNECT = $localize`Disconnect`;
240241
this.RECONNECT = $localize`Reconnect`;
241242
this.EXPAND_CLUSTER = $localize`Expand Cluster`;
243+
244+
this.NFS_EXPORT = $localize`Create NFS Export`;
242245
}
243246
}
244247

src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export enum Icons {
8484
eye = 'fa fa-eye', // Observability
8585
calendar = 'fa fa-calendar',
8686
externalUrl = 'fa fa-external-link', // links to external page
87+
nfsExport = 'fa fa-server', // NFS export
8788

8889
/* Icons for special effect */
8990
large = 'fa fa-lg', // icon becomes 33% larger

0 commit comments

Comments
 (0)