]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: list subvolume groups
authorPedro Gonzalez Gomez <pegonzal@redhat.com>
Tue, 25 Jul 2023 16:16:35 +0000 (18:16 +0200)
committerNizamudeen A <nia@redhat.com>
Wed, 16 Aug 2023 05:24:46 +0000 (10:54 +0530)
Fixes: https://tracker.ceph.com/issues/62168
Signed-off-by: Pedro Gonzalez Gomez <pegonzal@redhat.com>
(cherry picked from commit cd165601e8538bfea56ed8bc7057f915b1e132c8)

src/pybind/mgr/dashboard/controllers/cephfs.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-subvolume-group.model.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/openapi.yaml

index 6b26e0a91f2a7ae6621aa63346ae33ed23c4a217..0758d2ee9ab5e4e24fef108dd1e3e42cad2d02df 100644 (file)
@@ -624,3 +624,29 @@ class CephFSSubvolume(RESTController):
             )
 
         return f'Subvolume {subvol_name} created successfully'
+
+
+@APIRouter('/cephfs/subvolume/group', Scope.CEPHFS)
+@APIDoc("Cephfs Subvolume Group Management API", "CephfsSubvolumeGroup")
+class CephFSSubvolumeGroups(RESTController):
+
+    def get(self, vol_name):
+        if not vol_name:
+            raise DashboardException(
+                'Error listing subvolume groups')
+        error_code, out, err = mgr.remote('volumes', '_cmd_fs_subvolumegroup_ls',
+                                          None, {'vol_name': vol_name})
+        if error_code != 0:
+            raise DashboardException(
+                'Error listing subvolume groups')
+        subvolume_groups = json.loads(out)
+        for group in subvolume_groups:
+            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}'
+                )
+            group['info'] = json.loads(out)
+        return subvolume_groups
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
new file mode 100644 (file)
index 0000000..27cdc15
--- /dev/null
@@ -0,0 +1,43 @@
+
+<ng-container *ngIf="subvolumeGroup$ | async as subvolumeGroup">
+  <cd-table *ngIf="subvolumeGroup"
+            [data]="subvolumeGroup"
+            columnMode="flex"
+            [columns]="columns"
+            selectionType="single"
+            [hasDetails]="false">
+  </cd-table>
+</ng-container>
+
+<ng-template #quotaUsageTpl
+             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"
+                [title]="row.name"
+                [calculatePerc]="false"
+                customLegend="Quota"
+                [customLegendValue]="row.info.bytes_quota"
+                decimals="2"></cd-usage-bar>
+
+  <ng-template #noLimitTpl>
+    <span ngbTooltip="Quota limit is not set"
+          *ngIf="row.info.bytes_pcent === 'undefined'"
+          i18n-ngbTooltip>
+      {{row.info.bytes_used | dimlessBinary}}</span>
+  </ng-template>
+</ng-template>
+
+<ng-template #typeTpl
+             let-value="value">
+  <cd-label [value]="value"></cd-label>
+</ng-template>
+
+<ng-template #modeToHumanReadableTpl
+             let-value="value">
+  <span *ngFor="let result of (value | octalToHumanReadable)"
+        [ngClass]="result.class"
+        [ngbTooltip]="result.toolTip">
+    {{ result.content }}
+  </span>
+</ng-template>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.spec.ts
new file mode 100644 (file)
index 0000000..b36bd2c
--- /dev/null
@@ -0,0 +1,26 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CephfsSubvolumeGroupComponent } from './cephfs-subvolume-group.component';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+
+describe('CephfsSubvolumeGroupComponent', () => {
+  let component: CephfsSubvolumeGroupComponent;
+  let fixture: ComponentFixture<CephfsSubvolumeGroupComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [CephfsSubvolumeGroupComponent],
+      imports: [HttpClientTestingModule]
+    }).compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CephfsSubvolumeGroupComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
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
new file mode 100644 (file)
index 0000000..63a36f2
--- /dev/null
@@ -0,0 +1,95 @@
+import { Component, Input, OnInit, ViewChild } from '@angular/core';
+import { Observable, of } from 'rxjs';
+import { catchError } from 'rxjs/operators';
+
+import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
+import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
+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';
+
+@Component({
+  selector: 'cd-cephfs-subvolume-group',
+  templateUrl: './cephfs-subvolume-group.component.html',
+  styleUrls: ['./cephfs-subvolume-group.component.scss']
+})
+export class CephfsSubvolumeGroupComponent implements OnInit {
+  @ViewChild('quotaUsageTpl', { static: true })
+  quotaUsageTpl: any;
+
+  @ViewChild('typeTpl', { static: true })
+  typeTpl: any;
+
+  @ViewChild('modeToHumanReadableTpl', { static: true })
+  modeToHumanReadableTpl: any;
+
+  @ViewChild('nameTpl', { static: true })
+  nameTpl: any;
+
+  @ViewChild('quotaSizeTpl', { static: true })
+  quotaSizeTpl: any;
+
+  @Input()
+  fsName: any;
+
+  columns: CdTableColumn[];
+  context: CdTableFetchDataContext;
+  selection = new CdTableSelection();
+
+  subvolumeGroup$: Observable<CephfsSubvolumeGroup[]>;
+
+  constructor(private cephfsSubvolumeGroup: CephfsSubvolumeGroupService) {}
+
+  ngOnInit(): void {
+    this.columns = [
+      {
+        name: $localize`Name`,
+        prop: 'name',
+        flexGrow: 0.6,
+        cellTransformation: CellTemplate.bold
+      },
+      {
+        name: $localize`Data Pool`,
+        prop: 'info.data_pool',
+        flexGrow: 0.7,
+        cellTransformation: CellTemplate.badge,
+        customTemplateConfig: {
+          class: 'badge-background-primary'
+        }
+      },
+      {
+        name: $localize`Usage`,
+        prop: 'info.bytes_pcent',
+        flexGrow: 0.7,
+        cellTemplate: this.quotaUsageTpl,
+        cellClass: 'text-right'
+      },
+      {
+        name: $localize`Mode`,
+        prop: 'info.mode',
+        flexGrow: 0.5,
+        cellTemplate: this.modeToHumanReadableTpl
+      },
+      {
+        name: $localize`Created`,
+        prop: 'info.created_at',
+        flexGrow: 0.5,
+        cellTransformation: CellTemplate.timeAgo
+      }
+    ];
+  }
+
+  ngOnChanges() {
+    this.subvolumeGroup$ = this.cephfsSubvolumeGroup.get(this.fsName).pipe(
+      catchError(() => {
+        this.context.error();
+        return of(null);
+      })
+    );
+  }
+
+  updateSelection(selection: CdTableSelection) {
+    this.selection = selection;
+  }
+}
index 8d49a74dfb8a11823c104eef4793dab026207b66..8a41575c36fc55c88e6e26892f7956f318cf7b1f 100644 (file)
                                   [pools]="details.pools"></cd-cephfs-subvolume-list>
       </ng-template>
     </ng-container>
