From 3d5339517420f40b5528009e606fe79281dd683c Mon Sep 17 00:00:00 2001 From: Nizamudeen A Date: Mon, 7 Aug 2023 21:02:55 +0530 Subject: [PATCH] mgr/dashboard: cephfs subvolume remove Fixes: https://tracker.ceph.com/issues/62349 Signed-off-by: Nizamudeen A --- .../mgr/dashboard/controllers/cephfs.py | 32 +++++--- .../cephfs-subvolume-list.component.spec.ts | 4 +- .../cephfs-subvolume-list.component.ts | 39 ++++++++-- .../api/cephfs-subvolume.service.spec.ts | 6 ++ .../shared/api/cephfs-subvolume.service.ts | 9 +++ .../shared/services/task-message.service.ts | 3 + src/pybind/mgr/dashboard/openapi.yaml | 76 ++++++++++++++----- 7 files changed, 130 insertions(+), 39 deletions(-) diff --git a/src/pybind/mgr/dashboard/controllers/cephfs.py b/src/pybind/mgr/dashboard/controllers/cephfs.py index 0c845009ad2..1596b73a12f 100644 --- a/src/pybind/mgr/dashboard/controllers/cephfs.py +++ b/src/pybind/mgr/dashboard/controllers/cephfs.py @@ -711,6 +711,27 @@ class CephFSSubvolume(RESTController): return f'Subvolume {subvol_name} created successfully' + def set(self, vol_name: str, subvol_name: str, size: str): + if size: + error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolume_resize', None, { + 'vol_name': vol_name, 'sub_name': subvol_name, 'new_size': size}) + 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): + error_code, _, err = mgr.remote( + 'volumes', '_cmd_fs_subvolume_rm', None, { + 'vol_name': vol_name, 'sub_name': subvol_name}) + if error_code != 0: + raise RuntimeError( + f'Failed to delete subvolume {subvol_name}: {err}' + ) + return f'Subvolume {subvol_name} removed successfully' + @APIRouter('/cephfs/subvolume/group', Scope.CEPHFS) @APIDoc("Cephfs Subvolume Group Management API", "CephfsSubvolumeGroup") @@ -744,14 +765,3 @@ class CephFSSubvolumeGroups(RESTController): raise DashboardException( f'Failed to create subvolume group {group_name}: {err}' ) - - def set(self, vol_name: str, subvol_name: str, size: str): - if size: - error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolume_resize', None, { - 'vol_name': vol_name, 'sub_name': subvol_name, 'new_size': size}) - if error_code != 0: - raise DashboardException( - f'Failed to update subvolume {subvol_name}: {err}' - ) - - return f'Subvolume {subvol_name} updated successfully' diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.spec.ts index cafbff71768..b3e0b526fb1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.spec.ts @@ -3,6 +3,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CephfsSubvolumeListComponent } from './cephfs-subvolume-list.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { SharedModule } from '~/app/shared/shared.module'; +import { ToastrModule } from 'ngx-toastr'; +import { RouterTestingModule } from '@angular/router/testing'; describe('CephfsSubvolumeListComponent', () => { let component: CephfsSubvolumeListComponent; @@ -11,7 +13,7 @@ describe('CephfsSubvolumeListComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [CephfsSubvolumeListComponent], - imports: [HttpClientTestingModule, SharedModule] + imports: [HttpClientTestingModule, SharedModule, ToastrModule.forRoot(), RouterTestingModule] }).compileComponents(); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts index 47953b34c89..25b5ef14a58 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-list/cephfs-subvolume-list.component.ts @@ -14,6 +14,9 @@ import { ModalService } from '~/app/shared/services/modal.service'; import { CephfsSubvolumeFormComponent } from '../cephfs-subvolume-form/cephfs-subvolume-form.component'; import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; import { Permissions } from '~/app/shared/models/permissions'; +import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; +import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; +import { FinishedTask } from '~/app/shared/models/finished-task'; @Component({ selector: 'cd-cephfs-subvolume-list', @@ -53,7 +56,8 @@ export class CephfsSubvolumeListComponent implements OnInit, OnChanges { private cephfsSubVolume: CephfsSubvolumeService, private actionLabels: ActionLabelsI18n, private modalService: ModalService, - private authStorageService: AuthStorageService + private authStorageService: AuthStorageService, + private taskWrapper: TaskWrapperService ) { this.permissions = this.authStorageService.getPermissions(); } @@ -107,14 +111,21 @@ export class CephfsSubvolumeListComponent implements OnInit, OnChanges { name: this.actionLabels.CREATE, permission: 'create', icon: Icons.add, - click: () => this.openModal(), - canBePrimary: (selection: CdTableSelection) => !selection.hasSelection + click: () => + this.modalService.show( + CephfsSubvolumeFormComponent, + { + fsName: this.fsName, + pools: this.pools + }, + { size: 'lg' } + ) }, { - name: this.actionLabels.EDIT, - permission: 'update', - icon: Icons.edit, - click: () => this.openModal(true) + name: this.actionLabels.REMOVE, + permission: 'delete', + icon: Icons.destroy, + click: () => this.removeSubVolumeModal() } ]; @@ -155,4 +166,18 @@ export class CephfsSubvolumeListComponent implements OnInit, OnChanges { { size: 'lg' } ); } + + removeSubVolumeModal() { + const name = this.selection.first().name; + this.modalService.show(CriticalConfirmationModalComponent, { + itemDescription: 'subvolume', + itemNames: [name], + actionDescription: 'remove', + submitActionObservable: () => + this.taskWrapper.wrapTaskAroundCall({ + task: new FinishedTask('cephfs/subvolume/remove', { subVolumeName: name }), + call: this.cephfsSubVolume.remove(this.fsName, name) + }) + }); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.spec.ts index 0e65aa6c0d7..e2d1db3e8f3 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume.service.spec.ts @@ -32,4 +32,10 @@ describe('CephfsSubvolumeService', () => { const req = httpTesting.expectOne('api/cephfs/subvolume/testFS'); expect(req.request.method).toBe('GET'); }); + + it('should call remove', () => { + service.remove('testFS', 'testSubvol').subscribe(); + const req = httpTesting.expectOne('api/cephfs/subvolume/testFS?subvol_name=testSubvol'); + expect(req.request.method).toBe('DELETE'); + }); }); 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 ca7bf095891..67c7bb346a2 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 @@ -51,6 +51,15 @@ export class CephfsSubvolumeService { }); } + remove(fsName: string, subVolumeName: string) { + return this.http.delete(`${this.baseURL}/${fsName}`, { + params: { + subvol_name: subVolumeName + }, + observe: 'response' + }); + } + exists(subVolumeName: string, fsName: string) { return this.info(fsName, subVolumeName).pipe( mapTo(true), 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 994adf2dcd8..766b31dbcbb 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 @@ -365,6 +365,9 @@ export class TaskMessageService { 'cephfs/subvolume/edit': this.newTaskMessage(this.commonOperations.update, (metadata) => this.subvolume(metadata) ), + 'cephfs/subvolume/remove': this.newTaskMessage(this.commonOperations.remove, (metadata) => + this.subvolume(metadata) + ), 'cephfs/subvolume/group/create': this.newTaskMessage(this.commonOperations.create, (metadata) => this.subvolumegroup(metadata) ) diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 86743ab1c4c..233a3c09bb0 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -1865,37 +1865,30 @@ paths: - jwt: [] tags: - CephfsSubvolumeGroup - put: + /api/cephfs/subvolume/{vol_name}: + delete: parameters: - in: path name: vol_name required: true schema: type: string - requestBody: - content: - application/json: - schema: - properties: - size: - type: integer - subvol_name: - type: string - required: - - subvol_name - - size - type: object + - in: query + name: subvol_name + required: true + schema: + type: string responses: - '200': + '202': content: application/vnd.ceph.api.v1.0+json: type: object - description: Resource updated. - '202': + description: Operation is still executing. Please check the task queue. + '204': content: application/vnd.ceph.api.v1.0+json: type: object - description: Operation is still executing. Please check the task queue. + description: Resource deleted. '400': description: Operation exception. Please check the response body for details. '401': @@ -1908,8 +1901,7 @@ paths: security: - jwt: [] tags: - - CephfsSubvolumeGroup - /api/cephfs/subvolume/{vol_name}: + - CephFSSubvolume get: parameters: - in: path @@ -1936,6 +1928,50 @@ paths: - jwt: [] tags: - CephFSSubvolume + put: + parameters: + - in: path + name: vol_name + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + properties: + size: + type: integer + subvol_name: + type: string + required: + - subvol_name + - size + type: object + responses: + '200': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Resource updated. + '202': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Operation is still executing. Please check the task queue. + '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: + - CephFSSubvolume /api/cephfs/subvolume/{vol_name}/info: get: parameters: -- 2.39.5