]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: edit cephfs subvolumegroup
authorPedro Gonzalez Gomez <pegonzal@redhat.com>
Wed, 9 Aug 2023 08:43:54 +0000 (10:43 +0200)
committerNizamudeen A <nia@redhat.com>
Wed, 16 Aug 2023 05:26:15 +0000 (10:56 +0530)
Fixes: https://tracker.ceph.com/issues/62370
Signed-off-by: Pedro Gonzalez Gomez <pegonzal@redhat.com>
(cherry picked from commit 44fc6f7cd3a82c33e153700dc1ffdeaa186711d0)

src/pybind/mgr/dashboard/controllers/cephfs.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolumegroup-form/cephfs-subvolumegroup-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-subvolumegroup.model.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts
src/pybind/mgr/dashboard/openapi.yaml

index d51683e1b96b45847de892c099e7c55716bce49c..6995b18244ebfd7cd053c1a25cc63a9bea2e2cf4 100644 (file)
@@ -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'
index 653bd77a0c7689118e2a9d0dc102e75529dbd47f..8b88c47d5e90ed4c6639a14e84f75d7f97f934b6 100644 (file)
@@ -6,7 +6,8 @@
             [columns]="columns"
             selectionType="single"
             [hasDetails]="false"
-            (fetchData)="fetchData()">
+            (fetchData)="fetchData()"
+            (updateSelection)="updateSelection($event)">
 
     <div class="table-actions btn-toolbar">
       <cd-table-actions [permission]="permissions.cephfs"
@@ -23,9 +24,9 @@
              let-row="row">
   <cd-usage-bar *ngIf="row.info.bytes_pcent && row.info.bytes_pcent !== 'undefined'; else noLimitTpl"
                 [total]="row.info.bytes_quota"
-                [used]="row.info.bytes_pcent"
+                [used]="row.info.bytes_used"
                 [title]="row.name"
-                [calculatePerc]="false"
+                [showFreeToolTip]="false"
                 customLegend="Quota"
                 [customLegendValue]="row.info.bytes_quota"
                 decimals="2"></cd-usage-bar>
index e1fc307afaf99221b1f58e32ba1b9f426adf7c81..b7ca08fb9471d2d1e3e22143c8978fe096cdce1c 100644 (file)
@@ -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' }
+    );
+  }
 }
index b3a26cb15ccd0d7c526cd00f81afade0ff5f3414..d83233b8ef0a25d99bb068614e99b14e78fca296 100644 (file)
@@ -2,7 +2,8 @@
   <ng-container i18n="form title"
                 class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
 
-  <ng-container class="modal-content">
+  <ng-container class="modal-content"
+                *cdFormLoading="loading">
     <form name="subvolumegroupForm"
           #formDir="ngForm"
           [formGroup]="subvolumegroupForm"
@@ -25,7 +26,7 @@
                   i18n>This field is required.</span>
             <span class="invalid-feedback"
                   *ngIf="subvolumegroupForm.showError('subvolumegroupName', formDir, 'notUnique')"
-                  i18n>The subvolumegroup already exists.</span>
+                  i18n>The subvolume group already exists.</span>
           </div>
         </div>
 
@@ -47,8 +48,8 @@
           <label class="cd-col-form-label"
                  for="size"
                  i18n>Size
-            <cd-helper>The size of the subvolumegropup is specified by setting a quota on it.
-                  If left blank or put 0, then quota will be infinite</cd-helper>
+            <cd-helper>The size of the subvolume group is specified by setting a quota on it.
+            If left blank or put 0, then quota will be infinite</cd-helper>
           </label>
           <div class="cd-col-form-input">
             <input class="form-control"
                                    [form]="subvolumegroupForm"
                                    inputField="mode"
                                    [isTableForOctalMode]="true"
-                                   [scopes]="scopes"></cd-checked-table-form>
+                                   [initialValue]="initialMode"
+                                   [scopes]="scopes"
+                                   [isDisabled]="isEdit"></cd-checked-table-form>
           </div>
         </div>
       </div>
index 3168889f1fc56c693f6d0a5717bc8b1e4f1cf696..f7d5c4802a4d3637fc74154ebf54da89c01daf60 100644 (file)
@@ -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<any> = [];
+  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();
+          }
+        });
+    }
   }
 }
index db7a9db91bb980b97d043cf9d1e11901bcb259ca..642f4609f3d00944b8131f98ef984d454650bcb7 100644 (file)
@@ -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 (file)
index 0000000..fc087ab
--- /dev/null
@@ -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;
+}
index 766b31dbcbb25064ae77150b116130021a880d01..eb4ae7651f7257b9d39698bdc82e2a585f9b6085 100644 (file)
@@ -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)
     )
   };
 
index 6d1c3da5e3f7a4acd99a4cd106d74f9511ffba84..aa797ae703ca8965d79c2a474b5f5f1bece48c80 100644 (file)
@@ -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: