Skip to content

feat(power-control): route standalone vs rack-scale machines separately#3873

Merged
spydaNVIDIA merged 3 commits into
NVIDIA:mainfrom
spydaNVIDIA:component_manager
Jul 23, 2026
Merged

feat(power-control): route standalone vs rack-scale machines separately#3873
spydaNVIDIA merged 3 commits into
NVIDIA:mainfrom
spydaNVIDIA:component_manager

Conversation

@spydaNVIDIA

Copy link
Copy Markdown
Contributor

Compute-tray power control called cm.compute_tray.power_control for all machines regardless of type. On an RMS-backed deployment this fails for standalone (non-rack-scale) servers, since RMS cannot resolve them.

Partition requested machines the same way compute firmware updates do (is_mnnvl_capable):

  • Standalone servers: always dispatched synchronously through NICo-core's Redfish stack (CoreComputeTrayManager over api.redfish_pool), never the state machine, which has no non-rack path.
  • Rack-scale servers: state-controller maintenance flow when enabled, else a synchronous dispatch through the configured backend (RMS).

Related issues

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

Testing

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

Additional Notes

@spydaNVIDIA
spydaNVIDIA requested a review from a team as a code owner July 22, 2026 22:44
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Summary by CodeRabbit

  • Bug Fixes
    • Improved power control for both rack-scale and standalone targets with more reliable per-device outcomes.
    • Power option updates now treat “already set” as a successful state to reduce false failures.
    • Maintenance transitions are no longer preempted by power-manager handling when a maintenance request is pending.
  • Tests
    • Added unit tests for rack-scale classification and per-machine error result generation.
    • Added integration coverage for queued maintenance execution with the power manager enabled.

Walkthrough

The component manager now classifies machine power-control requests, persists desired state before dispatch, routes rack-scale and standalone systems through distinct control paths, and maps failures per machine. Machine-state reconciliation defers power handling for pending maintenance, with integration coverage for queued power-off execution.

Changes

Compute-tray power-control routing

Layer / File(s) Summary
Machine classification helpers
crates/api-core/src/handlers/component_manager.rs
Machine loading and rack-scale classification are separated into helpers that preserve per-machine not-found errors, with unit tests covering classification and partitioned error results.
Power-control routing and dispatch
crates/api-core/src/handlers/component_manager.rs, crates/machine-controller/src/handler.rs
Power control persists each desired option before dispatch, routes rack-scale machines through maintenance or compute-tray control, routes standalone machines through CoreComputeTrayManager, maps backend and tonic errors per machine, and defers power handling when maintenance is pending.
Maintenance reconciliation coverage
crates/machine-controller/tests/integration/maintenance.rs, crates/machine-controller/tests/integration/power_management.rs
Test helpers expose the reconciliation backend and environment wiring, while integration coverage verifies queued PowerOff maintenance transitions to maintenance and then returns to ready.

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

Sequence Diagram(s)

sequenceDiagram
  participant ComponentManager
  participant MachineStateHandler
  participant StateController
  participant ComputeTrayManager
  participant SiteExploration
  ComponentManager->>ComponentManager: Classify machines and persist desired power options
  ComponentManager->>StateController: Route eligible rack-scale maintenance requests
  ComponentManager->>ComputeTrayManager: Dispatch synchronous rack-scale or standalone power control
  ComputeTrayManager-->>ComponentManager: Return per-machine results and dispatched BMC IPs
  ComponentManager->>SiteExploration: Re-explore dispatched BMC IPs
  MachineStateHandler->>MachineStateHandler: Defer power handling when maintenance is pending
  MachineStateHandler->>StateController: Reconcile queued PowerOff maintenance
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: power control now routes standalone and rack-scale machines separately.
Description check ✅ Passed The description is clearly related to the refactor and explains the new standalone versus rack-scale routing behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@spydaNVIDIA

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review please

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@spydaNVIDIA: Starting a complete review of all changes in #3873, with emphasis on routing correctness, failure handling, test coverage, and maintainability.

✅ Action performed

Full review finished.

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/api-core/src/handlers/component_manager.rs (1)

1645-1646: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Optional: the same machine set is fetched from the database multiple times.

