Skip to content

feat(scale-up-fabric): support RMS v2 fabric manager configuration#4158

Open
jayzhudev wants to merge 1 commit into
NVIDIA:mainfrom
jayzhudev:feat/nmx-c-configure-v2
Open

feat(scale-up-fabric): support RMS v2 fabric manager configuration#4158
jayzhudev wants to merge 1 commit into
NVIDIA:mainfrom
jayzhudev:feat/nmx-c-configure-v2

Conversation

@jayzhudev

@jayzhudev jayzhudev commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Add support for the librms v0.10.0 ConfigureScaleUpFabricManagerV2 async RPC.

The V1 requires NICo to coordinate multiple RMS RPCs: disable the cluster state across all switches, query switch tray indexes to select a primary, synchronously configure that primary, and then query fabric status separately. V2 moves this coordination into RMS as a scale-up fabric-wide desired-state reconciliation job.

With the V2 RPC, NICo gains a stronger completion contract: RMS selects the primary, reconciles the scale-up fabric, waits for NMX-C to reach configured state, and verifies final switch and topology readback before completing the job. NICo submits the group of switches and desired topology, polls the job, then reads and persists the RMS-selected primary and per-switch Fabric Manager status.

V1 remains the default for compatibility.

To enable V2 under the RMS configuration:

[rms]
scale_up_fabric_manager_api_version = "v2"

If unset, v1 is used.

Related issues

Resolves #1923

Type of Change

  • Add - New feature or capability
  • Change - Changes in existing functionality
  • Fix - Bug fixes
  • Remove - Removed features or deprecated functionality
  • Internal - Internal changes (refactoring, tests, docs, etc.)

Breaking Changes

  • This PR contains breaking changes

V1 remains the default, so existing deployments retain current behavior.

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Manual Testing

Validated against two switches using RMS and the V2 selector.

  • V2 certificate preparation completed for both switches.
  • NICo submitted the asynchronous V2 job and polled it to completion.
  • RMS selected the lowest-tray switch as primary.
  • NICo called GetScaleUpFabricStatus after completion and persisted the observed primary and Fabric Manager status.
  • A second reconciliation completed successfully and retained the same primary.
  • No V1 states or NICo BatchSetScaleUpFabricState calls were observed.

Selected redacted logs:

NICo:
05:39:48 msg="Starting ConfigureNmxCluster"
         next_state=ConfigureCertificatesV2(Start)

05:40:20 msg="ConfigureNmxCluster V2 certificate configuration completed;
              advancing to ConfigureScaleUpFabricManagerV2"

05:40:22 msg="Submitting RMS v2 NMX cluster configuration"
         rack_id=<rack-id> switch_count=2
         topology_type=gb200_nvl36r1_c2g4_topology

05:40:23 msg="V2 ConfigureScaleUpFabricManager submitted;
              waiting for RMS job"
         job_id=<job-id>

05:41:32 msg="ConfigureScaleUpFabricManager job completed;
              verifying observed primary switch"

05:41:34 msg="Verified and persisted RMS v2 fabric status; advancing"
         next_state=Completed
         observed_primary_switch=<primary-switch-id>
         switch_count=2
RMS:
05:40:23 msg="selected primary by lowest tray index"
         primary=<primary-switch-id>

05:40:23 msg="scale-up fabric state already set; skipping nv cli update"
         node=<non-primary-switch-id>
         current_state=disabled desired_state=disabled

05:41:08 msg="NMX topology already configured, skipping"
         current_topology=gb200_nvl36r1_c2g4_topology
         desired_topology=gb200_nvl36r1_c2g4_topology

05:41:09 msg="nmx-controller control plane configured"
         node=<primary-switch-id>
         control_plane_state=CONTROL_PLANE_STATE_CONFIGURED

05:41:33 rpc_method=GetScaleUpFabricStatus
         rpc_grpc_status_code=0

Trimmed completed job response:

{
  "executionState": "JOB_EXECUTION_STATE_COMPLETED",
  "stateDescription": "Completed",
  "result": {
    "topology_type": "gb200_nvl36r1_c2g4_topology",
    "switches": [
      {
        "node_id": "<primary-switch-id>",
        "enabled": true,
        "fabric_manager_status": "ok"
      },
      {
        "node_id": "<non-primary-switch-id>",
        "enabled": false,
        "fabric_manager_status": ""
      }
    ]
  }
}

Persisted NICo state:

<primary-switch-id>
Primary       : Yes
Fabric Manager: running
Info          : CONTROL_PLANE_STATE_CONFIGURED

