Skip to content

Commit 4b7bfda

Browse files
authored
Merge pull request ceph#54076 from rhcs-dashboard/subvolume-snapshot
mgr/dashboard: cephfs subvolume list snapshots Reviewed-by: Pedro Gonzalez Gomez <[email protected]> Reviewed-by: Ankush Behl <[email protected]> Reviewed-by: ivoalmeida <NOT@FOUND>
2 parents e89bb53 + 4689c3d commit 4b7bfda

25 files changed

+686
-90
lines changed

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

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ def ls_dir(self, fs_id, path=None, depth=1):
676676
@APIDoc('CephFS Subvolume Management API', 'CephFSSubvolume')
677677
class CephFSSubvolume(RESTController):
678678

679-
def get(self, vol_name: str, group_name: str = ""):
679+
def get(self, vol_name: str, group_name: str = "", info=True):
680680
params = {'vol_name': vol_name}
681681
if group_name:
682682
params['group_name'] = group_name
@@ -687,15 +687,17 @@ def get(self, vol_name: str, group_name: str = ""):
687687
f'Failed to list subvolumes for volume {vol_name}: {err}'
688688
)
689689
subvolumes = json.loads(out)
690-
for subvolume in subvolumes:
691-
params['sub_name'] = subvolume['name']
692-
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_info', None,
693-
params)
694-
if error_code != 0:
695-
raise DashboardException(
696-
f'Failed to get info for subvolume {subvolume["name"]}: {err}'
697-
)
698-
subvolume['info'] = json.loads(out)
690+
691+
if info:
692+
for subvolume in subvolumes:
693+
params['sub_name'] = subvolume['name']
694+
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_info', None,
695+
params)
696+
if error_code != 0:
697+
raise DashboardException(
698+
f'Failed to get info for subvolume {subvolume["name"]}: {err}'
699+
)
700+
subvolume['info'] = json.loads(out)
699701
return subvolumes
700702

701703
@RESTController.Resource('GET')
@@ -752,12 +754,27 @@ def delete(self, vol_name: str, subvol_name: str, group_name: str = "",
752754
component='cephfs')
753755
return f'Subvolume {subvol_name} removed successfully'
754756

757+
@RESTController.Resource('GET')
758+
def exists(self, vol_name: str, group_name=''):
759+
params = {'vol_name': vol_name}
760+
if group_name:
761+
params['group_name'] = group_name
762+
error_code, out, err = mgr.remote(
763+
'volumes', '_cmd_fs_subvolume_exist', None, params)
764+
if error_code != 0:
765+
raise DashboardException(
766+
f'Failed to check if subvolume exists: {err}'
767+
)
768+
if out == 'no subvolume exists':
769+
return False
770+
return True
771+
755772

756773
@APIRouter('/cephfs/subvolume/group', Scope.CEPHFS)
757774
@APIDoc("Cephfs Subvolume Group Management API", "CephfsSubvolumeGroup")
758775
class CephFSSubvolumeGroups(RESTController):
759776

760-
def get(self, vol_name):
777+
def get(self, vol_name, info=True):
761778
if not vol_name:
762779
raise DashboardException(
763780
f'Error listing subvolume groups for {vol_name}')
@@ -767,15 +784,17 @@ def get(self, vol_name):
767784
raise DashboardException(
768785
f'Error listing subvolume groups for {vol_name}')
769786
subvolume_groups = json.loads(out)
770-
for group in subvolume_groups:
771-
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_info',
772-
None, {'vol_name': vol_name,
773-
'group_name': group['name']})
774-
if error_code != 0:
775-
raise DashboardException(
776-
f'Failed to get info for subvolume group {group["name"]}: {err}'
777-
)
778-
group['info'] = json.loads(out)
787+
788+
if info:
789+
for group in subvolume_groups:
790+
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_info',
791+
None, {'vol_name': vol_name,
792+
'group_name': group['name']})
793+
if error_code != 0:
794+
raise DashboardException(
795+
f'Failed to get info for subvolume group {group["name"]}: {err}'
796+
)
797+
group['info'] = json.loads(out)
779798
return subvolume_groups
780799

