From a3654c330828a24ecc9a51a84080f1a8b250a713 Mon Sep 17 00:00:00 2001 From: Pedro Gonzalez Gomez Date: Tue, 25 Jul 2023 18:16:35 +0200 Subject: [PATCH] mgr/dashboard: list subvolume groups Fixes: https://tracker.ceph.com/issues/62168 Signed-off-by: Pedro Gonzalez Gomez (cherry picked from commit cd165601e8538bfea56ed8bc7057f915b1e132c8) --- .../mgr/dashboard/controllers/cephfs.py | 26 +++++ .../cephfs-subvolume-group.component.html | 43 +++++++++ .../cephfs-subvolume-group.component.scss | 0 .../cephfs-subvolume-group.component.spec.ts | 26 +++++ .../cephfs-subvolume-group.component.ts | 95 +++++++++++++++++++ .../cephfs-tabs/cephfs-tabs.component.html | 8 ++ .../src/app/ceph/cephfs/cephfs.module.ts | 5 +- .../cephfs-subvolume-group.service.spec.ts | 23 +++++ .../api/cephfs-subvolume-group.service.ts | 17 ++++ .../models/cephfs-subvolume-group.model.ts | 13 +++ src/pybind/mgr/dashboard/openapi.yaml | 29 ++++++ 11 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-subvolume-group.model.ts diff --git a/src/pybind/mgr/dashboard/controllers/cephfs.py b/src/pybind/mgr/dashboard/controllers/cephfs.py index 6b26e0a91f2a7..0758d2ee9ab5e 100644 --- a/src/pybind/mgr/dashboard/controllers/cephfs.py +++ b/src/pybind/mgr/dashboard/controllers/cephfs.py @@ -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 index 0000000000000..27cdc155424e6 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + {{row.info.bytes_used | dimlessBinary}} + + + + + + + + + + {{ result.content }} + + 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 index 0000000000000..e69de29bb2d1d 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 index 0000000000000..b36bd2cb09000 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.spec.ts @@ -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; + + 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 index 0000000000000..63a36f27bc0b9 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-subvolume-group/cephfs-subvolume-group.component.ts @@ -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; + + 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; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.html index 8d49a74dfb8a1..8a41575c36fc5 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.html @@ -20,6 +20,14 @@ [pools]="details.pools"> + + Subvolume groups + + + + + Clients diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts index 892c9058655b1..51ca4f96e1a8a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts @@ -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 index 0000000000000..13dad1430412b --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.spec.ts @@ -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 index 0000000000000..853041fe8b755 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/cephfs-subvolume-group.service.ts @@ -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 { + return this.http.get(`${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 index 0000000000000..fc087ab53d00c --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cephfs-subvolume-group.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/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 4650abd306feb..364bcbb876d0d 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -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 -- 2.39.5