]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard : Unable to remove gateway node from gateway group 68302/head
authorpujaoshahu <pshahu@redhat.com>
Fri, 10 Apr 2026 07:13:29 +0000 (12:43 +0530)
committerpujaoshahu <pshahu@redhat.com>
Sun, 19 Apr 2026 15:37:42 +0000 (21:07 +0530)
Fixes: https://tracker.ceph.com/issues/75864
Signed-off-by: pujaoshahu <pshahu@redhat.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-node/nvmeof-gateway-node-add-modal/nvmeof-gateway-node-add-modal.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-node/nvmeof-gateway-node.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/models/service.interface.ts

index c6f1937f315bc9d15222c049356c635d7b54c53b..6c447d2e244f701ac7009e4b10a930617c1dcbfa 100644 (file)
@@ -169,8 +169,8 @@ export class NvmeofGatewayNodeAddModalComponent extends CdForm implements OnInit
 
     const { status, ...modifiedSpec } = this.serviceSpec;
 
-    if ('events' in modifiedSpec) {
-      delete (modifiedSpec as any).events;
+    if (modifiedSpec.events) {
+      delete modifiedSpec.events;
     }
 
     if (modifiedSpec.placement) {
@@ -180,7 +180,7 @@ export class NvmeofGatewayNodeAddModalComponent extends CdForm implements OnInit
     }
 
     if ('locations' in modifiedSpec.placement) {
-      delete (modifiedSpec.placement as any).locations;
+      delete modifiedSpec.placement.locations;
     }
 
     modifiedSpec.placement.hosts = newHosts;
index 833d31aaf624b53473d5919324c0882847cc3484..5e7aea62685a186c568dc52f780f6e859104ac85 100644 (file)
@@ -26,7 +26,7 @@ import { OrchestratorStatus } from '~/app/shared/models/orchestrator.interface';
 import { Permission } from '~/app/shared/models/permissions';
 
 import { Host } from '~/app/shared/models/host.interface';
-import { CephServiceSpec } from '~/app/shared/models/service.interface';
+import { CephServiceSpec, CephServiceSpecUpdate } from '~/app/shared/models/service.interface';
 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { CephServiceService } from '~/app/shared/api/ceph-service.service';
 import { NvmeofService } from '~/app/shared/api/nvmeof.service';
@@ -186,11 +186,9 @@ export class NvmeofGatewayNodeComponent implements OnInit, OnDestroy {
         deletionMessage: $localize`Removing <strong>${hostname}</strong> will detach it from the gateway group and stop handling new I/O requests. Active connections may be disrupted.<br><br>You can re-add this node later if required.`
       },
       submitActionObservable: () => {
-        const updatedSpec = _.cloneDeep(this.serviceSpec);
-        updatedSpec.placement.hosts = updatedSpec.placement.hosts.filter((h) => h !== hostname);
-        delete updatedSpec.status;
-        if (updatedSpec['events']) {
-          delete updatedSpec['events'];
+        const updatedSpec = this.buildRemoveGatewaySpecPayload(hostname);
+        if (!updatedSpec) {
+          return of(null);
         }
         return this.taskWrapper
           .wrapTaskAroundCall({
@@ -216,6 +214,34 @@ export class NvmeofGatewayNodeComponent implements OnInit, OnDestroy {
     });
   }
 
+  private buildRemoveGatewaySpecPayload(hostname: string): CephServiceSpecUpdate | null {
+    if (!this.serviceSpec) {
+      this.notificationService.show(
+        NotificationType.error,
+        $localize`Service specification is missing.`
+      );
+      return null;
+    }
+
+    const { status, ...updatedSpec } = _.cloneDeep(this.serviceSpec);
+
+    if (updatedSpec['events']) {
+      delete updatedSpec['events'];
+    }
+
+    if (!updatedSpec.placement) {
+      updatedSpec.placement = {};
+    }
+
+    if ('locations' in updatedSpec.placement) {
+      delete updatedSpec.placement.locations;
+    }
+
+    const currentHosts = updatedSpec.placement.hosts || [];
+    updatedSpec.placement.hosts = currentHosts.filter((h: string) => h !== hostname);
+    return updatedSpec;
+  }
+
   ngOnDestroy(): void {
     this.destroy$.next();
     this.destroy$.complete();
@@ -275,7 +301,7 @@ export class NvmeofGatewayNodeComponent implements OnInit, OnDestroy {
 
     const allUsedHostnames = new Set<string>();
     groupList.forEach((group: CephServiceSpec) => {
-      const hosts = group.placement?.hosts || (group.spec as any)?.placement?.hosts || [];
+      const hosts = group.placement?.hosts || group.spec?.placement?.hosts || [];
       hosts.forEach((hostname: string) => allUsedHostnames.add(hostname));
     });
 
@@ -301,7 +327,7 @@ export class NvmeofGatewayNodeComponent implements OnInit, OnDestroy {
       this.hosts = [];
     } else {
       const placementHosts =
-        this.serviceSpec.placement?.hosts || (this.serviceSpec.spec as any)?.placement?.hosts || [];
+        this.serviceSpec.placement?.hosts || this.serviceSpec.spec?.placement?.hosts || [];
       const currentGroupHosts = new Set<string>(placementHosts);
 
       this.hosts = (hostList || []).filter((host: Host) => {
index 583c7b7edcf5baffac37ee6ee80b0163d0242e4a..c85b6b795722efb80347b6c6b1533479b7b9fbfd 100644 (file)
@@ -49,12 +49,14 @@ export interface CephServiceSpec {
   certificate?: CephServiceCertificate;
   spec: CephServiceAdditionalSpec;
   placement: CephServicePlacement;
+  events?: string[];
 }
 
 // Type for service spec update payload (excludes read-only status field)
 export type CephServiceSpecUpdate = Omit<CephServiceSpec, 'status'>;
 
 export interface CephServiceAdditionalSpec {
+  placement?: CephServicePlacement;
   backend_service: string;
   api_user: string;
   api_password: string;
@@ -107,6 +109,7 @@ export interface CephServicePlacement {
   placement?: string;
   hosts?: string[];
   label?: string | string[];
+  locations?: Record<string, string[]>;
 }
 
 export interface QatSepcs {