Skip to content

Commit

Permalink
Merge pull request ceph#53246 from rhcs-dashboard/subvolumes-in-subvo…
Browse files Browse the repository at this point in the history
…lumegroups

mgr/dashboard: display the groups in cephfs subvolume tab

Reviewed-by: Aashish Sharma <[email protected]>
Reviewed-by: Nizamudeen A <[email protected]>
  • Loading branch information
nizamial09 authored Sep 11, 2023
2 parents 46ff2b5 + 041bc0c commit 63871e3
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 55 deletions.
36 changes: 25 additions & 11 deletions src/pybind/mgr/dashboard/controllers/cephfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,17 +673,21 @@ def ls_dir(self, fs_id, path=None, depth=1):
@APIDoc('CephFS Subvolume Management API', 'CephFSSubvolume')
class CephFSSubvolume(RESTController):

def get(self, vol_name: str):
def get(self, vol_name: str, group_name: str = ""):
params = {'vol_name': vol_name}
if group_name:
params['group_name'] = group_name
error_code, out, err = mgr.remote(
'volumes', '_cmd_fs_subvolume_ls', None, {'vol_name': vol_name})
'volumes', '_cmd_fs_subvolume_ls', None, params)
if error_code != 0:
raise DashboardException(
f'Failed to list subvolumes for volume {vol_name}: {err}'
)
subvolumes = json.loads(out)
for subvolume in subvolumes:
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_info', None, {
'vol_name': vol_name, 'sub_name': subvolume['name']})
params['sub_name'] = subvolume['name']
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_info', None,
params)
if error_code != 0:
raise DashboardException(
f'Failed to get info for subvolume {subvolume["name"]}: {err}'
Expand All @@ -692,9 +696,12 @@ def get(self, vol_name: str):
return subvolumes

@RESTController.Resource('GET')
def info(self, vol_name: str, subvol_name: str):
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_info', None, {
'vol_name': vol_name, 'sub_name': subvol_name})
def info(self, vol_name: str, subvol_name: str, group_name: str = ""):
params = {'vol_name': vol_name, 'sub_name': subvol_name}
if group_name:
params['group_name'] = group_name
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_info', None,
params)
if error_code != 0:
raise DashboardException(
f'Failed to get info for subvolume {subvol_name}: {err}'
Expand All @@ -711,19 +718,26 @@ def create(self, vol_name: str, subvol_name: str, **kwargs):

return f'Subvolume {subvol_name} created successfully'

def set(self, vol_name: str, subvol_name: str, size: str):
def set(self, vol_name: str, subvol_name: str, size: str, group_name: str = ""):
params = {'vol_name': vol_name, 'sub_name': subvol_name}
if size:
error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolume_resize', None, {
'vol_name': vol_name, 'sub_name': subvol_name, 'new_size': size})
params['new_size'] = size
if group_name:
params['group_name'] = group_name
error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolume_resize', None,
params)
if error_code != 0:
raise DashboardException(
f'Failed to update subvolume {subvol_name}: {err}'
)

return f'Subvolume {subvol_name} updated successfully'

def delete(self, vol_name: str, subvol_name: str, retain_snapshots: bool = False):
def delete(self, vol_name: str, subvol_name: str, group_name: str = "",
retain_snapshots: bool = False):
params = {'vol_name': vol_name, 'sub_name': subvol_name}
if group_name:
params['group_name'] = group_name
retain_snapshots = str_to_bool(retain_snapshots)
if retain_snapshots:
params['retain_snapshots'] = 'True'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@
</div>
</div>

<!--Subvolume Group name -->
<div class="form-group row">
<label class="cd-col-form-label"
for="subvolumeGroupName"
i18n>Subvolume group
</label>
<div class="cd-col-form-input">
<select class="form-select"
id="subvolumeGroupName"
name="subvolumeGroupName"
formControlName="subvolumeGroupName"
*ngIf="subVolumeGroups$ | async as subvolumeGroups">
<option value=""
i18n>Default</option>
<option *ngFor="let subvolumegroup of subvolumeGroups"
[value]="subvolumegroup.name">{{ subvolumegroup.name }}</option>
</select>
</div>
</div>

<!-- Size -->
<div class="form-group row">
<label class="cd-col-form-label"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
import { Pool } from '../../pool/pool';
import { FormatterService } from '~/app/shared/services/formatter.service';
import { CdTableColumn } from '~/app/shared/models/cd-table-column';
import _ from 'lodash';
import { CdValidators } from '~/app/shared/forms/cd-validators';
import { CephfsSubvolumeInfo } from '~/app/shared/models/cephfs-subvolume.model';
import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
import { OctalToHumanReadablePipe } from '~/app/shared/pipes/octal-to-human-readable.pipe';
import { CdForm } from '~/app/shared/forms/cd-form';
import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
import { Observable } from 'rxjs';

