Skip to content

Commit 9051031

Browse files
authored
Merge pull request ceph#53182 from rhcs-dashboard/fix-subvolume-rm-snapshots
mgr/dashboard: subvolume rm with snapshots Reviewed-by: Aashish Sharma <[email protected]> Reviewed-by: Nizamudeen A <[email protected]>
2 parents a8ad05f + 453fbcb commit 9051031

File tree

12 files changed

+100
-28
lines changed

12 files changed

+100
-28
lines changed

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ..services.ceph_service import CephService
1515
from ..services.cephfs import CephFS as CephFS_
1616
from ..services.exception import handle_cephfs_error
17-
from ..tools import ViewCache
17+
from ..tools import ViewCache, str_to_bool
1818
from . import APIDoc, APIRouter, DeletePermission, Endpoint, EndpointDoc, \
1919
RESTController, UIRouter, UpdatePermission, allow_empty_body
2020

@@ -722,14 +722,17 @@ def set(self, vol_name: str, subvol_name: str, size: str):
722722

723723
return f'Subvolume {subvol_name} updated successfully'
724724

725-
def delete(self, vol_name: str, subvol_name: str):
725+
def delete(self, vol_name: str, subvol_name: str, retain_snapshots: bool = False):
726+
params = {'vol_name': vol_name, 'sub_name': subvol_name}
727+
retain_snapshots = str_to_bool(retain_snapshots)
728+
if retain_snapshots:
729+
params['retain_snapshots'] = 'True'
726730
error_code, _, err = mgr.remote(
727-
'volumes', '_cmd_fs_subvolume_rm', None, {
728-
'vol_name': vol_name, 'sub_name': subvol_name})
731+
'volumes', '_cmd_fs_subvolume_rm', None, params)
729732
if error_code != 0:
730-
raise RuntimeError(
731-
f'Failed to delete subvolume {subvol_name}: {err}'
732-
)
733+
raise DashboardException(
734+
msg=f'Failed to remove subvolume {subvol_name}: {err}',
735+
component='cephfs')
733736
return f'Subvolume {subvol_name} removed successfully'
734737

