From abc2cd078f434eb6705eaddb3fc2476b280d3dca Mon Sep 17 00:00:00 2001 From: Sagar Gopale Date: Mon, 16 Feb 2026 18:18:42 +0530 Subject: [PATCH] mgr/dashboard: NVme- Add namespace in subsystem resource page Fixes: https://tracker.ceph.com/issues/74338 Signed-off-by: Sagar Gopale --- .../src/app/ceph/block/block.module.ts | 6 +++ .../nvme-subsystem-view.component.html | 1 - .../nvmeof-gateway.component.html | 3 ++ .../nvmeof-namespaces-form.component.html | 6 +-- .../nvmeof-namespaces-form.component.ts | 45 ++++++++++++++----- .../nvmeof-namespaces-list.component.html | 17 ++++--- .../nvmeof-namespaces-list.component.spec.ts | 3 +- .../nvmeof-namespaces-list.component.ts | 13 +++--- ...eof-subsystem-namespaces-list.component.ts | 37 +++++++-------- .../nvmeof-subsystems.component.ts | 4 +- 10 files changed, 81 insertions(+), 54 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts index 825c3b8b3f5..a039e6a76fd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts @@ -432,6 +432,7 @@ const routes: Routes = [ path: 'hosts', component: NvmeofInitiatorsListComponent }, + { path: 'namespaces', component: NvmeofSubsystemNamespacesListComponent @@ -453,6 +454,11 @@ const routes: Routes = [ path: `${URLVerbs.ADD}/listener`, component: NvmeofListenersFormComponent, outlet: 'modal' + }, + { + path: `${URLVerbs.EDIT}/:subsystem_nqn/namespace/:nsid`, + component: NvmeofNamespaceExpandModalComponent, + outlet: 'modal' } ] } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvme-subsystem-view/nvme-subsystem-view.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvme-subsystem-view/nvme-subsystem-view.component.html index 296351107ec..50ac54ed73c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvme-subsystem-view/nvme-subsystem-view.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvme-subsystem-view/nvme-subsystem-view.component.html @@ -2,5 +2,4 @@ [title]="subsystemNQN" [items]="sidebarItems" > - diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.html index 3af5b947f0a..74f93c89d34 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.html @@ -12,6 +12,7 @@ @@ -19,6 +20,7 @@ @@ -26,6 +28,7 @@ diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.html index 6c6f11fdf75..c87e889e42e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.html @@ -12,10 +12,10 @@ [columnNumbers]="{sm: 4, md: 8}">
-

{{ action | titlecase }} {{ resource | titlecase }}

+

{{ title }}

