From: Afreen Misbah Date: Thu, 23 Apr 2026 10:42:26 +0000 (+0530) Subject: mgr/dashboard: Nvmeof gateway group should account for labels X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=e3ce6fe31277d6d7aaa93f7a546e991a45484230;p=ceph.git mgr/dashboard: Nvmeof gateway group should account for labels - updates gateway group sections to account for placement label Signed-off-by: Afreen Misbah --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.ts index 4b4ac02794f1..3ccef418bd08 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.ts @@ -228,6 +228,7 @@ export class NvmeofGatewayGroupComponent implements OnInit { } }); } + private checkNodesAvailability(): void { forkJoin([this.nvmeofService.listGatewayGroups(), this.hostService.getAllHosts()]).subscribe( ([groups, hosts]: [GatewayGroup[][], any[]]) => { @@ -236,6 +237,15 @@ export class NvmeofGatewayGroupComponent implements OnInit { groupList.forEach((group: CephServiceSpec) => { const placementHosts = group.placement?.hosts || []; placementHosts.forEach((hostname: string) => usedHosts.add(hostname)); + + const placementLabel = group.placement?.label; + if (placementLabel) { + (hosts || []).forEach((host) => { + if (host.labels?.includes(placementLabel)) { + usedHosts.add(host.hostname); + } + }); + } }); const availableHosts = (hosts || []).filter((host) => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-node/nvmeof-gateway-node.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-node/nvmeof-gateway-node.component.ts index 5e7aea62685a..32549d1a828f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-node/nvmeof-gateway-node.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-node/nvmeof-gateway-node.component.ts @@ -303,8 +303,16 @@ export class NvmeofGatewayNodeComponent implements OnInit, OnDestroy { groupList.forEach((group: CephServiceSpec) => { const hosts = group.placement?.hosts || group.spec?.placement?.hosts || []; hosts.forEach((hostname: string) => allUsedHostnames.add(hostname)); - }); + const label = group.placement?.label || group.spec?.placement?.label; + if (label) { + (hostList || []).forEach((host: Host) => { + if (host.labels?.includes(label as string)) { + allUsedHostnames.add(host.hostname); + } + }); + } + }); this.usedHostnames = allUsedHostnames; // Check if there are any available hosts globally (not used by any group) @@ -328,11 +336,19 @@ export class NvmeofGatewayNodeComponent implements OnInit, OnDestroy { } else { const placementHosts = this.serviceSpec.placement?.hosts || this.serviceSpec.spec?.placement?.hosts || []; - const currentGroupHosts = new Set(placementHosts); - - this.hosts = (hostList || []).filter((host: Host) => { - return currentGroupHosts.has(host.hostname); - }); + const placementLabel = + this.serviceSpec.placement?.label || this.serviceSpec.spec?.placement?.label; + + if (placementHosts.length > 0) { + const currentGroupHosts = new Set(placementHosts); + this.hosts = (hostList || []).filter((host: Host) => currentGroupHosts.has(host.hostname)); + } else if (placementLabel) { + this.hosts = (hostList || []).filter((host: Host) => + host.labels?.includes(placementLabel as string) + ); + } else { + this.hosts = []; + } } this.count = this.hosts.length; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.spec.ts index e3ea965b0d3a..7db7faa1543e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.spec.ts @@ -109,7 +109,7 @@ describe('ServiceFormComponent', () => { // placement labels take only single value formHelper.setValue('service_type', 'mgr'); formHelper.setValue('placement', 'label'); - formHelper.setValue('label', 'foo'); + formHelper.setValue('label', "{content: 'foo', selected: true}"); component.onSubmit(); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.ts index 5d4573ab40f9..85fde2ce9302 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/nvmeof.service.ts @@ -90,9 +90,22 @@ export class NvmeofService { }).pipe( map(({ groups, hosts }) => { const usedHosts = new Set(); + (groups?.[0] ?? []).forEach((group: CephServiceSpec) => { - group.placement?.hosts?.forEach((hostname: string) => usedHosts.add(hostname)); + const placementHosts = group.placement?.hosts || []; + const placementLabel = group.placement?.label; + + placementHosts.forEach((hostname: string) => usedHosts.add(hostname)); + + if (placementLabel) { + (hosts || []).forEach((host: Host) => { + if (host.labels?.includes(placementLabel as string)) { + usedHosts.add(host.hostname); + } + }); + } }); + return (hosts || []).filter((host: Host) => { const isAvailable = host.status === HostStatus.AVAILABLE || host.status === HostStatus.RUNNING; @@ -130,15 +143,8 @@ export class NvmeofService { if (hosts?.length) { return allHosts.filter((host: Host) => hosts.includes(host.hostname)); - } else if (label?.length) { - if (typeof label === 'string') { - return allHosts.filter((host: Host) => host?.labels?.includes(label)); - } - return allHosts.filter( - (host: Host) => - host?.labels?.length === label?.length && - _.isEqual([...host.labels].sort(), [...label].sort()) - ); + } else if (label) { + return allHosts.filter((host: Host) => host?.labels?.includes(label as string)); } return []; })