+    <ng-container ngbNavItem="subvolume-groups">
+      <a ngbNavLink
+         i18n>Subvolume groups</a>
+      <ng-template ngbNavContent>
+        <cd-cephfs-subvolume-group [fsName]="selection.mdsmap.fs_name">
+        </cd-cephfs-subvolume-group>
+      </ng-template>
+    </ng-container>
     <ng-container ngbNavItem="clients">
       <a ngbNavLink>
         <ng-container i18n>Clients</ng-container>
index 892c9058655b1eaab1f0ed8e6ef142835e4422c9..51ca4f96e1a8a76dad8dcbf45cf0fbaa735b450c 100644 (file)
@@ -17,6 +17,7 @@ import { CephfsListComponent } from './cephfs-list/cephfs-list.component';
 import { CephfsTabsComponent } from './cephfs-tabs/cephfs-tabs.component';
 import { CephfsSubvolumeListComponent } from './cephfs-subvolume-list/cephfs-subvolume-list.component';
 import { CephfsSubvolumeFormComponent } from './cephfs-subvolume-form/cephfs-subvolume-form.component';
+import { CephfsSubvolumeGroupComponent } from './cephfs-subvolume-group/cephfs-subvolume-group.component';
 
 @NgModule({
   imports: [
@@ -40,7 +41,9 @@ import { CephfsSubvolumeFormComponent } from './cephfs-subvolume-form/cephfs-sub
     CephfsVolumeFormComponent,
     CephfsDirectoriesComponent,
     CephfsSubvolumeListComponent,
-    CephfsSubvolumeFormComponent
+    CephfsSubvolumeFormComponent,
+    CephfsDirectoriesComponent,
+    CephfsSubvolumeGroupComponent
   ]
 })
 export class CephfsModule {}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.spec.ts
new file mode 100644 (file)
index 0000000..13dad14
--- /dev/null
@@ -0,0 +1,23 @@
+import { TestBed } from '@angular/core/testing';
+
+import { configureTestBed } from '~/testing/unit-test-helper';
+import { CephfsSubvolumeGroupService } from './cephfs-subvolume-group.service';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+
+describe('CephfsSubvolumeGroupService', () => {
+  let service: CephfsSubvolumeGroupService;
+
+  configureTestBed({
+    imports: [HttpClientTestingModule],
+    providers: [CephfsSubvolumeGroupService]
+  });
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(CephfsSubvolumeGroupService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});
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
new file mode 100644 (file)
index 0000000..853041f
--- /dev/null
@@ -0,0 +1,17 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { CephfsSubvolumeGroup } from '../models/cephfs-subvolume-group.model';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class CephfsSubvolumeGroupService {
+  baseURL = 'api/cephfs/subvolume/group';
+
+  constructor(private http: HttpClient) {}
+
+  get(volName: string): Observable<CephfsSubvolumeGroup[]> {
+    return this.http.get<CephfsSubvolumeGroup[]>(`${this.baseURL}/${volName}`);
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-subvolume-group.model.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-subvolume-group.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 4650abd306feb54f500db0a8c52cececd41a3f46..364bcbb876d0d51df967551da03317802711b4c1 100644 (file)
@@ -1698,6 +1698,33 @@ paths:
       - jwt: []
       tags:
       - CephFSSubvolume
+  /api/cephfs/subvolume/group/{vol_name}:
+    get:
+      parameters:
+      - in: path
+        name: vol_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}:
     get:
       parameters:
@@ -11156,6 +11183,8 @@ tags:
   name: CephFSSubvolume
 - description: Cephfs Management API
   name: Cephfs
+- description: Cephfs Subvolume Group Management API
+  name: CephfsSubvolumeGroup
 - description: Get Cluster Details
   name: Cluster
 - description: Manage Cluster Configurations