<non-primary-switch-id>
Primary       : No
Fabric Manager: not_running

Repeat reconciliation evidence:

05:44:01 msg="selected primary by lowest tray index"
         primary=<same-primary-switch-id>

05:44:03 msg="NVLink fabric already matches requested state"
         node=<same-primary-switch-id>

05:44:32 rpc_method=GetScaleUpFabricStatus
         rpc_grpc_status_code=0

Additional notes:

  • The first attempt encountered an RMS HTTP/2 REFUSED_STREAM during final readback. RMS rolled both switches back to disabled; retrying the same desired state passed.

Simulation Testing

Tested scenarios:

  • V2 selector: Configured NICo with scale_up_fabric_manager_api_version = "v2" and verified selection of the V2 workflow.
  • Certificate preparation: Verified NICo configured certificates for every rack switch before V2 submission.
  • Simplified V2 workflow: Verified NICo skipped V1-only BatchSetScaleUpFabricState(false) and device-info preparation calls.
  • V2 request construction: Verified NICo submitted one ConfigureScaleUpFabricManager V2 request containing the selected topology, all rack switches, and no primary-switch override.
  • Asynchronous completion: Verified NICo persisted the RMS job ID, polled GetJobStatus, and continued only after job completion.
  • RMS-owned primary selection: Verified NICo called GetScaleUpFabricStatus after completion, accepted the sole primary selected by RMS, and replaced the previously stored primary.
  • Observed-state persistence: Verified NICo queried Fabric Manager status for all switches and persisted both the RMS-selected primary and per-switch Fabric Manager status.

Signed-off-by: Jay Zhu <jayzhu@nvidia.com>
@jayzhudev jayzhudev self-assigned this Jul 25, 2026
@jayzhudev jayzhudev added the rack lifecycle Issues that relate to managing the lifecycle of a full rack (compute, switches and powershelves) label Jul 25, 2026
@jayzhudev
jayzhudev requested a review from a team as a code owner July 25, 2026 03:38
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

RMS v2 fabric manager workflow

Layer / File(s) Summary
Configuration and maintenance state contract
crates/rack-controller/src/config.rs, crates/api-model/src/rack.rs, crates/api-core/src/cfg/README.md
Adds the configurable V1/V2 API selection, V2 maintenance states, display formatting, deserialization tests, and workflow documentation.
Status observation and retry contracts
crates/rack-controller/src/fabric_manager.rs, crates/rack-controller/src/maintenance.rs
Adds injected RMS clients, primary-switch response classification, and retryability rules for RMS failures.
V2 certificate and fabric pipeline
crates/rack-controller/src/maintenance.rs
Adds V2 certificate submission, complete-rack inventory handling, asynchronous fabric configuration, job polling, verification, persistence, and state-machine routing.
RMS simulation and end-to-end validation
crates/rack/src/rms_client.rs, crates/api-core/src/tests/rack_state_controller/handler.rs
Adds queued and recorded V2 RMS interactions and validates full-rack requests and persisted fabric-manager status in an integration test.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MaintenanceHandler
  participant CertificateWorkflow
  participant FabricWorkflow
  participant RMS
  participant StatusVerification
  MaintenanceHandler->>CertificateWorkflow: submit V2 certificate jobs
  CertificateWorkflow->>FabricWorkflow: transition after completion
  FabricWorkflow->>RMS: submit rack-wide fabric configuration
  RMS-->>FabricWorkflow: return asynchronous job_id
  FabricWorkflow->>RMS: poll job status
  RMS-->>FabricWorkflow: return completion state
  FabricWorkflow->>StatusVerification: verify completed configuration
  StatusVerification->>RMS: retrieve fabric and switch statuses
  RMS-->>StatusVerification: return observed statuses
  StatusVerification-->>MaintenanceHandler: persist status and complete maintenance
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes align with #1923 by moving the NMX configuration sequence into an async RMS V2 API and updating the rack state machine accordingly.
Out of Scope Changes check ✅ Passed The docs, tests, and simulator updates all support the RMS V2 workflow and do not introduce unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed It clearly summarizes the new RMS v2 fabric manager configuration support.
Description check ✅ Passed It describes the V2 async RPC, default V1 behavior, and config enablement.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@jayzhudev jayzhudev changed the title feat(nmx-c): support RMS v2 fabric manager configuration feat(scale-up-fabric): support RMS v2 fabric manager configuration Jul 25, 2026

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (3)
crates/api-core/src/tests/rack_state_controller/handler.rs (1)