@Component({
selector: 'cd-cephfs-subvolume-form',
Expand All @@ -24,6 +26,7 @@ import { CdForm } from '~/app/shared/forms/cd-form';
export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
fsName: string;
subVolumeName: string;
subVolumeGroupName: string;
pools: Pool[];
isEdit = false;

Expand All @@ -32,6 +35,8 @@ export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
action: string;
resource: string;

subVolumeGroups$: Observable<CephfsSubvolumeGroup[]>;
subVolumeGroups: CephfsSubvolumeGroup[];
dataPools: Pool[];

columns: CdTableColumn[];
Expand All @@ -48,6 +53,7 @@ export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
private actionLabels: ActionLabelsI18n,
private taskWrapper: TaskWrapperService,
private cephFsSubvolumeService: CephfsSubvolumeService,
private cephFsSubvolumeGroupService: CephfsSubvolumeGroupService,
private formatter: FormatterService,
private dimlessBinary: DimlessBinaryPipe,
private octalToHumanReadable: OctalToHumanReadablePipe
Expand Down Expand Up @@ -84,6 +90,7 @@ export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
}
];

this.subVolumeGroups$ = this.cephFsSubvolumeGroupService.get(this.fsName);
this.dataPools = this.pools.filter((pool) => pool.type === 'data');
this.createForm();

