Skip to content

feat(topology): support variable network fabric tiers#411

Merged
dmitsh merged 2 commits into
mainfrom
ds-tiers
Jul 21, 2026
Merged

feat(topology): support variable network fabric tiers#411
dmitsh merged 2 commits into
mainfrom
ds-tiers

Conversation

@dmitsh

@dmitsh dmitsh commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Replace the fixed leaf, spine, and core topology model with variable-depth, closest-first fabric tiers.
This change:
Replaces ToThreeTierGraph with arbitrary-depth ToGraph.

Represents per-instance fabric topology using:

  • FabricTiers []FabricTier

Uses tier 0 for the switch closest to the compute node, increasing outward.

Publishes Kubernetes topology labels as:

  • network.topology.nvidia.com/tier-N
  • network.topology.nvidia.com/accelerator

Updates the Kubernetes and NFD engines to emit any number of discovered fabric tiers.

Retains the single AcceleratorID and Graph.Domains representation for Slurm block-topology compatibility.

Configures label names through Kubernetes engine parameters:

  • fabricLabels
  • acceleratorLabel

Allows users to reproduce the previous accelerator, leaf, spine, and core label set through custom label configuration.

Prevents mixed-depth paths sharing an outer switch ID from overwriting one another.

Updates providers, simulation models, documentation, Helm configuration, tests, and release notes.

This is a breaking change for consumers using the previous fixed leaf, spine, and core Kubernetes label keys or the process-wide Helm/CLI label overrides.

Addresses #412
Fixes #408

Signed-off-by: Dmitry Shmulevich <dshmulevich@nvidia.com>
@dmitsh
dmitsh requested a review from ravisoundar as a code owner July 19, 2026 11:10
@dmitsh
dmitsh requested a review from ArangoGutierrez July 19, 2026 11:11
@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.71595% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.36%. Comparing base (8468abe) to head (ad51015).
⚠️ Report is 21 commits behind head on main.

Files with missing lines Patch % Lines
pkg/topology/graph.go 91.13% 4 Missing and 3 partials ⚠️
pkg/engines/k8s/kubernetes.go 82.14% 3 Missing and 2 partials ⚠️
pkg/translate/topology.go 55.55% 2 Missing and 2 partials ⚠️
pkg/engines/nfd/objects.go 92.85% 1 Missing and 1 partial ⚠️
pkg/providers/oci/provider_imds.go 0.00% 2 Missing ⚠️
pkg/engines/k8s/engine.go 75.00% 1 Missing ⚠️
pkg/engines/k8s/labeler.go 97.43% 0 Missing and 1 partial ⚠️
pkg/engines/slinky/engine.go 50.00% 1 Missing ⚠️
pkg/providers/aws/provider.go 0.00% 1 Missing ⚠️
pkg/providers/dsx/provider.go 0.00% 1 Missing ⚠️
... and 4 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #411      +/-   ##
==========================================
+ Coverage   72.15%   73.36%   +1.20%     
==========================================
  Files          89       94       +5     
  Lines        5689     6269     +580     
==========================================
+ Hits         4105     4599     +494     
- Misses       1382     1429      +47     
- Partials      202      241      +39     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread pkg/providers/dsx/instance_topology.go
Comment thread pkg/topology/graph.go Outdated
Comment thread pkg/engines/k8s/kubernetes.go Outdated
@NVIDIA NVIDIA deleted a comment from greptile-apps Bot Jul 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@dmitsh
dmitsh force-pushed the ds-tiers branch 2 times, most recently from 9976bcd to 9464cd1 Compare July 19, 2026 12:34
@dmitsh
dmitsh requested a review from resker July 19, 2026 15:06
@dmitsh
dmitsh force-pushed the ds-tiers branch 7 times, most recently from 6ee51c0 to 7f0812e Compare July 20, 2026 04:41
Comment thread pkg/providers/nebius/instance_topology.go
@dmitsh
dmitsh force-pushed the ds-tiers branch 3 times, most recently from 33b2487 to 6f4ba01 Compare July 20, 2026 12:26
Comment thread docs/design/nfd-engine-sdd.md Outdated