partition_compute_machines_by_rack_scale performs a db::machine::find here, and each subsequent dispatch_compute_tray_power_control call re-queries the very same rows via resolve_compute_tray_endpoints. For large targets this is up to three redundant round trips. Consider fetching once and threading the resolved Machine records (or endpoints) through the partition and dispatch steps.

🤖 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/handlers/component_manager.rs` around lines 1645 - 1646,
Optimize the flow around partition_compute_machines_by_rack_scale and
dispatch_compute_tray_power_control by fetching the target machines once and
reusing the resolved Machine records or endpoints throughout partitioning and
dispatch. Thread the fetched data through the relevant calls, including
resolve_compute_tray_endpoints, while preserving the existing rack-scale and
standalone grouping behavior.
🤖 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.

Inline comments:
In `@crates/api-core/src/handlers/component_manager.rs`:
- Around line 1660-1740: Track machine IDs that enter the final error arm of
update_power_option in a failed-ID set, while still appending their
error_result. Before the rack-scale and standalone dispatch blocks, filter
rack_scale_ids and standalone_ids to exclude those failed IDs, preserving
successful machines and ensuring each failed machine produces only its recorded
error result.

---

Nitpick comments:
In `@crates/api-core/src/handlers/component_manager.rs`:
- Around line 1645-1646: Optimize the flow around
partition_compute_machines_by_rack_scale and dispatch_compute_tray_power_control
by fetching the target machines once and reusing the resolved Machine records or
endpoints throughout partitioning and dispatch. Thread the fetched data through
the relevant calls, including resolve_compute_tray_endpoints, while preserving
the existing rack-scale and standalone grouping behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6d9fcb09-6cbb-4d42-aa95-2efac183ff2c

📥 Commits

Reviewing files that changed from the base of the PR and between b8c30d3 and d35aedf.

📒 Files selected for processing (1)
  • crates/api-core/src/handlers/component_manager.rs

Comment thread crates/api-core/src/handlers/component_manager.rs

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

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@crates/api-core/src/handlers/component_manager.rs`:
- Around line 1703-1740: Update the rack and standalone dispatch handling around
queue_machine_power_control_via_state_controller and
dispatch_compute_tray_power_control so an Err from either partition is converted
into per-machine error_result entries rather than propagated with ?. Preserve
results already collected from the other partition, and ensure every machine in
the failed partition is represented in the response while successful partition
results remain intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ee48b4ca-f3aa-4a1c-867c-de21df6bb971

📥 Commits

Reviewing files that changed from the base of the PR and between b8c30d3 and d35aedf.

📒 Files selected for processing (1)
  • crates/api-core/src/handlers/component_manager.rs

Comment thread crates/api-core/src/handlers/component_manager.rs

@kunzhao-nv kunzhao-nv 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.

Better add unit tests for cases:

  • rack scale/ standalone/ mix
  • compute_tray_use_state_controller on/off
  • machine failed in update_power_option should not be dispatched
  • unknown machine id handling

// driven synchronously through NICo-core's Redfish stack, because
// RMS cannot power-control them.
let (rack_scale_ids, standalone_ids) =
partition_compute_machines_by_rack_scale(api, &list.machine_ids).await?;

@kunzhao-nv kunzhao-nv Jul 22, 2026

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.

Before, unknown id will go through unresolved branch in resolve_compute_tray_endpoints and generate one error_result, while the rest machines will continue, which could lead to a partial success; after, with partition_compute_machines_by_rack_scale, a single bad id will fail entire batch. Please confirm this patten change is as expected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

Comment thread crates/api-core/src/handlers/component_manager.rs

@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 (1)
crates/api-core/src/handlers/component_manager.rs (1)

1687-1747: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Sequential per-machine persistence loop.

Each machine in the request is processed strictly sequentially: health-override insert (DB write), update_power_option (its own transaction), then health-override removal (another DB write). For larger machine_ids batches this compounds into serialized round trips before any dispatch begins, even though the subsequent dispatch to rack-scale/standalone backends is already batched. Consider bounding concurrency for this pre-dispatch loop (e.g. futures::stream::iter(...).for_each_concurrent(N, ...)) if large batches are a realistic use case for this endpoint.