Expand All @@ -105,6 +112,7 @@ export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
)
]
}),
subvolumeGroupName: new FormControl(this.subVolumeGroupName),
pool: new FormControl(this.dataPools[0]?.pool, {
validators: [Validators.required]
}),
Expand All @@ -121,16 +129,18 @@ export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
populateForm() {
this.action = this.actionLabels.EDIT;
this.cephFsSubvolumeService
.info(this.fsName, this.subVolumeName)
.info(this.fsName, this.subVolumeName, this.subVolumeGroupName)
.subscribe((resp: CephfsSubvolumeInfo) => {
// Disabled these fields since its not editable
this.subvolumeForm.get('subvolumeName').disable();
this.subvolumeForm.get('subvolumeGroupName').disable();
this.subvolumeForm.get('pool').disable();
this.subvolumeForm.get('uid').disable();
this.subvolumeForm.get('gid').disable();

this.subvolumeForm.get('isolatedNamespace').disable();
this.subvolumeForm.get('subvolumeName').setValue(this.subVolumeName);
this.subvolumeForm.get('subvolumeGroupName').setValue(this.subVolumeGroupName);
if (resp.bytes_quota !== 'infinite') {
this.subvolumeForm.get('size').setValue(this.dimlessBinary.transform(resp.bytes_quota));
}
Expand All @@ -145,6 +155,7 @@ export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {

submit() {
const subVolumeName = this.subvolumeForm.getValue('subvolumeName');
const subVolumeGroupName = this.subvolumeForm.getValue('subvolumeGroupName');
const pool = this.subvolumeForm.getValue('pool');
const size = this.formatter.toBytes(this.subvolumeForm.getValue('size')) || 0;
const uid = this.subvolumeForm.getValue('uid');
Expand All @@ -159,7 +170,12 @@ export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
task: new FinishedTask('cephfs/subvolume/' + URLVerbs.EDIT, {
subVolumeName: subVolumeName
}),
call: this.cephFsSubvolumeService.update(this.fsName, subVolumeName, String(editSize))
call: this.cephFsSubvolumeService.update(
this.fsName,
subVolumeName,
String(editSize),
subVolumeGroupName
)
})
.subscribe({
error: () => {
Expand All @@ -178,6 +194,7 @@ export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
call: this.cephFsSubvolumeService.create(
this.fsName,
subVolumeName,
subVolumeGroupName,
pool,
String(size),
uid,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,42 @@
<ng-container *ngIf="subVolumes$ | async as subVolumes">
<cd-table [data]="subVolumes"
columnMode="flex"
[columns]="columns"
selectionType="single"
[hasDetails]="false"
(fetchData)="fetchData()"
(updateSelection)="updateSelection($event)">
<div class="row">
<div class="col-sm-1">
<h3 i18n>Groups</h3>
<ng-container *ngIf="subVolumeGroups$ | async as subVolumeGroups">
<ul class="nav flex-column nav-pills">
<li class="nav-item">
<a class="nav-link"
[class.active]="!activeGroupName"
(click)="selectSubVolumeGroup()">Default</a>
</li>
<li class="nav-item"
*ngFor="let subVolumeGroup of subVolumeGroups">
<a class="nav-link text-decoration-none text-break"
[class.active]="subVolumeGroup.name === activeGroupName"
(click)="selectSubVolumeGroup(subVolumeGroup.name)">{{subVolumeGroup.name}}</a>
</li>
</ul>
</ng-container>
</div>
<div class="col-11 vertical-line">
<cd-table [data]="subVolumes$ | async"
columnMode="flex"
[columns]="columns"
selectionType="single"
[hasDetails]="false"
(fetchData)="fetchData()"
(updateSelection)="updateSelection($event)">

<div class="table-actions btn-toolbar">
<cd-table-actions [permission]="permissions.cephfs"
[selection]="selection"
class="btn-group"
id="cephfs-subvolume-actions"
[tableActions]="tableActions">
</cd-table-actions>
</div>
</cd-table>
</ng-container>
<div class="table-actions btn-toolbar">
<cd-table-actions [permission]="permissions.cephfs"
[selection]="selection"
class="btn-group"
id="cephfs-subvolume-actions"
[tableActions]="tableActions">
</cd-table-actions>
</div>
</cd-table>
</div>
</div>

<ng-template #quotaUsageTpl
let-row="row">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { FormControl } from '@angular/forms';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { CdForm } from '~/app/shared/forms/cd-form';
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolumegroup.model';

@Component({
selector: 'cd-cephfs-subvolume-list',
Expand Down Expand Up @@ -61,14 +63,19 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
selectedName: string = '';

subVolumes$: Observable<CephfsSubvolume[]>;
subVolumeGroups$: Observable<CephfsSubvolumeGroup[]>;
subject = new ReplaySubject<CephfsSubvolume[]>();
groupsSubject = new ReplaySubject<CephfsSubvolume[]>();

activeGroupName: string = '';

constructor(
private cephfsSubVolume: CephfsSubvolumeService,
private actionLabels: ActionLabelsI18n,
private modalService: ModalService,
private authStorageService: AuthStorageService,
private taskWrapper: TaskWrapperService
private taskWrapper: TaskWrapperService,
private cephfsSubvolumeGroupService: CephfsSubvolumeGroupService
) {
super();
this.permissions = this.authStorageService.getPermissions();
Expand Down Expand Up @@ -123,15 +130,7 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
name: this.actionLabels.CREATE,
permission: 'create',
icon: Icons.add,
click: () =>
this.modalService.show(
CephfsSubvolumeFormComponent,
{
fsName: this.fsName,
pools: this.pools
},
{ size: 'lg' }
)
click: () => this.openModal()
},
{
name: this.actionLabels.EDIT,
Expand All @@ -147,16 +146,17 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
}
];

this.subVolumes$ = this.subject.pipe(
this.getSubVolumes();

this.subVolumeGroups$ = this.groupsSubject.pipe(
switchMap(() =>
this.cephfsSubVolume.get(this.fsName).pipe(
this.cephfsSubvolumeGroupService.get(this.fsName).pipe(
catchError(() => {
this.context.error();
return of(null);
})
)
),
shareReplay(1)
)
);
}

Expand All @@ -166,6 +166,7 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh

ngOnChanges() {
this.subject.next();
this.groupsSubject.next();
}

updateSelection(selection: CdTableSelection) {
Expand All @@ -178,6 +179,7 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
{
fsName: this.fsName,
subVolumeName: this.selection?.first()?.name,
subVolumeGroupName: this.activeGroupName,
pools: this.pools,
isEdit: edit
},
Expand All @@ -204,6 +206,7 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
call: this.cephfsSubVolume.remove(
this.fsName,
this.selectedName,
this.activeGroupName,
this.removeForm.getValue('retainSnapshots')
)
})
Expand All @@ -216,4 +219,23 @@ export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnCh
})
});
}

selectSubVolumeGroup(subVolumeGroupName: string) {
this.activeGroupName = subVolumeGroupName;
this.getSubVolumes(subVolumeGroupName);
}

getSubVolumes(subVolumeGroupName = '') {
this.subVolumes$ = this.subject.pipe(
switchMap(() =>
this.cephfsSubVolume.get(this.fsName, subVolumeGroupName).pipe(
catchError(() => {
this.context.error();
return of(null);
})
)
),
shareReplay(1)
);
}
}
Loading

0 comments on commit 63871e3

Please sign in to comment.