781800
@RESTController.Resource('GET')
@@ -816,3 +835,31 @@ def delete(self, vol_name: str, group_name: str):
816835
f'Failed to delete subvolume group {group_name}: {err}'
817836
)
818837
return f'Subvolume group {group_name} removed successfully'
838+
839+
840+
@APIRouter('/cephfs/subvolume/snapshot', Scope.CEPHFS)
841+
@APIDoc("Cephfs Subvolume Snapshot Management API", "CephfsSubvolumeSnapshot")
842+
class CephFSSubvolumeSnapshots(RESTController):
843+
def get(self, vol_name: str, subvol_name, group_name: str = '', info=True):
844+
params = {'vol_name': vol_name, 'sub_name': subvol_name}
845+
if group_name:
846+
params['group_name'] = group_name
847+
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_snapshot_ls', None,
848+
params)
849+
if error_code != 0:
850+
raise DashboardException(
851+
f'Failed to list subvolume snapshots for subvolume {subvol_name}: {err}'
852+
)
853+
snapshots = json.loads(out)
854+
855+
if info:
856+
for snapshot in snapshots:
857+
params['snap_name'] = snapshot['name']
858+
error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolume_snapshot_info',
859+
None, params)
860+
if error_code != 0:
861+
raise DashboardException(
862+
f'Failed to get info for subvolume snapshot {snapshot["name"]}: {err}'
863+
)
864+
snapshot['info'] = json.loads(out)
865+
return snapshots

src/pybind/mgr/dashboard/frontend/cypress/e2e/common/table-helper.feature.po.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ Then('I should not see a row with {string}', (row: string) => {
5454
);
5555
});
5656