🤖 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/handlers/component_manager.rs` around lines 1687 - 1747,
Bound concurrency for the per-machine persistence work in the loop handling
list.machine_ids, using the project’s established concurrency pattern and an
appropriate limit. Preserve per-machine error results, health-override cleanup,
power-option idempotency handling, and classification into rack_scale_ids or
standalone_ids before dispatch.
🤖 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/handlers/component_manager.rs`:
- Around line 1687-1747: Bound concurrency for the per-machine persistence work
in the loop handling list.machine_ids, using the project’s established
concurrency pattern and an appropriate limit. Preserve per-machine error
results, health-override cleanup, power-option idempotency handling, and
classification into rack_scale_ids or standalone_ids before dispatch.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2828346a-3c45-49eb-80cc-5963b173cedb

📥 Commits

Reviewing files that changed from the base of the PR and between d35aedf and 94e203e.

📒 Files selected for processing (1)
  • crates/api-core/src/handlers/component_manager.rs

@spydaNVIDIA

Copy link
Copy Markdown
Contributor Author

Better add unit tests for cases:

  • rack scale/ standalone/ mix
  • compute_tray_use_state_controller on/off
  • machine failed in update_power_option should not be dispatched
  • unknown machine id handling

I added test cases that should cover the changes introduced in this MR

@spydaNVIDIA
spydaNVIDIA enabled auto-merge (squash) July 23, 2026 19:34
@github-actions

Copy link
Copy Markdown

machine_id: Some(machine_id),
power_state: desired_state,
};
let power_option_ok = match crate::handlers::power_options::update_power_option(

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.

Setting desired_power_state = Off before queuing the state-controller operation can prevent that operation from ever running when the power manager is enabled.

For a Ready host, MachineStateHandler runs handle_power() before attempt_state_transition(). The desired-Off path always returns continue_state_machine = false, so attempt_state_transition() never reaches maintenance_transition_if_requested(). The host remains physically on while the queued PowerOff maintenance request is never consumed.

The synchronous path is safe because it dispatches the physical power action immediately after updating the desired state, but the state-controller path is asynchronous. I think we should change the ordering/precedence so a queued maintenance request can run, and add a test covering:

  • compute_tray_use_state_controller = true
  • power_manager_options.enabled = true
  • PowerOff / ForceOff

One possible fix is to make pending machine maintenance take precedence over power-manager handling for Ready hosts; alternatively, avoid persisting desired Off until the queued maintenance operation can actually execute.

let is_rack_scale = match machine_is_rack_scale(&machines_by_id, machine_id) {
Ok(v) => v,
Err(status) => {
results.push(error_result(

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.

machine_is_rack_scale() deliberately returns Status::not_found, but this converts it to ComponentManagerStatusCode::InternalError.

Should we use the existing status_result(&machine_id.to_string(), status) helper so the per-machine response preserves NotFound. The new helper test currently verifies the intermediate Status, but not the status returned by ComponentPowerControlResponse.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/machine-controller/src/handler.rs (1)

9935-9972: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Use dpu_machine_id consistently in the DPU restart logs. handler_restart_dpu already uses dpu_machine_id; the machine_id field in restart_dpu makes correlation harder.

🤖 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/machine-controller/src/handler.rs` around lines 9935 - 9972, Update
the logging inside restart_dpu to use the dpu_machine_id field name consistently
with handler_restart_dpu, while preserving the existing machine identifier value
and log behavior.

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.

Outside diff comments:
In `@crates/machine-controller/src/handler.rs`:
- Around line 9935-9972: Update the logging inside restart_dpu to use the
dpu_machine_id field name consistently with handler_restart_dpu, while
preserving the existing machine identifier value and log behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 64498049-2af2-4221-bb24-77ad387f338d

📥 Commits

Reviewing files that changed from the base of the PR and between 7a04b49 and 48b80ec.

📒 Files selected for processing (4)
  • crates/api-core/src/handlers/component_manager.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/tests/integration/maintenance.rs
  • crates/machine-controller/tests/integration/power_management.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/api-core/src/handlers/component_manager.rs

@spydaNVIDIA
spydaNVIDIA merged commit 80d0b8b into NVIDIA:main Jul 23, 2026
62 checks passed
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.

3 participants