]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: NVme- Add namespace in subsystem resource page
authorSagar Gopale <sagar.gopale@ibm.com>
Mon, 16 Feb 2026 12:48:42 +0000 (18:18 +0530)
committerSagar Gopale <sagar.gopale@ibm.com>
Thu, 26 Feb 2026 09:36:35 +0000 (15:06 +0530)
Fixes: https://tracker.ceph.com/issues/74338
Signed-off-by: Sagar Gopale <sagar.gopale@ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvme-subsystem-view/nvme-subsystem-view.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway/nvmeof-gateway.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-form/nvmeof-namespaces-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-namespaces-list/nvmeof-namespaces-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystem-namespaces-list/nvmeof-subsystem-namespaces-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-subsystems/nvmeof-subsystems.component.ts

index 825c3b8b3f59b5f3183c86a50942ee2415008bc4..a039e6a76fdda6db87e6a134b6d378b2762075c1 100644 (file)
@@ -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'
           }
         ]
       }
index 3af5b947f0ad8a5af004fa18c6ce0dd3709dbada..74f93c89d3408dd0b65065bf04f5be9452c86a22 100644 (file)
@@ -12,6 +12,7 @@
   <cds-tab
     heading="Gateways"
     [tabContent]="gateways_content"
+
     i18n-heading
     [active]="activeTab === Tabs.gateways"
     (selected)="onSelected(Tabs.gateways)">
@@ -19,6 +20,7 @@
   <cds-tab
     heading="Subsystem"
     [tabContent]="subsystem_content"
+
     i18n-heading
     [active]="activeTab === Tabs.subsystem"
     (selected)="onSelected(Tabs.subsystem)">
@@ -26,6 +28,7 @@
   <cds-tab
     heading="Namespace"
     [tabContent]="namespace_content"
+
     i18n-heading
     [active]="activeTab === Tabs.namespace"
     (selected)="onSelected(Tabs.namespace)">
index 6c6f11fdf75cf316cfba7c7aba36cabfdb4db0cd..c87e889e42eada174ce25c3baf4d601f67e83f7d 100644 (file)
          [columnNumbers]="{sm: 4, md: 8}">
       <div cdsRow
            class="form-heading form-item">
-        <h3>{{ action | titlecase }} {{ resource | titlecase }}</h3>
+        <h3>{{ title }}</h3>
         <cd-help-text>
-          <span i18n>
-            Namespaces define the storage volumes that subsystems present to hosts.
+          <span>
+            {{ description }}
           </span>
         </cd-help-text>
       </div>
index d71e41046b8520a72ddd6238ae36fe162dc047a0..e730bb081e4bd2ef5764a30e8cf4675b72ff5fef 100644 (file)
@@ -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<Pool> = 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 }
         });
       }
     });
index f3e43fe6d60d5d5492ec2648949f8bbfbd4f6d78..59bd7b3dade677eb30138f418a088886d25ba267 100644 (file)
@@ -25,7 +25,6 @@
   <cd-table [data]="namespaces"
             columnMode="flex"
             (fetchData)="fetchData()"
-            [autoReload]="false"
             [columns]="namespacesColumns"
             selectionType="single"
             (updateSelection)="updateSelection($event)"
             emptyStateMessage="Namespaces are storage volumes mapped to subsystems for host access. Create a namespace to start provisioning storage within a subsystem."
             i18n-emptyStateMessage>
 
-    <div class="table-actions">
-      <cd-table-actions [permission]="permission"
-                        [selection]="selection"
-                        class="btn-group"
-                        [tableActions]="tableActions">
-      </cd-table-actions>
-    </div>
-  </cd-table>
+  <div class="table-actions">
+    <cd-table-actions [permission]="permission"
+                      [selection]="selection"
+                      class="btn-group"
+                      [tableActions]="tableActions">
+    </cd-table-actions>
+  </div>
+</cd-table>
 </ng-container>
index 2345d29074155ea916040a89418ba647d4f217fd..1f9c412f21531d6404e4b9d28770ec2880621961 100644 (file)
@@ -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();
     });
index 804ecf0b8023dc28a3723cee60c857a8b47c7874..804bcbf192bdc601844a4d79e6d6ed6ea6cfa56b 100644 (file)
@@ -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,
index 7f3957f6196fbd9206c30f0b2fca7679b3d0e99b..852f0a006d860c97541262a59551b163ae192a47 100644 (file)
@@ -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,
index d3efcb3facaaefb96376505e070cd2bb85693512..e40ae66e498af1ff9aaa704a25f17daeb799cc0c 100644 (file)
@@ -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`;