@ArangoGutierrez ArangoGutierrez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 5 issues.

  1. isManagedLevelLabel only recognizes the configured keys and the new tier-N/domain-N prefixes, so the retired accelerator/leaf/spine/core labels are never removed from nodes labeled by a pre-upgrade Topograph -- the legacy-core case in kubernetes_test.go pins that behavior in. Since KAI Scheduler, NVSentinel, and Kueue consume those keys, frozen pre-upgrade values could silently mislead them; special-casing the four legacy defaults for removal (or documenting manual cleanup in the changelog) would close the gap. (pkg/engines/k8s/kubernetes.go:67)
  2. Hardcoding NewTopologyLabelKeys(nil, nil) drops the operator key customization the NFD engine honored via CurrentTopologyLabelKeys() until #402, and nfd Params has no fabricLabels/acceleratedLabels equivalent -- customized keys silently revert to defaults after upgrade. Plumbing the same params through the nfd engine (or calling the loss out in the changelog) would make the break explicit. (pkg/engines/nfd/engine.go:124)
  3. When a node carries both a provider-derived accelerated-domain-0 value and a differing nvidia.com/gpu.clique label, the first pass has already recorded the provider value in groupValues and this clique branch only overwrites elements -- producing a NodeFeatureGroup that no NodeFeature ever matches, which cleanupResource never removes because its name stays in objectNames. Resolving the clique override before recording groupValues (as the pre-refactor single pass did) avoids the dangling CR. (pkg/engines/nfd/objects.go:104)
  4. This reshapes the canonical Graph (Domains -> AcceleratedDomains) and retires the four fixed label keys, but no issue is linked from the PR; the contribution docs gate pkg/topology changes on prior issue discussion. Linking the discussion (or opening one) would satisfy that gate. (pkg/topology/topology.go:64)
  5. The label families this PR adds are tier-N and domain-N; there is no level-N label, so following this line to grep node labels matches nothing. Same for the accelerated level-0 phrasing here. (docs/providers/lambdai.md:183)

Comment thread pkg/engines/k8s/kubernetes.go
Comment thread pkg/engines/nfd/engine.go Outdated
Comment thread pkg/engines/nfd/objects.go Outdated
Comment thread pkg/topology/topology.go Outdated
Comment thread docs/providers/lambdai.md Outdated
Comment thread pkg/engines/k8s/labeler.go
@dmitsh dmitsh changed the title feat(topology): support variable fabric and accelerated tiers feat(topology): support variable network fabric tiers Jul 21, 2026
@NVIDIA NVIDIA deleted a comment from greptile-apps Bot Jul 21, 2026
@dmitsh
dmitsh force-pushed the ds-tiers branch 2 times, most recently from e84c7ca to 1d01174 Compare July 21, 2026 05:43
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the fixed three-tier (leaf/spine/core) fabric topology model with a variable-depth, closest-first tier system (FabricTiers []FabricTier), and transitions Kubernetes node labels from hard-coded leaf/spine/core keys to network.topology.nvidia.com/tier-N (or user-configured fabricLabels). It is a documented breaking change.

  • Core data model: InstanceTopology now carries []FabricTier (closest-first) in place of three named fields; ClusterTopology.ToThreeTierGraph is renamed ToGraph and uses per-level tierKey to keep same-ID switches at different depths distinct, with mergeVertices to safely merge mixed-depth paths that share an outermost switch ID.
  • K8s/NFD engines: TopologyLabelKeys is now per-engine instance state rather than a process-wide global; the NFD buildNFDObjects function infers topology kind from the default label prefix instead of a fixed four-element list; MergeNodeLabels gains a removeManagedTopologyLabels pass that strips default tier-N labels and any user-configured fabric/accelerator keys before writing new labels.
  • All previously raised issues (empty-node-name guard in getTierLabels/getDomainLabels, nil-graph panic in TopologyBlock translate, unconditional Domains assignment in model simulation, forest collision overwriting nodes with shared outermost IDs) have been addressed.

Confidence Score: 5/5

Safe to merge. All previously raised blocking issues have been addressed, and the new variable-tier logic is well-tested with mixed-depth and shared-root scenarios.

Every issue flagged in prior review rounds is resolved: the empty-node-name guard is restored in both getTierLabels and getDomainLabels; the nil-graph panic in TopologyBlock translate is fixed and covered by new test cases; model.go only assigns Domains when non-empty; forest collision for mixed-depth paths is handled by the level-keyed nodes map plus mergeVertices. The new TopologyLabelKeys is per-engine-instance rather than a process-wide global, and is validated at startup. Test coverage was substantially extended for all changed paths.

No files require special attention. The intentional non-removal of legacy leaf/spine/core Kubernetes labels during upgrades is confirmed by the test expectations and acknowledged as a documented breaking change.

Important Files Changed

