From 1f3ccd1892dd0cc9605a15c251a33d447bcbf1ff Mon Sep 17 00:00:00 2001 From: Nizamudeen A Date: Wed, 10 Jan 2024 12:12:23 +0530 Subject: [PATCH] mgr/dashboard: delete cephfs snapshot Fixes: https://tracker.ceph.com/issues/63990 Signed-off-by: Nizamudeen A (cherry picked from commit c3d7f70b7a7d594050e3e231cd7d6544179fdfeb) --- .../mgr/dashboard/controllers/cephfs.py | 13 +++++ ...subvolume-snapshots-list.component.spec.ts | 3 +- ...phfs-subvolume-snapshots-list.component.ts | 46 ++++++++++++++++- .../shared/api/cephfs-subvolume.service.ts | 10 ++++ .../shared/services/task-message.service.ts | 4 ++ src/pybind/mgr/dashboard/openapi.yaml | 51 +++++++++++++++++++ 6 files changed, 125 insertions(+), 2 deletions(-) diff --git a/src/pybind/mgr/dashboard/controllers/cephfs.py b/src/pybind/mgr/dashboard/controllers/cephfs.py index 389d30ccbfe9a..cfa40b2dd7c0b 100644 --- a/src/pybind/mgr/dashboard/controllers/cephfs.py +++ b/src/pybind/mgr/dashboard/controllers/cephfs.py @@ -837,3 +837,16 @@ class CephFSSubvolumeSnapshots(RESTController): f'Failed to create subvolume snapshot {snap_name}: {err}' ) return f'Subvolume snapshot {snap_name} created successfully' + + def delete(self, vol_name: str, subvol_name: str, snap_name: str, group_name='', force=True): + params = {'vol_name': vol_name, 'sub_name': subvol_name, 'snap_name': snap_name} + if group_name: + params['group_name'] = group_name + params['force'] = str_to_bool(force) + error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolume_snapshot_rm', None, + params) + if error_code != 0: + raise DashboardException( + f'Failed to delete subvolume snapshot {snap_name}: {err}' + ) + return f'Subvolume snapshot {snap_name} removed successfully' diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-list.component.spec.ts index 1d03cf2a8bcaa..c69f916c2c16a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-list.component.spec.ts @@ -3,6 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CephfsSubvolumeSnapshotsListComponent } from './cephfs-subvolume-snapshots-list.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { SharedModule } from '~/app/shared/shared.module'; +import { ToastrModule } from 'ngx-toastr'; describe('CephfsSubvolumeSnapshotsListComponent', () => { let component: CephfsSubvolumeSnapshotsListComponent; @@ -11,7 +12,7 @@ describe('CephfsSubvolumeSnapshotsListComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [CephfsSubvolumeSnapshotsListComponent], - imports: [HttpClientTestingModule, SharedModule] + imports: [HttpClientTestingModule, SharedModule, ToastrModule.forRoot()] }).compileComponents(); fixture = TestBed.createComponent(CephfsSubvolumeSnapshotsListComponent); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-list.component.ts index 9970d59888798..798307a0cf9f4 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-snapshots-list/cephfs-subvolume-snapshots-list.component.ts @@ -16,6 +16,10 @@ import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; import { Permissions } from '~/app/shared/models/permissions'; import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; +import { FinishedTask } from '~/app/shared/models/finished-task'; +import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; @Component({ selector: 'cd-cephfs-subvolume-snapshots-list', @@ -30,6 +34,7 @@ export class CephfsSubvolumeSnapshotsListComponent implements OnInit, OnChanges tableActions: CdTableAction[]; selection = new CdTableSelection(); permissions: Permissions; + modalRef: NgbModalRef; subVolumes$: Observable; snapshots$: Observable; @@ -53,7 +58,8 @@ export class CephfsSubvolumeSnapshotsListComponent implements OnInit, OnChanges private actionLabels: ActionLabelsI18n, private modalService: ModalService, private authStorageService: AuthStorageService, - private cdDatePipe: CdDatePipe + private cdDatePipe: CdDatePipe, + private taskWrapper: TaskWrapperService ) { this.permissions = this.authStorageService.getPermissions(); } @@ -91,6 +97,12 @@ export class CephfsSubvolumeSnapshotsListComponent implements OnInit, OnChanges permission: 'create', icon: Icons.add, click: () => this.openModal() + }, + { + name: this.actionLabels.REMOVE, + permission: 'delete', + icon: Icons.destroy, + click: () => this.deleteSnapshot() } ]; @@ -190,4 +202,36 @@ export class CephfsSubvolumeSnapshotsListComponent implements OnInit, OnChanges updateSelection(selection: CdTableSelection) { this.selection = selection; } + + deleteSnapshot() { + const snapshotName = this.selection.first().name; + const subVolumeName = this.activeSubVolumeName; + const subVolumeGroupName = this.activeGroupName; + const fsName = this.fsName; + this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, { + actionDescription: 'Remove', + itemNames: [snapshotName], + itemDescription: 'Snapshot', + submitAction: () => + this.taskWrapper + .wrapTaskAroundCall({ + task: new FinishedTask('cephfs/subvolume/snapshot/delete', { + fsName: fsName, + subVolumeName: subVolumeName, + subVolumeGroupName: subVolumeGroupName, + snapshotName: snapshotName + }), + call: this.cephfsSubvolumeService.deleteSnapshot( + fsName, + subVolumeName, + snapshotName, + subVolumeGroupName + ) + }) + .subscribe({ + complete: () => this.modalRef.close(), + error: () => this.modalRef.componentInstance.stopLoadingSpinner() + }) + }); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.ts index 1995fd293bae0..ad0ce248064db 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.ts @@ -161,4 +161,14 @@ export class CephfsSubvolumeService { { observe: 'response' } ); } + + deleteSnapshot(fsName: string, subVolumeName: string, snapshotName: string, groupName = '') { + return this.http.delete(`${this.baseURL}/snapshot/${fsName}/${subVolumeName}`, { + params: { + snap_name: snapshotName, + group_name: groupName + }, + observe: 'response' + }); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts index c1165d318a364..dba742fbf7831 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts @@ -383,6 +383,10 @@ export class TaskMessageService { 'cephfs/subvolume/snapshot/create': this.newTaskMessage( this.commonOperations.create, (metadata) => this.snapshot(metadata) + ), + 'cephfs/subvolume/snapshot/delete': this.newTaskMessage( + this.commonOperations.delete, + (metadata) => this.snapshot(metadata) ) }; diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 2be67eb877c0e..45af71681ef46 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -2006,6 +2006,57 @@ paths: tags: - CephfsSubvolumeSnapshot /api/cephfs/subvolume/snapshot/{vol_name}/{subvol_name}: + delete: + parameters: + - in: path + name: vol_name + required: true + schema: + type: string + - in: path + name: subvol_name + required: true + schema: + type: string + - in: query + name: snap_name + required: true + schema: + type: string + - default: '' + in: query + name: group_name + schema: + type: string + - default: true + in: query + name: force + schema: + type: boolean + responses: + '202': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Operation is still executing. Please check the task queue. + '204': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Resource deleted. + '400': + description: Operation exception. Please check the response body for details. + '401': + description: Unauthenticated access. Please login first. + '403': + description: Unauthorized access. Please check your permissions. + '500': + description: Unexpected error. Please check the response body for the stack + trace. + security: + - jwt: [] + tags: + - CephfsSubvolumeSnapshot get: parameters: - in: path -- 2.39.5