From e541dca22788e74405a5a59805266e87ee0bec79 Mon Sep 17 00:00:00 2001 From: Achintk1491 Date: Tue, 18 Feb 2025 18:01:22 +0530 Subject: [PATCH] mgr/nfs: NFS Cluster and Export Listing Fixes: https://tracker.ceph.com/issues/70022 Signed-off-by: Achint Kaur --- src/pybind/mgr/dashboard/controllers/nfs.py | 13 ++- .../frontend/src/app/app-routing.module.ts | 4 +- .../app/ceph/nfs/models/nfs-cluster-config.ts | 12 +++ .../nfs-cluster-details.component.html | 11 +++ .../nfs-cluster-details.component.scss | 0 .../nfs-cluster-details.component.spec.ts | 23 +++++ .../nfs-cluster-details.component.ts | 13 +++ .../nfs-cluster/nfs-cluster.component.html | 51 +++++++++++ .../nfs-cluster/nfs-cluster.component.scss | 0 .../nfs-cluster/nfs-cluster.component.spec.ts | 23 +++++ .../nfs/nfs-cluster/nfs-cluster.component.ts | 91 +++++++++++++++++++ .../ceph/nfs/nfs-list/nfs-list.component.html | 16 +++- .../nfs/nfs-list/nfs-list.component.spec.ts | 6 -- .../ceph/nfs/nfs-list/nfs-list.component.ts | 30 +++++- .../frontend/src/app/ceph/nfs/nfs.module.ts | 23 ++++- .../frontend/src/app/ceph/rgw/rgw.module.ts | 4 +- .../src/app/shared/api/nfs.service.spec.ts | 5 +- .../src/app/shared/api/nfs.service.ts | 15 ++- src/pybind/mgr/dashboard/openapi.yaml | 16 +++- src/pybind/mgr/nfs/module.py | 12 ++- 20 files changed, 334 insertions(+), 34 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/models/nfs-cluster-config.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.ts diff --git a/src/pybind/mgr/dashboard/controllers/nfs.py b/src/pybind/mgr/dashboard/controllers/nfs.py index b417a585c299c..e170054b444cb 100644 --- a/src/pybind/mgr/dashboard/controllers/nfs.py +++ b/src/pybind/mgr/dashboard/controllers/nfs.py @@ -14,6 +14,7 @@ from ..security import Scope from ..services.cephfs import CephFS from ..services.exception import DashboardException, handle_cephfs_error, \ serialize_dashboard_exception +from ..tools import str_to_bool from . import APIDoc, APIRouter, BaseController, Endpoint, EndpointDoc, \ ReadPermission, RESTController, Task, UIRouter from ._version import APIVersion @@ -87,7 +88,11 @@ def NfsTask(name, metadata, wait_for): # noqa: N802 class NFSGaneshaCluster(RESTController): @ReadPermission @RESTController.MethodMap(version=APIVersion.EXPERIMENTAL) - def list(self): + def list(self, info: Optional[bool] = False): + if str_to_bool(info): + return [ + {"name": key, **value} for key, value in mgr.remote('nfs', 'cluster_info').items() + ] return mgr.remote('nfs', 'cluster_ls') @@ -109,11 +114,11 @@ class NFSGaneshaExports(RESTController): export['fsal'] = schema_fsal_info return export - @EndpointDoc("List all NFS-Ganesha exports", + @EndpointDoc("List all or cluster specific NFS-Ganesha exports ", responses={200: [EXPORT_SCHEMA]}) - def list(self) -> List[Dict[str, Any]]: + def list(self, cluster_id=None) -> List[Dict[str, Any]]: exports = [] - for export in mgr.remote('nfs', 'export_ls'): + for export in mgr.remote('nfs', 'export_ls', cluster_id, True): exports.append(self._get_schema_export(export)) return exports diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts index b9b9d453a0581..82e0c9c98724e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts @@ -26,7 +26,6 @@ import { ServicesComponent } from './ceph/cluster/services/services.component'; import { TelemetryComponent } from './ceph/cluster/telemetry/telemetry.component'; import { DashboardComponent } from './ceph/dashboard/dashboard/dashboard.component'; import { NfsFormComponent } from './ceph/nfs/nfs-form/nfs-form.component'; -import { NfsListComponent } from './ceph/nfs/nfs-list/nfs-list.component'; import { PerformanceCounterComponent } from './ceph/performance-counter/performance-counter/performance-counter.component'; import { LoginPasswordFormComponent } from './core/auth/login-password-form/login-password-form.component'; import { LoginComponent } from './core/auth/login/login.component'; @@ -56,6 +55,7 @@ import { SmbTabsComponent } from './ceph/smb/smb-tabs/smb-tabs.component'; import { SmbShareFormComponent } from './ceph/smb/smb-share-form/smb-share-form.component'; import { SmbJoinAuthFormComponent } from './ceph/smb/smb-join-auth-form/smb-join-auth-form.component'; import { SmbUsersgroupsFormComponent } from './ceph/smb/smb-usersgroups-form/smb-usersgroups-form.component'; +import { NfsClusterComponent } from './ceph/nfs/nfs-cluster/nfs-cluster.component'; @Injectable() export class PerformanceCounterBreadcrumbsResolver extends BreadcrumbsResolver { @@ -417,7 +417,7 @@ const routes: Routes = [ breadcrumbs: 'File/NFS' }, children: [ - { path: '', component: NfsListComponent }, + { path: '', component: NfsClusterComponent }, { path: `${URLVerbs.CREATE}/:fs_name/:subvolume_group`, component: NfsFormComponent, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/models/nfs-cluster-config.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/models/nfs-cluster-config.ts new file mode 100644 index 0000000000000..8fa98728bd7bc --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/models/nfs-cluster-config.ts @@ -0,0 +1,12 @@ +export interface NFSBackend { + hostname: string; + ip: string; + port: number; +} + +export interface NFSCluster { + name: string; + virtual_ip: number; + port: number; + backend: NFSBackend[]; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.html new file mode 100644 index 0000000000000..eb6a1ef2b7f0e --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.html @@ -0,0 +1,11 @@ + + + {{title | titlecase}} + + Lists exports for a cluster + + + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.scss new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.spec.ts new file mode 100644 index 0000000000000..b0f4176a02275 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NfsClusterDetailsComponent } from './nfs-cluster-details.component'; +import { configureTestBed } from '~/testing/unit-test-helper'; +import { SharedModule } from '~/app/shared/shared.module'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; + +describe('NfsClusterDetailsComponent', () => { + let component: NfsClusterDetailsComponent; + let fixture: ComponentFixture; + + configureTestBed({ + declarations: [NfsClusterDetailsComponent], + imports: [HttpClientTestingModule, SharedModule] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(NfsClusterDetailsComponent); + component = fixture.componentInstance; + }); + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.ts new file mode 100644 index 0000000000000..47128c1692e4d --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster-details/nfs-cluster-details.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; + +@Component({ + selector: 'cd-nfs-cluster-details', + templateUrl: './nfs-cluster-details.component.html', + styleUrls: ['./nfs-cluster-details.component.scss'] +}) +export class NfsClusterDetailsComponent { + title = $localize`Export`; + @Input() + selection: CdTableSelection; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.html new file mode 100644 index 0000000000000..2a6bdb815dbda --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.html @@ -0,0 +1,51 @@ + + + + + + + + + + + {{ row.virtual_ip }}:{{row.ports}} + + + + + + + + + {{ backend.ip }}:{{backend.port}} + + + + + + + + + {{backend.hostname }} + + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.scss new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.spec.ts new file mode 100644 index 0000000000000..7cfa939a23b61 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { configureTestBed } from '~/testing/unit-test-helper'; +import { SharedModule } from '~/app/shared/shared.module'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { NfsClusterComponent } from './nfs-cluster.component'; + +describe('NfsClusterComponent', () => { + let component: NfsClusterComponent; + let fixture: ComponentFixture; + + configureTestBed({ + declarations: [NfsClusterComponent], + imports: [HttpClientTestingModule, SharedModule] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(NfsClusterComponent); + component = fixture.componentInstance; + }); + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.ts new file mode 100644 index 0000000000000..0d9af51a078cf --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-cluster/nfs-cluster.component.ts @@ -0,0 +1,91 @@ +import { Component, NgZone, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { NfsService } from '~/app/shared/api/nfs.service'; +import { ListWithDetails } from '~/app/shared/classes/list-with-details.class'; +import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; +import { CdTableAction } from '~/app/shared/models/cd-table-action'; +import { CdTableColumn } from '~/app/shared/models/cd-table-column'; +import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; +import { Permission } from '~/app/shared/models/permissions'; +import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; +import { URLBuilderService } from '~/app/shared/services/url-builder.service'; +import { NFSCluster } from '../models/nfs-cluster-config'; +import { OrchestratorStatus } from '~/app/shared/models/orchestrator.interface'; +import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +const BASE_URL = 'cephfs/nfs'; +@Component({ + selector: 'cd-nfs-cluster', + templateUrl: './nfs-cluster.component.html', + styleUrls: ['./nfs-cluster.component.scss'], + providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }] +}) +export class NfsClusterComponent extends ListWithDetails implements OnInit { + @ViewChild('hostnameTpl', { static: true }) + hostnameTpl: TemplateRef; + + @ViewChild('ipAddrTpl', { static: true }) + ipAddrTpl: TemplateRef; + + @ViewChild('virtualIpTpl', { static: true }) + virtualIpTpl: TemplateRef; + + columns: CdTableColumn[] = []; + selection: CdTableSelection = new CdTableSelection(); + tableActions: CdTableAction[] = []; + permission: Permission; + orchStatus: OrchestratorStatus; + clusters$: Observable; + subject = new BehaviorSubject([]); + + constructor( + public actionLabels: ActionLabelsI18n, + protected ngZone: NgZone, + private authStorageService: AuthStorageService, + private nfsService: NfsService, + private orchService: OrchestratorService + ) { + super(); + } + + ngOnInit(): void { + this.orchService.status().subscribe((status: OrchestratorStatus) => { + this.orchStatus = status; + }); + this.permission = this.authStorageService.getPermissions().nfs; + this.clusters$ = this.subject.pipe(switchMap(() => this.nfsService.nfsClusterList())); + this.columns = [ + { + name: $localize`Name`, + prop: 'name', + flexGrow: 1 + }, + { + name: $localize`Hostnames`, + prop: 'backend', + flexGrow: 2, + cellTemplate: this.hostnameTpl + }, + { + name: $localize`IP Address`, + prop: 'backend', + flexGrow: 2, + cellTemplate: this.ipAddrTpl + }, + { + name: $localize`Virtual IP Address`, + prop: 'virtual_ip', + flexGrow: 1, + cellTemplate: this.virtualIpTpl + } + ]; + } + + loadData() { + this.subject.next([]); + } + + updateSelection(selection: CdTableSelection) { + this.selection = selection; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.html index 9adf835d75dcd..7ab6beda7801f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.html @@ -5,7 +5,7 @@ identifier="id" forceIdentifier="true" selectionType="single" - [hasDetails]="true" + [hasDetails]="false" (setExpandedRow)="setExpandedRow($event)" (updateSelection)="updateSelection($event)">
@@ -29,6 +29,20 @@ i18n>Object Gateway + + + NFSv{{protocol}} + + + + + + {{transport}} + + + { httpTesting.verify(); }); - it('should load exports on init', () => { - refresh(new Summary()); - httpTesting.expectOne('api/nfs-ganesha/export'); - expect(nfsService.list).toHaveBeenCalled(); - }); - it('should not load images on init because no data', () => { refresh(undefined); expect(nfsService.list).not.toHaveBeenCalled(); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.ts index 53b764be0b637..e00fe577625c0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { Router } from '@angular/router'; import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; @@ -49,6 +49,15 @@ export class NfsListComponent extends ListWithDetails implements OnInit, OnDestr @ViewChild('table', { static: true }) table: TableComponent; + @ViewChild('protocolTpl', { static: true }) + protocolTpl: TemplateRef; + + @ViewChild('transportTpl', { static: true }) + transportTpl: TemplateRef; + + @Input() clusterId: string; + modalRef: NgbModalRef; + columns: CdTableColumn[]; permission: Permission; selection = new CdTableSelection(); @@ -59,8 +68,6 @@ export class NfsListComponent extends ListWithDetails implements OnInit, OnDestr isDefaultCluster = false; fsal: SUPPORTED_FSAL; - modalRef: NgbModalRef; - builders = { 'nfs/create': (metadata: any) => { return { @@ -135,7 +142,8 @@ export class NfsListComponent extends ListWithDetails implements OnInit, OnDestr name: this.fsal === SUPPORTED_FSAL.CEPH ? $localize`Path` : $localize`Bucket`, prop: 'path', flexGrow: 2, - cellTemplate: this.pathTmpl + cellTemplate: this.pathTmpl, + cellTransformation: CellTemplate.path }, { name: $localize`Pseudo`, @@ -157,11 +165,23 @@ export class NfsListComponent extends ListWithDetails implements OnInit, OnDestr name: $localize`Access Type`, prop: 'access_type', flexGrow: 2 + }, + { + name: $localize`NFS Protocol`, + prop: 'protocols', + flexGrow: 2, + cellTemplate: this.protocolTpl + }, + { + name: $localize`Transports`, + prop: 'transports', + flexGrow: 2, + cellTemplate: this.transportTpl } ]; this.taskListService.init( - () => this.nfsService.list(), + () => this.nfsService.list(this.clusterId), (resp) => this.prepareResponse(resp), (exports) => (this.exports = exports), () => this.onFetchError(), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs.module.ts index 13d66ac162760..f7004c0501ad6 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs.module.ts @@ -18,10 +18,15 @@ import { IconService, InputModule, RadioModule, - SelectModule + SelectModule, + TabsModule, + TagModule } from 'carbon-components-angular'; import Close from '@carbon/icons/es/close/32'; +import { NfsClusterComponent } from './nfs-cluster/nfs-cluster.component'; +import { ClusterModule } from '../cluster/cluster.module'; +import { NfsClusterDetailsComponent } from './nfs-cluster-details/nfs-cluster-details.component'; @NgModule({ imports: [ @@ -33,15 +38,25 @@ import Close from '@carbon/icons/es/close/32'; NgbTypeaheadModule, NgbTooltipModule, GridModule, + TagModule, SelectModule, InputModule, RadioModule, CheckboxModule, ButtonModule, - IconModule + IconModule, + TabsModule, + ClusterModule ], - exports: [NfsListComponent, NfsFormComponent, NfsDetailsComponent], - declarations: [NfsListComponent, NfsDetailsComponent, NfsFormComponent, NfsFormClientComponent] + exports: [NfsListComponent, NfsFormComponent, NfsDetailsComponent, NfsClusterComponent], + declarations: [ + NfsListComponent, + NfsDetailsComponent, + NfsFormComponent, + NfsFormClientComponent, + NfsClusterComponent, + NfsClusterDetailsComponent + ] }) export class NfsModule { constructor(private iconService: IconService) { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts index f65ca55cb768b..c8017df84cb49 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts @@ -52,7 +52,6 @@ import { RgwSyncPrimaryZoneComponent } from './rgw-sync-primary-zone/rgw-sync-pr import { RgwSyncMetadataInfoComponent } from './rgw-sync-metadata-info/rgw-sync-metadata-info.component'; import { RgwSyncDataInfoComponent } from './rgw-sync-data-info/rgw-sync-data-info.component'; import { BucketTagModalComponent } from './bucket-tag-modal/bucket-tag-modal.component'; -import { NfsListComponent } from '../nfs/nfs-list/nfs-list.component'; import { NfsFormComponent } from '../nfs/nfs-form/nfs-form.component'; import { RgwMultisiteSyncPolicyComponent } from './rgw-multisite-sync-policy/rgw-multisite-sync-policy.component'; import { RgwMultisiteSyncPolicyFormComponent } from './rgw-multisite-sync-policy-form/rgw-multisite-sync-policy-form.component'; @@ -95,6 +94,7 @@ import { RgwBucketTieringFormComponent } from './rgw-bucket-tiering-form/rgw-buc import { RgwBucketLifecycleListComponent } from './rgw-bucket-lifecycle-list/rgw-bucket-lifecycle-list.component'; import { RgwRateLimitComponent } from './rgw-rate-limit/rgw-rate-limit.component'; import { RgwRateLimitDetailsComponent } from './rgw-rate-limit-details/rgw-rate-limit-details.component'; +import { NfsClusterComponent } from '../nfs/nfs-cluster/nfs-cluster.component'; @NgModule({ imports: [ @@ -374,7 +374,7 @@ const routes: Routes = [ breadcrumbs: 'NFS' }, children: [ - { path: '', component: NfsListComponent }, + { path: '', component: NfsClusterComponent }, { path: URLVerbs.CREATE, component: NfsFormComponent, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.spec.ts index 139fa490bfd82..431333bfad5d0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.spec.ts @@ -27,8 +27,9 @@ describe('NfsService', () => { }); it('should call list', () => { - service.list().subscribe(); - const req = httpTesting.expectOne('api/nfs-ganesha/export'); + let cluster_id = 'test'; + service.list(cluster_id).subscribe(); + const req = httpTesting.expectOne('api/nfs-ganesha/export?cluster_id=test'); expect(req.request.method).toBe('GET'); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.ts index 1fcce26e50a4e..a9953d330697b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nfs.service.ts @@ -2,6 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, throwError } from 'rxjs'; +import { NFSCluster } from '~/app/ceph/nfs/models/nfs-cluster-config'; import { NfsFSAbstractionLayer, SUPPORTED_FSAL } from '~/app/ceph/nfs/models/nfs.fsal'; import { ApiClient } from '~/app/shared/api/api-client'; @@ -44,7 +45,6 @@ export class NfsService extends ApiClient { disabled: false } ]; - nfsSquash = { no_root_squash: ['no_root_squash', 'noidsquash', 'none'], root_id_squash: ['root_id_squash', 'rootidsquash', 'rootid'], @@ -56,8 +56,10 @@ export class NfsService extends ApiClient { super(); } - list() { - return this.http.get(`${this.apiPath}/export`); + list(clusterId?: string) { + return this.http.get(`${this.apiPath}/export`, { + params: { cluster_id: clusterId } + }); } get(clusterId: string, exportId: string) { @@ -105,4 +107,11 @@ export class NfsService extends ApiClient { filesystems() { return this.http.get(`${this.uiApiPath}/cephfs/filesystems`); } + + nfsClusterList(): Observable { + return this.http.get(`${this.apiPath}/cluster`, { + headers: { Accept: this.getVersionHeaderValue(0, 1) }, + params: { info: true } + }); + } } diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index f35e75ac01e27..3a7ae4ae8ffef 100755 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -7546,7 +7546,12 @@ paths: - Multi-cluster /api/nfs-ganesha/cluster: get: - parameters: [] + parameters: + - default: false + in: query + name: info + schema: + type: boolean responses: '200': content: @@ -7568,7 +7573,12 @@ paths: - NFS-Ganesha /api/nfs-ganesha/export: get: - parameters: [] + parameters: + - allowEmptyValue: true + in: query + name: cluster_id + schema: + type: string responses: '200': content: @@ -7672,7 +7682,7 @@ paths: trace. security: - jwt: [] - summary: List all NFS-Ganesha exports + summary: 'List all or cluster specific NFS-Ganesha exports ' tags: - NFS-Ganesha post: diff --git a/src/pybind/mgr/nfs/module.py b/src/pybind/mgr/nfs/module.py index 80490ac8e7fe5..00fe8b0e60e2d 100644 --- a/src/pybind/mgr/nfs/module.py +++ b/src/pybind/mgr/nfs/module.py @@ -183,8 +183,10 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): def fetch_nfs_export_obj(self) -> ExportMgr: return self.export_mgr - def export_ls(self) -> List[Dict[Any, Any]]: - return self.export_mgr.list_all_exports() + def export_ls(self, cluster_id: Optional[str] = None, detailed: bool = False) -> List[Dict[Any, Any]]: + if not (cluster_id): + return self.export_mgr.list_all_exports() + return self.export_mgr.list_exports(cluster_id, detailed) def export_get(self, cluster_id: str, export_id: int) -> Optional[Dict[str, Any]]: return self.export_mgr.get_export_by_id(cluster_id, export_id) @@ -194,3 +196,9 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): def cluster_ls(self) -> List[str]: return available_clusters(self) + + def cluster_info(self, cluster_id: Optional[str] = None) -> Dict[str, Any]: + return self.nfs.show_nfs_cluster_info(cluster_id=cluster_id) + + def fetch_nfs_cluster_obj(self) -> NFSCluster: + return self.nfs -- 2.39.5