Filename Overview
pkg/topology/graph.go Core topology restructuring: InstanceTopology gains []FabricTier (replacing LeafID/SpineID/CoreID), ToThreeTierGraph renamed ToGraph with level-keyed nodes map and mergeVertices to handle mixed-depth shared roots correctly.
pkg/engines/k8s/labeler.go TopologyLabelKeys refactored from global state to per-instance struct; empty-nodeName guards added to both getDomainLabels and getTierLabels; FabricKey stops at first unconfigured custom tier.
pkg/engines/k8s/kubernetes.go MergeNodeLabels now accepts keys and calls removeManagedTopologyLabels before writing new labels; isManagedLevelLabel recognises both default tier-N prefix and configured custom keys.
pkg/translate/topology.go initBlocks nil-graph checks split into two guards; Validate() still checks graph.Domains == nil for TopologyBlock; tests added for nil-graph in both cluster-wide and per-partition block config paths.
pkg/engines/nfd/objects.go buildNFDObjects now derives topology kind from default label prefix via topologyKind(); NFD NodeFeatureGroup ordering changed to alphabetical; old leaf/spine/core NFD objects become stale and will be deleted on first run.
pkg/models/model.go Domains only assigned to graph when non-empty, matching ClusterTopology.ToGraph; getInstanceID helper extracted.
pkg/engines/k8s/engine.go Params gains FabricLabels and AcceleratorLabel; labelKeys built and validated at engine init time; global InitLabels/CLI flags removed.
pkg/topology/topology.go Adds KeyFabricTierPrefix constant and FabricTierKey helper; removes old leaf/spine/core label key constants.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Provider API] -->|FabricTiers slice| B[InstanceTopology]
    B --> C[ClusterTopology.ToGraph]
    C -->|level-keyed nodes map| D[forest map]
    D -->|mergeVertices for shared roots| E[topology.Graph]

    E --> F{Engine}
    F --> G[K8s Engine]
    F --> H[NFD Engine]

    G --> G1[BuildNodeLabels with configured keys]
    G1 --> G2[MergeNodeLabels - removeManagedTopologyLabels]
    G2 --> G3[K8s Node Labels tier-0 ... tier-N]

    H --> H1[BuildNodeLabels with default keys]
    H1 --> H2[buildNFDObjects - topologyKind]
    H2 --> H3[NFD NodeFeature and NodeFeatureGroup]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Provider API] -->|FabricTiers slice| B[InstanceTopology]
    B --> C[ClusterTopology.ToGraph]
    C -->|level-keyed nodes map| D[forest map]
    D -->|mergeVertices for shared roots| E[topology.Graph]

    E --> F{Engine}
    F --> G[K8s Engine]
    F --> H[NFD Engine]

    G --> G1[BuildNodeLabels with configured keys]
    G1 --> G2[MergeNodeLabels - removeManagedTopologyLabels]
    G2 --> G3[K8s Node Labels tier-0 ... tier-N]

    H --> H1[BuildNodeLabels with default keys]
    H1 --> H2[buildNFDObjects - topologyKind]
    H2 --> H3[NFD NodeFeature and NodeFeatureGroup]
Loading

Reviews (16): Last reviewed commit: "feat(topology): support variable network..." | Re-trigger Greptile

Comment thread pkg/translate/topology.go
Comment on lines 69 to 72
case topology.TopologyBlock:
if graph == nil || graph.Domains == nil {
if graph.Domains == nil {
return fmt.Errorf("missing block topology for topology %q", topo)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Nil graph panics instead of returning an error for TopologyBlock

The graph == nil guard was removed from the TopologyBlock branch of Validate (both the per-partition path at ~line 69 and the cluster-wide path at ~line 88), but it was kept for TopologyTree. If a caller passes graph = nil with a block-topology config, graph.Domains dereferences a nil pointer and panics at runtime. The old code returned a clean "missing block topology" error in this case.

The guard needs to be restored to match the TopologyTree treatment: if graph == nil || graph.Domains == nil.

@dmitsh
dmitsh force-pushed the ds-tiers branch 2 times, most recently from 9878c1b to ce6885a Compare July 21, 2026 08:36

@ArangoGutierrez ArangoGutierrez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 4 issues.

  1. When every member of an accelerator domain carries gpu.clique, the provider value recorded into groupValues in the first pass is never removed, so a NodeFeatureGroup gets emitted that no NodeFeature publishes and cleanup keeps it forever. Mirroring the k8s engine's skip-when-clique guard would close it - same point as my comment on the previous revision. (pkg/engines/nfd/objects.go:110)
  2. Legacy leaf/spine/core keys aren't recognized here, so upgraded nodes keep them frozen alongside the new tier-N labels - consumers still keyed on the old names read stale topology, and Case 4 in the test pins that behavior. Configurability covers new deployments, not values already on nodes; either strip the three well-known legacy keys during relabel or add a manual-cleanup step to the CHANGELOG migration note. (pkg/engines/k8s/kubernetes.go:67)
  3. Swapping this case away from acceleratorLabel == nvidia.com/gpu.clique removes the only coverage of the clique-preservation branches; either guard could now be deleted without a failing test, and the config is operator-reachable. Worth keeping the custom-label case and re-adding a clique case alongside. (pkg/engines/k8s/kubernetes_test.go:134)
  4. Still hardcoded to the defaults, so the new fabricLabels/acceleratorLabel customization stops at the k8s engine - NFD operators can't use it and the limitation isn't documented. Plumbing the params through or a line in the CHANGELOG would both settle it; re-raising from the previous revision. (pkg/engines/nfd/engine.go:124)

Comment thread pkg/engines/nfd/objects.go
Comment thread pkg/engines/k8s/kubernetes.go
Comment thread pkg/engines/k8s/kubernetes_test.go
Comment thread pkg/engines/nfd/engine.go
Comment thread pkg/models/model.go Outdated
Signed-off-by: Dmitry Shmulevich <dshmulevich@nvidia.com>
@dmitsh
dmitsh merged commit 71314a4 into main Jul 21, 2026
12 checks passed
@dmitsh
dmitsh deleted the ds-tiers branch July 21, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: Kubernetes engine leaves stale higher-tier topology labels when the network hierarchy shrinks

2 participants