735738

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ And('I click on submit button', () => {
5757
* by ticking the 'Are you sure?' box.
5858
*/
5959
Then('I check the tick box in modal', () => {
60-
cy.get('cd-modal .custom-control-label').click();
60+
cy.get('cd-modal input#confirmation').click();
6161
});
6262

6363
And('I confirm to {string}', (action: string) => {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Feature: CephFS Subvolume management
3838
When I select a row "test_subvolume" in the expanded row
3939
And I click on "Remove" button from the table actions in the expanded row
4040
And I check the tick box in modal
41-
And I click on "Remove subvolume" button
41+
And I click on "Remove Subvolume" button
4242
Then I should not see a row with "test_subvolume" in the expanded row
4343

4444
Scenario: Remove CephFS Volume

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,30 @@
7474
*ngIf="row.info.pool_namespace"
7575
[tooltipText]="row.info.pool_namespace"></cd-label>
7676
</ng-template>
77+
78+
<ng-template #removeTmpl
79+
let-form="form">
80+
<ng-container [formGroup]="form">
81+
<ng-container formGroupName="child">
82+
<cd-alert-panel *ngIf="errorMessage.length > 1"
83+
type="error">
84+
{{errorMessage}}
85+
</cd-alert-panel>
86+
<div class="form-group">
87+
<div class="custom-control custom-checkbox">
88+
<input type="checkbox"
89+
class="custom-control-input"
90+
name="retainSnapshots"
91+
id="retainSnapshots"
92+
formControlName="retainSnapshots">
93+
<label class="custom-control-label"
94+
for="retainSnapshots"
95+
i18n>Retain snapshots <cd-helper>The subvolume can be removed retaining
96+
existing snapshots using this option.
97+
If snapshots are retained, the subvolume is considered empty for all
98+
operations not involving the retained snapshots.</cd-helper></label>
99+
</div>
100+
</div>
101+
</ng-container>
102+
</ng-container>
103+
</ng-template>

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
55
import { SharedModule } from '~/app/shared/shared.module';
66
import { ToastrModule } from 'ngx-toastr';
77
import { RouterTestingModule } from '@angular/router/testing';
8+
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
89

910
describe('CephfsSubvolumeListComponent', () => {
1011
let component: CephfsSubvolumeListComponent;
@@ -13,7 +14,8 @@ describe('CephfsSubvolumeListComponent', () => {
1314
beforeEach(async () => {
1415
await TestBed.configureTestingModule({
1516
declarations: [CephfsSubvolumeListComponent],
16-
imports: [HttpClientTestingModule, SharedModule, ToastrModule.forRoot(), RouterTestingModule]
17+
imports: [HttpClientTestingModule, SharedModule, ToastrModule.forRoot(), RouterTestingModule],
18+
providers: [NgbActiveModal]
1719
}).compileComponents();
1820
});
1921

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

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
1+
import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
22
import { Observable, ReplaySubject, of } from 'rxjs';
33
import { catchError, shareReplay, switchMap } from 'rxjs/operators';
44
import { CephfsSubvolumeService } from '~/app/shared/api/cephfs-subvolume.service';
@@ -14,16 +14,20 @@ import { ModalService } from '~/app/shared/services/modal.service';
1414
import { CephfsSubvolumeFormComponent } from '../cephfs-subvolume-form/cephfs-subvolume-form.component';
1515
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
1616
import { Permissions } from '~/app/shared/models/permissions';
17-
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
1817
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
1918
import { FinishedTask } from '~/app/shared/models/finished-task';
19+
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
20+
import { FormControl } from '@angular/forms';
21+
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
22+
import { CdForm } from '~/app/shared/forms/cd-form';
23+
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
2024

2125
@Component({
2226
selector: 'cd-cephfs-subvolume-list',
2327
templateUrl: './cephfs-subvolume-list.component.html',
2428
styleUrls: ['./cephfs-subvolume-list.component.scss']
2529
})
26-
export class CephfsSubvolumeListComponent implements OnInit, OnChanges {
30+
export class CephfsSubvolumeListComponent extends CdForm implements OnInit, OnChanges {
2731
@ViewChild('quotaUsageTpl', { static: true })
2832
quotaUsageTpl: any;
2933

@@ -39,15 +43,22 @@ export class CephfsSubvolumeListComponent implements OnInit, OnChanges {
3943
@ViewChild('quotaSizeTpl', { static: true })
4044
quotaSizeTpl: any;
4145

46+
@ViewChild('removeTmpl', { static: true })
47+
removeTmpl: TemplateRef<any>;
48+
4249
@Input() fsName: string;
4350
@Input() pools: any[];
4451

4552
columns: CdTableColumn[] = [];
4653
tableActions: CdTableAction[];
4754
context: CdTableFetchDataContext;
4855
selection = new CdTableSelection();
56+
removeForm: CdFormGroup;
4957
icons = Icons;
5058
permissions: Permissions;
59+
modalRef: NgbModalRef;
60+
errorMessage: string = '';
61+
selectedName: string = '';
5162

5263
subVolumes$: Observable<CephfsSubvolume[]>;
5364
subject = new ReplaySubject<CephfsSubvolume[]>();
@@ -59,6 +70,7 @@ export class CephfsSubvolumeListComponent implements OnInit, OnChanges {
5970
private authStorageService: AuthStorageService,
6071
private taskWrapper: TaskWrapperService
6172
) {
73+
super();
6274
this.permissions = this.authStorageService.getPermissions();
6375
}
6476

@@ -174,16 +186,34 @@ export class CephfsSubvolumeListComponent implements OnInit, OnChanges {
174186
}
175187

176188
removeSubVolumeModal() {
177-
const name = this.selection.first().name;
178-
this.modalService.show(CriticalConfirmationModalComponent, {
179-
itemDescription: 'subvolume',
180-
itemNames: [name],
181-
actionDescription: 'remove',
182-
submitActionObservable: () =>
183-
this.taskWrapper.wrapTaskAroundCall({
184-
task: new FinishedTask('cephfs/subvolume/remove', { subVolumeName: name }),
185-
call: this.cephfsSubVolume.remove(this.fsName, name)
186-
})
189+
this.removeForm = new CdFormGroup({
190+
retainSnapshots: new FormControl(false)
191+
});
192+
this.errorMessage = '';
193+
this.selectedName = this.selection.first().name;
194+
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
195+
actionDescription: 'Remove',
196+
itemNames: [this.selectedName],
197+
itemDescription: 'Subvolume',
198+
childFormGroup: this.removeForm,
199+
childFormGroupTemplate: this.removeTmpl,
200+
submitAction: () =>
201+
this.taskWrapper
202+
.wrapTaskAroundCall({
203+
task: new FinishedTask('cephfs/subvolume/remove', { subVolumeName: this.selectedName }),
204+
call: this.cephfsSubVolume.remove(
205+
this.fsName,
206+
this.selectedName,
207+
this.removeForm.getValue('retainSnapshots')
208+
)
209+
})
210+
.subscribe({
211+
complete: () => this.modalRef.close(),
212+
error: (error) => {
213+
this.modalRef.componentInstance.stopLoadingSpinner();
214+
this.errorMessage = error.error.detail;
215+
}
216+
})
187217
});
188218
}
189219
}

src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ describe('CephfsSubvolumeService', () => {
3535

3636
it('should call remove', () => {
3737
service.remove('testFS', 'testSubvol').subscribe();
38-
const req = httpTesting.expectOne('api/cephfs/subvolume/testFS?subvol_name=testSubvol');
38+
const req = httpTesting.expectOne(
39+
'api/cephfs/subvolume/testFS?subvol_name=testSubvol&retain_snapshots=false'
40+
);
3941
expect(req.request.method).toBe('DELETE');
4042
});
4143
});

src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ export class CephfsSubvolumeService {
5151
});
5252
}
5353

54-
remove(fsName: string, subVolumeName: string) {
54+
remove(fsName: string, subVolumeName: string, retainSnapshots: boolean = false) {
5555
return this.http.delete(`${this.baseURL}/${fsName}`, {
5656
params: {
57-
subvol_name: subVolumeName
57+
subvol_name: subVolumeName,
58+
retain_snapshots: retainSnapshots
5859
},
5960
observe: 'response'
6061
});

src/pybind/mgr/dashboard/frontend/src/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
</div>
4444
<div class="modal-footer">
4545
<cd-form-button-panel (submitActionEvent)="callSubmitAction()"
46-
(backActionEvent)="callBackAction()"
46+
(backActionEvent)="backAction ? callBackAction() : hideModal()"
4747
[form]="deletionForm"
4848
[submitText]="(actionDescription | titlecase) + ' ' + itemDescription"></cd-form-button-panel>
4949
</div>

src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,8 @@
347347
<span data-toggle="tooltip"
348348
[title]="value"
349349
class="font-monospace">{{ value | path }}
350-
<cd-copy-2-clipboard-button [source]="value"
350+
<cd-copy-2-clipboard-button *ngIf="value"
351+
[source]="value"
351352
[byId]="false"
352353
[showIconOnly]="true">
353354
</cd-copy-2-clipboard-button>

0 commit comments

Comments
 (0)