3681-3722: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Compare the returned node IDs, not just counts. The node payloads include IDs, so these assertions should compare the sorted IDs from each request against switch_ids; count-only checks still pass for duplicated or wrong switches. Apply the same check to status_nodes and fabric_manager_status_nodes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/api-core/src/tests/rack_state_controller/handler.rs` around lines 3681
- 3722, The assertions in the test should validate node identities rather than
only lengths. In the test block containing configure_nodes, status_nodes, and
fabric_manager_status_nodes, extract each payload’s node IDs, sort them, and
compare them with the sorted switch_ids for all three requests while preserving
the existing request-count assertions.
crates/rack-controller/src/maintenance.rs (1)

1704-1714: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the RMS client-resolution preamble.

This "prefer the NMX-configure client, fall back to the service client" sequence now appears verbatim at lines 1554-1563 and here, alongside the older if let/else variants at lines 536-553, 2698-2714 and 2845-2861. A single helper returning the resolved client (with the Arc clone where the caller must outlive a later &mut ctx borrow, as this call site requires for line 1785) would collapse five near-copies and make the lifetime distinction deliberate rather than incidental.

Entirely optional — the current code is correct as written.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/rack-controller/src/maintenance.rs` around lines 1704 - 1714, Extract
the repeated RMS client-resolution logic into a shared helper, including
NMX-configured client preference and service-client fallback. Update the
variants around this preamble and the older if-let branches to use the helper,
preserving an owned Arc clone where callers such as the current maintenance flow
need the client to outlive a later mutable ctx borrow.
crates/rack-controller/src/fabric_manager.rs (1)

671-733: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Express this as a check_values table rather than a hand-rolled loop.

observe_primary_case already reduces the fallible call to a total PrimaryObservation, so this is a textbook input→output mapping. check_values is imported at line 465 and used by test_fabric_manager_status_from_entry in this same module; aligning keeps scenario reporting uniform across the suite.

As per coding guidelines: "Use table-driven tests for functions mapping inputs to outputs or errors. Use scenarios! with Outcome for fallible operations, value_scenarios! for total operations, and direct check_cases/check_values when macros obscure intent."