- - Namespaces define the storage volumes that subsystems present to hosts. + + {{ description }}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.ts index d71e41046b8..e730bb081e4 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { UntypedFormControl, Validators } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Params, Router } from '@angular/router'; import { NamespaceCreateRequest, NamespaceInitiatorRequest, @@ -40,9 +40,12 @@ export class NvmeofNamespacesFormComponent implements OnInit { resource: string; pageURL: string; + title: string = ''; + description: string = ''; + nsForm: CdFormGroup; subsystemNQN: string; - subsystems: NvmeofSubsystem[]; + subsystems: NvmeofSubsystem[] = []; rbdPools: Array = null; rbdImages: any[] = []; initiatorCandidates: { content: string; selected: boolean }[] = []; @@ -74,18 +77,36 @@ export class NvmeofNamespacesFormComponent implements OnInit { } init() { - this.route.queryParams.subscribe((params) => { - this.group = params?.['group']; - }); this.createForm(); this.action = this.actionLabels.CREATE; - this.route.params.subscribe((params: { subsystem_nqn: string; nsid: string }) => { - this.subsystemNQN = params.subsystem_nqn; - this.nsid = params?.nsid; + this.title = this.action + ' ' + this.resource; + this.description = $localize`Namespaces define the storage volumes that subsystems present to hosts.`; + + this.route.params.subscribe((params: Params) => { + this.subsystemNQN = params['subsystem_nqn']; + this.nsid = params['nsid']; + if (params['group']) { + this.group = params['group']; + } + if (this.subsystemNQN && this.group) { + this.pageURL = `block/nvmeof/subsystems/${this.subsystemNQN}/${this.group}`; + this.action = this.actionLabels.ADD; + this.title = this.action + ' ' + this.resource; + this.description = $localize`Create a new namespace associated with this subsystem.`; + } }); - this.route.queryParams.subscribe((params) => { - if (params?.['subsystem_nqn']) { - this.subsystemNQN = params?.['subsystem_nqn']; + this.route.queryParams.subscribe((params: Params) => { + if (params['group']) { + this.group = params['group']; + } + if (params['subsystem_nqn']) { + this.subsystemNQN = params['subsystem_nqn']; + } + if (this.subsystemNQN && this.group) { + this.pageURL = `block/nvmeof/subsystems/${this.subsystemNQN}/namespaces`; + this.action = this.actionLabels.ADD; + this.title = this.action + ' ' + this.resource; + this.description = $localize`Create a new namespace associated with this subsystem.`; } }); } @@ -408,7 +429,7 @@ export class NvmeofNamespacesFormComponent implements OnInit { }, complete: () => { this.router.navigate([this.pageURL], { - queryParams: { group: this.group, tab: 'namespace' } + queryParams: { group: this.group } }); } }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.html index f3e43fe6d60..59bd7b3dade 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.html @@ -25,7 +25,6 @@ -
- - -
-
+
+ + +
+ diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts index 2345d290741..1f9c412f215 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts @@ -2,6 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HttpClientModule } from '@angular/common/http'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { of } from 'rxjs'; +import { take } from 'rxjs/operators'; import { RouterTestingModule } from '@angular/router/testing'; import { SharedModule } from '~/app/shared/shared.module'; @@ -88,7 +89,7 @@ describe('NvmeofNamespacesListComponent', () => { it('should retrieve namespaces', (done) => { component.group = 'g1'; - component.namespaces$.subscribe((namespaces) => { + component.namespaces$.pipe(take(1)).subscribe((namespaces) => { expect(namespaces).toEqual(mockNamespaces); done(); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.ts index 804ecf0b802..804bcbf192b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.ts @@ -110,17 +110,13 @@ export class NvmeofNamespacesListComponent implements OnInit, OnDestroy { name: $localize`Expand`, permission: 'update', icon: Icons.edit, - click: () => + click: (row: NvmeofSubsystemNamespace) => { + const namespace = row || this.selection.first(); this.router.navigate( [ { outlets: { - modal: [ - URLVerbs.EDIT, - this.selection.first().ns_subsystem_nqn, - 'namespace', - this.selection.first().nsid - ] + modal: [URLVerbs.EDIT, namespace.ns_subsystem_nqn, 'namespace', namespace.nsid] } } ], @@ -129,7 +125,8 @@ export class NvmeofNamespacesListComponent implements OnInit, OnDestroy { queryParams: { group: this.group }, queryParamsHandling: 'merge' } - ) + ); + } }, { name: this.actionLabels.DELETE, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystem-namespaces-list/nvmeof-subsystem-namespaces-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystem-namespaces-list/nvmeof-subsystem-namespaces-list.component.ts index 7f3957f6196..852f0a006d8 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystem-namespaces-list/nvmeof-subsystem-namespaces-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystem-namespaces-list/nvmeof-subsystem-namespaces-list.component.ts @@ -17,8 +17,6 @@ import { DeleteConfirmationModalComponent } from '~/app/shared/components/delete import { combineLatest, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -const BASE_URL = 'block/nvmeof/subsystems'; - @Component({ selector: 'cd-nvmeof-subsystem-namespaces-list', templateUrl: './nvmeof-subsystem-namespaces-list.component.html', @@ -105,37 +103,40 @@ export class NvmeofSubsystemNamespacesListComponent implements OnInit, OnDestroy setupTableActions() { this.tableActions = [ { - name: this.actionLabels.CREATE, + name: this.actionLabels.ADD, permission: 'create', icon: Icons.add, click: () => - this.router.navigate( - [BASE_URL, { outlets: { modal: [URLVerbs.CREATE, this.subsystemNQN, 'namespace'] } }], - { queryParams: { group: this.group } } - ), + this.router.navigate(['block/nvmeof/namespaces/create'], { + queryParams: { + group: this.group, + subsystem_nqn: this.subsystemNQN + } + }), + canBePrimary: (selection: CdTableSelection) => !selection.hasSelection }, { - name: this.actionLabels.EDIT, + name: $localize`Expand`, permission: 'update', icon: Icons.edit, - click: () => + click: (row: NvmeofSubsystemNamespace) => { + const namespace = row || this.selection.first(); this.router.navigate( [ - BASE_URL, { outlets: { - modal: [ - URLVerbs.EDIT, - this.subsystemNQN, - 'namespace', - this.selection.first().nsid - ] + modal: [URLVerbs.EDIT, this.subsystemNQN, 'namespace', namespace.nsid] } } ], - { queryParams: { group: this.group } } - ) + { + relativeTo: this.route.parent, + queryParams: { group: this.group }, + queryParamsHandling: 'merge' + } + ); + } }, { name: this.actionLabels.DELETE, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.ts index d3efcb3faca..e40ae66e498 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.ts @@ -221,6 +221,8 @@ export class NvmeofSubsystemsComponent extends ListWithDetails implements OnInit updateGroupSelectionState() { if (this.gwGroups.length) { + this.gwGroupsEmpty = false; + this.gwGroupPlaceholder = DEFAULT_PLACEHOLDER; if (!this.group) { this.onGroupSelection(this.gwGroups[0]); } else { @@ -229,8 +231,6 @@ export class NvmeofSubsystemsComponent extends ListWithDetails implements OnInit selected: g.content === this.group })); } - this.gwGroupsEmpty = false; - this.gwGroupPlaceholder = DEFAULT_PLACEHOLDER; } else { this.gwGroupsEmpty = true; this.gwGroupPlaceholder = $localize`No groups available`; -- 2.47.3