57+
Then('I should see a table in the expanded row', () => {
58+
cy.get('.datatable-row-detail').within(() => {
59+
cy.get('cd-table').should('exist');
60+
cy.get('datatable-scroller, .empty-row');
61+
});
62+
});
63+
5764
Then('I should not see a row with {string} in the expanded row', (row: string) => {
5865
cy.get('.datatable-row-detail').within(() => {
5966
cy.get('cd-table .search input').first().clear().type(row);
@@ -133,3 +140,9 @@ And('I should see row {string} have {string} on this tab', (row: string, options
133140
});
134141
}
135142
});
143+
144+
Then('I should see an alert {string} in the expanded row', (alert: string) => {
145+
cy.get('.datatable-row-detail').within(() => {
146+
cy.get('.alert-panel-text').contains(alert);
147+
});
148+
});
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
Feature: CephFS Snapshot Management
2+
3+
Goal: To test out the CephFS snapshot management features
4+
5+
Background: Login
6+
Given I am logged in
7+
8+
Scenario: Create a CephFS Volume
9+
Given I am on the "cephfs" page
10+
And I click on "Create" button
11+
And enter "name" "test_cephfs"
12+
And I click on "Create File System" button
13+
Then I should see a row with "test_cephfs"
14+
15+
Scenario: Snapshots tab without a subvolume
16+
Given I am on the "cephfs" page
17+
When I expand the row "test_cephfs"
18+
And I go to the "Snapshots" tab
19+
Then I should see an alert "No subvolumes are present" in the expanded row
20+
21+
Scenario: Create a CephFS Subvolume
22+
Given I am on the "cephfs" page
23+
When I expand the row "test_cephfs"
24+
And I go to the "Subvolumes" tab
25+
And I click on "Create" button from the expanded row
26+
And enter "subvolumeName" "test_subvolume" in the modal
27+
And I click on "Create Subvolume" button
28+
Then I should see a row with "test_subvolume" in the expanded row
29+
30+
Scenario: Show the CephFS Snapshots view
31+
Given I am on the "cephfs" page
32+
When I expand the row "test_cephfs"
33+
And I go to the "Snapshots" tab
34+
Then I should see a table in the expanded row
35+
36+
Scenario: Remove a CephFS Subvolume
37+
Given I am on the "cephfs" page
38+
When I expand the row "test_cephfs"
39+
And I go to the "Subvolumes" tab
40+
When I select a row "test_subvolume" in the expanded row
41+
And I click on "Remove" button from the table actions in the expanded row
42+
And I check the tick box in modal
43+
And I click on "Remove Subvolume" button
44+
Then I should not see a row with "test_subvolume" in the expanded row
45+
46+
Scenario: Remove CephFS Volume
47+
Given I am on the "cephfs" page
48+
And I select a row "test_cephfs"
49+
And I click on "Remove" button from the table actions
50+
Then I should see the modal
51+
And I check the tick box in modal
52+
And I click on "Remove File System" button
53+
Then I should not see a row with "test_cephfs"

src/pybind/mgr/dashboard/frontend/cypress/e2e/filesystems/subvolume-groups.e2e-spec.feature

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Feature: CephFS Subvolume Group management
2121
And I click on "Create Subvolume group" button
2222
Then I should see a row with "test_subvolume_group" in the expanded row
2323

24-
Scenario: Edit a CephFS Subvolume
24+
Scenario: Edit a CephFS Subvolume Group
2525
Given I am on the "cephfs" page
2626
When I expand the row "test_cephfs"
2727
And I go to the "Subvolume groups" tab
@@ -31,7 +31,7 @@ Feature: CephFS Subvolume Group management
3131
And I click on "Edit Subvolume group" button
3232
Then I should see row "test_subvolume_group" of the expanded row to have a usage bar
3333

34-
Scenario: Remove a CephFS Subvolume
34+
Scenario: Remove a CephFS Subvolume Group
3535
Given I am on the "cephfs" page
3636
When I expand the row "test_cephfs"
3737
And I go to the "Subvolume groups" tab

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
2-
import { Observable, ReplaySubject, of } from 'rxjs';
1+
import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
2+
import { BehaviorSubject, Observable, of } from 'rxjs';
33
import { catchError, shareReplay, switchMap } from 'rxjs/operators';
44

55
import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
@@ -9,7 +9,6 @@ import { CdTableAction } from '~/app/shared/models/cd-table-action';
99
import { CdTableColumn } from '~/app/shared/models/cd-table-column';
1010
import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
1111
import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
12-
import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolumegroup.model';
1312
import { CephfsSubvolumegroupFormComponent } from '../cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component';
1413
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
1514
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
@@ -18,6 +17,7 @@ import { Permissions } from '~/app/shared/models/permissions';
1817
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
1918
import { FinishedTask } from '~/app/shared/models/finished-task';
2019
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
20+
import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
2121

2222
@Component({
2323
selector: 'cd-cephfs-subvolume-group',
@@ -52,7 +52,7 @@ export class CephfsSubvolumeGroupComponent implements OnInit, OnChanges {
5252
permissions: Permissions;
5353

5454
subvolumeGroup$: Observable<CephfsSubvolumeGroup[]>;
55-
subject = new ReplaySubject<CephfsSubvolumeGroup[]>();
55+
subject = new BehaviorSubject<CephfsSubvolumeGroup[]>([]);
5656

5757
constructor(
5858
private cephfsSubvolumeGroup: CephfsSubvolumeGroupService,
@@ -138,11 +138,13 @@ export class CephfsSubvolumeGroupComponent implements OnInit, OnChanges {
138138
}
139139

140140
fetchData() {
141-
this.subject.next();
141+
this.subject.next([]);
142142
}
143143

144-
ngOnChanges() {
145-
this.subject.next();
144+
ngOnChanges(changes: SimpleChanges) {
145+
if (changes.fsName) {
146+
this.subject.next([]);
147+
}
146148
}
147149

148150
updateSelection(selection: CdTableSelection) {

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

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
11
<div class="row">
2-
<div class="col-sm-1">
3-
<h3 i18n>Groups</h3>
4-
<ng-container *ngIf="subVolumeGroups$ | async as subVolumeGroups">
5-
<ul class="nav flex-column nav-pills">
6-
<li class="nav-item">
7-
<a class="nav-link"
8-
[class.active]="!activeGroupName"
9-
(click)="selectSubVolumeGroup()">Default</a>
10-
</li>
11-
<li class="nav-item"
12-
*ngFor="let subVolumeGroup of subVolumeGroups">
13-
<a class="nav-link text-decoration-none text-break"
14-
[class.active]="subVolumeGroup.name === activeGroupName"
15-
(click)="selectSubVolumeGroup(subVolumeGroup.name)">{{subVolumeGroup.name}}</a>
16-
</li>
17-
</ul>
18-
</ng-container>
2+
<div class="col-sm-1"
3+
*ngIf="subVolumeGroups$ | async as subVolumeGroups">
4+
<cd-vertical-navigation title="Groups"
5+
[items]="subvolumeGroupList"
6+
inputIdentifier="group-filter"
7+
(emitActiveItem)="selectSubVolumeGroup($event)"></cd-vertical-navigation>
198
</div>
209
<div class="col-11 vertical-line">
2110
<cd-table [data]="subVolumes$ | async"

0 commit comments

Comments
 (0)