♻️ Proposed refactor to the established table-driven form
     #[test]
     fn observed_primary_switch_classifies_status_responses() {
         let second_id = SwitchId::new(SwitchIdSource::Tpm, [2; 32], SwitchType::NvLink).to_string();
 
-        for (scenario, input, expect) in [
-            (
-                "one enabled switch is the observed primary",
-                PrimaryResponseCase::Selected,
-                PrimaryObservation::Primary(second_id),
-            ),
-            (
-                "RMS failure can converge",
-                PrimaryResponseCase::RmsFailure,
-                PrimaryObservation::Retryable,
-            ),
-            (
-                "invalid RMS status violates the contract",
-                PrimaryResponseCase::InvalidStatus,
-                PrimaryObservation::Terminal,
-            ),
-            (
-                "missing fabric status violates the contract",
-                PrimaryResponseCase::MissingFabricStatus,
-                PrimaryObservation::Terminal,
-            ),
-            (
-                "unexpected switch violates the contract",
-                PrimaryResponseCase::UnexpectedSwitch,
-                PrimaryObservation::Terminal,
-            ),
-            (
-                "duplicate switch violates the contract",
-                PrimaryResponseCase::DuplicateSwitch,
-                PrimaryObservation::Terminal,
-            ),
-            (
-                "per-switch inspection failure can recover",
-                PrimaryResponseCase::InspectionFailure,
-                PrimaryObservation::Retryable,
-            ),
-            (
-                "omitted switch violates the contract",
-                PrimaryResponseCase::OmittedSwitch,
-                PrimaryObservation::Terminal,
-            ),
-            (
-                "no enabled switch can converge",
-                PrimaryResponseCase::NoPrimary,
-                PrimaryObservation::Retryable,
-            ),
-            (
-                "multiple enabled switches can converge",
-                PrimaryResponseCase::MultiplePrimaries,
-                PrimaryObservation::Retryable,
-            ),
-            (
-                "invalid primary ID violates the contract",
-                PrimaryResponseCase::InvalidPrimaryId,
-                PrimaryObservation::Terminal,
-            ),
-        ] {
-            assert_eq!(observe_primary_case(input), expect, "{scenario}");
-        }
+        check_values(
+            [
+                Check {
+                    scenario: "one enabled switch is the observed primary",
+                    input: PrimaryResponseCase::Selected,
+                    expect: PrimaryObservation::Primary(second_id),
+                },
+                Check {
+                    scenario: "RMS failure can converge",
+                    input: PrimaryResponseCase::RmsFailure,
+                    expect: PrimaryObservation::Retryable,
+                },
+                Check {
+                    scenario: "invalid RMS status violates the contract",
+                    input: PrimaryResponseCase::InvalidStatus,
+                    expect: PrimaryObservation::Terminal,
+                },
+                Check {
+                    scenario: "missing fabric status violates the contract",
+                    input: PrimaryResponseCase::MissingFabricStatus,
+                    expect: PrimaryObservation::Terminal,
+                },
+                Check {
+                    scenario: "unexpected switch violates the contract",
+                    input: PrimaryResponseCase::UnexpectedSwitch,
+                    expect: PrimaryObservation::Terminal,
+                },
+                Check {
+                    scenario: "duplicate switch violates the contract",
+                    input: PrimaryResponseCase::DuplicateSwitch,
+                    expect: PrimaryObservation::Terminal,
+                },
+                Check {
+                    scenario: "per-switch inspection failure can recover",
+                    input: PrimaryResponseCase::InspectionFailure,
+                    expect: PrimaryObservation::Retryable,
+                },
+                Check {
+                    scenario: "omitted switch violates the contract",
+                    input: PrimaryResponseCase::OmittedSwitch,
+                    expect: PrimaryObservation::Terminal,
+                },
+                Check {
+                    scenario: "no enabled switch can converge",
+                    input: PrimaryResponseCase::NoPrimary,
+                    expect: PrimaryObservation::Retryable,
+                },
+                Check {
+                    scenario: "multiple enabled switches can converge",
+                    input: PrimaryResponseCase::MultiplePrimaries,
+                    expect: PrimaryObservation::Retryable,
+                },
+                Check {
+                    scenario: "invalid primary ID violates the contract",
+                    input: PrimaryResponseCase::InvalidPrimaryId,
+                    expect: PrimaryObservation::Terminal,
+                },
+            ],
+            observe_primary_case,
+        );
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/rack-controller/src/fabric_manager.rs` around lines 671 - 733, Replace
the hand-rolled loop in observed_primary_switch_classifies_status_responses with
the imported check_values table-driven assertion, preserving all existing
scenario inputs, expected PrimaryObservation values, and scenario labels. Keep
the test focused on the total observe_primary_case input-to-output mapping and
follow the check_values usage established by
test_fabric_manager_status_from_entry.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/api-core/src/tests/rack_state_controller/handler.rs`:
- Around line 3681-3722: The assertions in the test should validate node
identities rather than only lengths. In the test block containing
configure_nodes, status_nodes, and fabric_manager_status_nodes, extract each
payload’s node IDs, sort them, and compare them with the sorted switch_ids for
all three requests while preserving the existing request-count assertions.

In `@crates/rack-controller/src/fabric_manager.rs`:
- Around line 671-733: Replace the hand-rolled loop in
observed_primary_switch_classifies_status_responses with the imported
check_values table-driven assertion, preserving all existing scenario inputs,
expected PrimaryObservation values, and scenario labels. Keep the test focused
on the total observe_primary_case input-to-output mapping and follow the
check_values usage established by test_fabric_manager_status_from_entry.

In `@crates/rack-controller/src/maintenance.rs`:
- Around line 1704-1714: Extract the repeated RMS client-resolution logic into a
shared helper, including NMX-configured client preference and service-client
fallback. Update the variants around this preamble and the older if-let branches
to use the helper, preserving an owned Arc clone where callers such as the
current maintenance flow need the client to outlive a later mutable ctx borrow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 458ee8ce-7f6e-46a3-ab4b-eaef103da77f

📥 Commits

Reviewing files that changed from the base of the PR and between 7f3f044 and dd153de.

📒 Files selected for processing (7)
  • crates/api-core/src/cfg/README.md
  • crates/api-core/src/tests/rack_state_controller/handler.rs
  • crates/api-model/src/rack.rs
  • crates/rack-controller/src/config.rs
  • crates/rack-controller/src/fabric_manager.rs
  • crates/rack-controller/src/maintenance.rs
  • crates/rack/src/rms_client.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rack lifecycle Issues that relate to managing the lifecycle of a full rack (compute, switches and powershelves)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: move nmxconfiguration sequence from NICo to RMS

1 participant