From: Pedro Gonzalez Gomez Date: Wed, 9 Aug 2023 08:43:54 +0000 (+0200) Subject: mgr/dashboard: edit cephfs subvolumegroup X-Git-Tag: v18.2.1~378^2~1 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=90255bc7c956489566859db058c6604f2c57f394;p=ceph-ci.git mgr/dashboard: edit cephfs subvolumegroup Fixes: https://tracker.ceph.com/issues/62370 Signed-off-by: Pedro Gonzalez Gomez (cherry picked from commit 44fc6f7cd3a82c33e153700dc1ffdeaa186711d0) --- diff --git a/src/pybind/mgr/dashboard/controllers/cephfs.py b/src/pybind/mgr/dashboard/controllers/cephfs.py index d51683e1b96..6995b18244e 100644 --- a/src/pybind/mgr/dashboard/controllers/cephfs.py +++ b/src/pybind/mgr/dashboard/controllers/cephfs.py @@ -672,6 +672,16 @@ class CephFSSubvolumeGroups(RESTController): group['info'] = json.loads(out) return subvolume_groups + @RESTController.Resource('GET') + def info(self, vol_name: str, group_name: str): + error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_info', None, { + 'vol_name': vol_name, 'group_name': group_name}) + if error_code != 0: + raise DashboardException( + f'Failed to get info for subvolume group {group_name}: {err}' + ) + return json.loads(out) + def create(self, vol_name: str, group_name: str, **kwargs): error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_create', None, { 'vol_name': vol_name, 'group_name': group_name, **kwargs}) @@ -679,3 +689,14 @@ class CephFSSubvolumeGroups(RESTController): raise DashboardException( f'Failed to create subvolume group {group_name}: {err}' ) + + def set(self, vol_name: str, group_name: str, size: str): + if not size: + return f'Failed to update subvolume group {group_name}, size was not provided' + error_code, _, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_resize', None, { + 'vol_name': vol_name, 'group_name': group_name, 'new_size': size}) + if error_code != 0: + raise DashboardException( + f'Failed to update subvolume group {group_name}: {err}' + ) + return f'Subvolume group {group_name} updated successfully' diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.html index 653bd77a0c7..8b88c47d5e9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.html @@ -6,7 +6,8 @@ [columns]="columns" selectionType="single" [hasDetails]="false" - (fetchData)="fetchData()"> + (fetchData)="fetchData()" + (updateSelection)="updateSelection($event)">
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts index e1fc307afaf..b7ca08fb947 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts @@ -9,7 +9,7 @@ import { CdTableAction } from '~/app/shared/models/cd-table-action'; import { CdTableColumn } from '~/app/shared/models/cd-table-column'; import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context'; import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; -import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model'; +import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolumegroup.model'; import { CephfsSubvolumegroupFormComponent } from '../cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component'; import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; @@ -103,15 +103,14 @@ export class CephfsSubvolumeGroupComponent implements OnInit { name: this.actionLabels.CREATE, permission: 'create', icon: Icons.add, - click: () => - this.modalService.show( - CephfsSubvolumegroupFormComponent, - { - fsName: this.fsName, - pools: this.pools - }, - { size: 'lg' } - ) + click: () => this.openModal(), + canBePrimary: (selection: CdTableSelection) => !selection.hasSelection + }, + { + name: this.actionLabels.EDIT, + permission: 'update', + icon: Icons.edit, + click: () => this.openModal(true) } ]; @@ -139,4 +138,17 @@ export class CephfsSubvolumeGroupComponent implements OnInit { updateSelection(selection: CdTableSelection) { this.selection = selection; } + + openModal(edit = false) { + this.modalService.show( + CephfsSubvolumegroupFormComponent, + { + fsName: this.fsName, + subvolumegroupName: this.selection?.first()?.name, + pools: this.pools, + isEdit: edit + }, + { size: 'lg' } + ); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.html index b3a26cb15cc..d83233b8ef0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.html @@ -2,7 +2,8 @@ {{ action | titlecase }} {{ resource | upperFirst }} - +
This field is required. The subvolumegroup already exists. + i18n>The subvolume group already exists.
@@ -47,8 +48,8 @@
+ [initialValue]="initialMode" + [scopes]="scopes" + [isDisabled]="isEdit">
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.ts index 3168889f1fc..f7d5c4802a4 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.ts @@ -11,15 +11,20 @@ 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 { CdForm } from '~/app/shared/forms/cd-form'; +import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe'; +import { OctalToHumanReadablePipe } from '~/app/shared/pipes/octal-to-human-readable.pipe'; @Component({ selector: 'cd-cephfs-subvolumegroup-form', templateUrl: './cephfs-subvolumegroup-form.component.html', styleUrls: ['./cephfs-subvolumegroup-form.component.scss'] }) -export class CephfsSubvolumegroupFormComponent implements OnInit { +export class CephfsSubvolumegroupFormComponent extends CdForm implements OnInit { fsName: string; + subvolumegroupName: string; pools: Pool[]; + isEdit: boolean = false; subvolumegroupForm: CdFormGroup; @@ -30,6 +35,11 @@ export class CephfsSubvolumegroupFormComponent implements OnInit { columns: CdTableColumn[]; scopePermissions: Array = []; + initialMode = { + owner: ['read', 'write', 'execute'], + group: ['read', 'execute'], + others: ['read', 'execute'] + }; scopes: string[] = ['owner', 'group', 'others']; constructor( @@ -37,13 +47,16 @@ export class CephfsSubvolumegroupFormComponent implements OnInit { private actionLabels: ActionLabelsI18n, private taskWrapper: TaskWrapperService, private cephfsSubvolumeGroupService: CephfsSubvolumeGroupService, - private formatter: FormatterService + private formatter: FormatterService, + private dimlessBinary: DimlessBinaryPipe, + private octalToHumanReadable: OctalToHumanReadablePipe ) { - this.action = this.actionLabels.CREATE; + super(); this.resource = $localize`subvolume group`; } ngOnInit(): void { + this.action = this.actionLabels.CREATE; this.columns = [ { prop: 'scope', @@ -72,6 +85,8 @@ export class CephfsSubvolumegroupFormComponent implements OnInit { this.dataPools = this.pools.filter((pool) => pool.type === 'data'); this.createForm(); + + this.isEdit ? this.populateForm() : this.loadingReady(); } createForm() { @@ -101,35 +116,83 @@ export class CephfsSubvolumegroupFormComponent implements OnInit { }); } + populateForm() { + this.action = this.actionLabels.EDIT; + this.cephfsSubvolumeGroupService + .info(this.fsName, this.subvolumegroupName) + .subscribe((resp: any) => { + // Disabled these fields since its not editable + this.subvolumegroupForm.get('subvolumegroupName').disable(); + this.subvolumegroupForm.get('pool').disable(); + this.subvolumegroupForm.get('uid').disable(); + this.subvolumegroupForm.get('gid').disable(); + + this.subvolumegroupForm.get('subvolumegroupName').setValue(this.subvolumegroupName); + if (resp.bytes_quota !== 'infinite') { + this.subvolumegroupForm + .get('size') + .setValue(this.dimlessBinary.transform(resp.bytes_quota)); + } + this.subvolumegroupForm.get('uid').setValue(resp.uid); + this.subvolumegroupForm.get('gid').setValue(resp.gid); + this.initialMode = this.octalToHumanReadable.transform(resp.mode, true); + + this.loadingReady(); + }); + } + submit() { const subvolumegroupName = this.subvolumegroupForm.getValue('subvolumegroupName'); const pool = this.subvolumegroupForm.getValue('pool'); - const size = this.formatter.toBytes(this.subvolumegroupForm.getValue('size')); + const size = this.formatter.toBytes(this.subvolumegroupForm.getValue('size')) || 0; const uid = this.subvolumegroupForm.getValue('uid'); const gid = this.subvolumegroupForm.getValue('gid'); const mode = this.formatter.toOctalPermission(this.subvolumegroupForm.getValue('mode')); - this.taskWrapper - .wrapTaskAroundCall({ - task: new FinishedTask('cephfs/subvolume/group/' + URLVerbs.CREATE, { - subvolumegroupName: subvolumegroupName - }), - call: this.cephfsSubvolumeGroupService.create( - this.fsName, - subvolumegroupName, - pool, - size, - uid, - gid, - mode - ) - }) - .subscribe({ - error: () => { - this.subvolumegroupForm.setErrors({ cdSubmitButton: true }); - }, - complete: () => { - this.activeModal.close(); - } - }); + if (this.isEdit) { + const editSize = size === 0 ? 'infinite' : size; + this.taskWrapper + .wrapTaskAroundCall({ + task: new FinishedTask('cephfs/subvolume/group/' + URLVerbs.EDIT, { + subvolumegroupName: subvolumegroupName + }), + call: this.cephfsSubvolumeGroupService.update( + this.fsName, + subvolumegroupName, + String(editSize) + ) + }) + .subscribe({ + error: () => { + this.subvolumegroupForm.setErrors({ cdSubmitButton: true }); + }, + complete: () => { + this.activeModal.close(); + } + }); + } else { + this.taskWrapper + .wrapTaskAroundCall({ + task: new FinishedTask('cephfs/subvolume/group/' + URLVerbs.CREATE, { + subvolumegroupName: subvolumegroupName + }), + call: this.cephfsSubvolumeGroupService.create( + this.fsName, + subvolumegroupName, + pool, + String(size), + uid, + gid, + mode + ) + }) + .subscribe({ + error: () => { + this.subvolumegroupForm.setErrors({ cdSubmitButton: true }); + }, + complete: () => { + this.activeModal.close(); + } + }); + } } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.ts index db7a9db91bb..642f4609f3d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.ts @@ -1,7 +1,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; -import { CephfsSubvolumeGroup } from '../models/cephfs-subvolume-group.model'; +import { CephfsSubvolumeGroup } from '../models/cephfs-subvolumegroup.model'; import _ from 'lodash'; import { mapTo, catchError } from 'rxjs/operators'; @@ -21,7 +21,7 @@ export class CephfsSubvolumeGroupService { volName: string, groupName: string, poolName: string, - size: number, + size: string, uid: number, gid: number, mode: string @@ -60,4 +60,11 @@ export class CephfsSubvolumeGroupService { }) ); } + + update(volName: string, groupName: string, size: string) { + return this.http.put(`${this.baseURL}/${volName}`, { + group_name: groupName, + size: size + }); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-subvolumegroup.model.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-subvolumegroup.model.ts new file mode 100644 index 00000000000..fc087ab53d0 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-subvolumegroup.model.ts @@ -0,0 +1,13 @@ +export interface CephfsSubvolumeGroup { + name: string; + info: CephfsSubvolumeGroupInfo; +} + +export interface CephfsSubvolumeGroupInfo { + mode: number; + bytes_pcent: number; + bytes_quota: number; + data_pool: string; + state: string; + created_at: string; +} 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 766b31dbcbb..eb4ae7651f7 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 @@ -370,6 +370,9 @@ export class TaskMessageService { ), 'cephfs/subvolume/group/create': this.newTaskMessage(this.commonOperations.create, (metadata) => this.subvolumegroup(metadata) + ), + 'cephfs/subvolume/group/edit': this.newTaskMessage(this.commonOperations.update, (metadata) => + this.subvolumegroup(metadata) ) }; diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 6d1c3da5e3f..aa797ae703c 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -1765,6 +1765,82 @@ paths: - jwt: [] tags: - CephfsSubvolumeGroup + put: + parameters: + - in: path + name: vol_name + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + properties: + group_name: + type: string + size: + type: integer + required: + - group_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: + - CephfsSubvolumeGroup + /api/cephfs/subvolume/group/{vol_name}/info: + get: + parameters: + - in: path + name: vol_name + required: true + schema: + type: string + - in: query + name: group_name + required: true + schema: + type: string + responses: + '200': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: OK + '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: + - CephfsSubvolumeGroup /api/cephfs/subvolume/{vol_name}: delete: parameters: