Skip to content

feat: route compute-tray power control through machine state controller#3683

Open
vinodchitraliNVIDIA wants to merge 1 commit into
NVIDIA:mainfrom
vinodchitraliNVIDIA:vc/ps
Open

feat: route compute-tray power control through machine state controller#3683
vinodchitraliNVIDIA wants to merge 1 commit into
NVIDIA:mainfrom
vinodchitraliNVIDIA:vc/ps

Conversation

@vinodchitraliNVIDIA

Copy link
Copy Markdown
Contributor

Add machine maintenance power operations mirroring the existing switch and power-shelf state-controller maintenance pattern. When compute_tray_use_state_controller is enabled, ComponentPowerControl for machines queues a maintenance request instead of issuing Redfish directly.

Model and persistence:

  • Introduce MachineMaintenanceOperation (PowerOn, PowerOff, Reset) and MachineMaintenanceRequest.
  • Add ManagedHostState::Maintenance to execute queued operations.
  • Add machines.machine_maintenance_requested JSONB column and DB helpers to set/clear the pending request.

Machine state handler:

  • New maintenance handler dispatches power actions via compute_tray backend.
  • Ready and Failed states transition into Maintenance when a request is pending; success returns to Ready, failure clears the request and moves to Failed to avoid retry loops.
  • Wire component_manager and credential_manager into machine controller services for endpoint/credential resolution.

Component manager and API:

  • Implement request_machine_maintenance_via_state_controller.
  • Replace the unimplemented compute-tray state-controller path in component_power_control with the maintenance queue.
  • Map PowerAction to MachineMaintenanceOperation consistently with switches.

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

@vinodchitraliNVIDIA
vinodchitraliNVIDIA requested a review from a team as a code owner July 17, 2026 18:30
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c2f35a7e-ee1b-4b83-bcf0-8a64e53d4319

📥 Commits

Reviewing files that changed from the base of the PR and between bd7731d and ba3a1d1.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (16)
  • crates/api-core/src/handlers/component_manager.rs
  • crates/api-core/src/setup.rs
  • crates/api-core/src/tests/common/api_fixtures/mod.rs
  • crates/api-db/migrations/20260717120000_machine_maintenance_requested.sql
  • crates/api-db/src/machine.rs
  • crates/api-model/src/machine/json.rs
  • crates/api-model/src/machine/mod.rs
  • crates/api-model/src/machine/slas.rs
  • crates/api-model/src/test_support/machine_snapshot.rs
  • crates/component-manager/src/component_manager.rs
  • crates/machine-controller/Cargo.toml
  • crates/machine-controller/src/context.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/src/handler/maintenance.rs
  • crates/machine-controller/src/io.rs
  • crates/machine-controller/tests/integration/env.rs
🚧 Files skipped from review as they are similar to previous changes (15)
  • crates/api-model/src/test_support/machine_snapshot.rs
  • crates/machine-controller/Cargo.toml
  • crates/api-model/src/machine/slas.rs
  • crates/api-core/src/tests/common/api_fixtures/mod.rs
  • crates/api-core/src/setup.rs
  • crates/machine-controller/src/io.rs
  • crates/component-manager/src/component_manager.rs
  • crates/machine-controller/src/context.rs
  • crates/api-model/src/machine/mod.rs
  • crates/machine-controller/tests/integration/env.rs
  • crates/api-model/src/machine/json.rs
  • crates/api-db/src/machine.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/src/handler/maintenance.rs
  • crates/api-core/src/handlers/component_manager.rs

Summary by CodeRabbit

  • New Features
    • Added machine maintenance workflows for power on, power off, and reset via a dedicated maintenance state.
    • Maintenance requests are stored with requested time, initiator, and operation, and reflected in machine state rendering and metrics (including a maintenance SLA window).
    • Machine power-control requests can now be routed through the state controller when enabled.
  • Bug Fixes
    • Replaced the previous unsupported response for eligible machine power-control requests with queued maintenance processing.
    • Machines now clear requests and transition to Ready on success, or Failed with captured error details on failure.

Walkthrough

Machine maintenance requests are persisted on machines, exposed through the component manager, and executed by the machine state controller as compute-tray power operations. The workflow adds maintenance state modeling, credential lookup, success/failure transitions, metrics, and service wiring.

Changes

Machine maintenance workflow

Layer / File(s) Summary
Maintenance contracts and persistence
crates/api-model/src/machine/*, crates/api-db/src/machine.rs, crates/api-db/migrations/*, crates/api-model/src/test_support/*
Adds maintenance operations, request payloads, the Maintenance host state, a five-minute SLA, JSON mapping, and database helpers for setting and clearing requests.
Maintenance request API
crates/component-manager/src/component_manager.rs, crates/api-core/src/handlers/component_manager.rs
Validates requested machines, records eligible maintenance operations, maps power actions, and routes machine power control through the state controller.
State-controller maintenance execution
crates/machine-controller/src/context.rs, crates/machine-controller/src/handler.rs, crates/machine-controller/src/handler/maintenance.rs, crates/machine-controller/src/io.rs
Detects pending requests, executes compute-tray power operations using BMC credentials, clears requests, and transitions machines to Ready or Failed with maintenance metrics.
Controller service wiring and fixtures
crates/api-core/src/setup.rs, crates/api-core/src/tests/common/api_fixtures/mod.rs, crates/machine-controller/tests/integration/env.rs, crates/machine-controller/Cargo.toml
Wires component and credential managers into machine state services and updates test initialization for the new dependency fields.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ComponentManager
  participant StateController
  participant CredentialManager
  participant ComputeTray
  participant Database
  Client->>ComponentManager: request machine power action
  ComponentManager->>Database: persist maintenance request
  StateController->>Database: read pending request
  StateController->>CredentialManager: fetch BMC credentials
  StateController->>ComputeTray: execute power operation
  ComputeTray-->>StateController: operation result
  StateController->>Database: clear request and persist Ready or Failed
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: routing compute-tray power control through the machine state controller.
Description check ✅ Passed The description accurately describes the maintenance workflow, persistence, and controller wiring changes in the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 82.61% 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.

@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: 3

🧹 Nitpick comments (2)
crates/machine-controller/src/handler/maintenance.rs (1)

64-118: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the maintenance operation typed through dispatch.

Passing "PowerOn", "PowerOff", and "Reset" separately from PowerAction permits labels and actions to diverge. Pass MachineMaintenanceOperation into the common driver and derive both representations from that enum.

As per coding guidelines, “Avoid stringly typed values in Rust. For finite sets of possibilities, use enums or structs of enums and derive their string representation with Display/FromStr.”

🤖 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/maintenance.rs` around lines 64 - 118,
Update handle_power_on, handle_power_off, handle_reset, and
invoke_power_operation to accept a MachineMaintenanceOperation instead of
separate PowerAction and operation_label arguments. Derive the corresponding
PowerAction and display/log representation from that operation enum within the
common driver, removing duplicated string labels and preventing action-label
mismatches.

Source: Coding guidelines

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

3363-3385: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add test coverage for GracefulRestart and AcPowercycle variants.

The test covers 4 of 6 PowerAction variants but omits GracefulRestart and AcPowercycle, both of which map to MachineMaintenanceOperation::Reset. As per coding guidelines, prefer table-driven tests for input-variant coverage on conversion functions. Consider using carbide-test-support value_scenarios! or check_values to cover all variants concisely.

♻️ Proposed test using check_values for all variants
 #[test]
 fn map_machine_maintenance_operation_variants() {
     use model::machine::MachineMaintenanceOperation;

     use super::map_machine_maintenance_operation;

-    assert_eq!(
-        map_machine_maintenance_operation(PowerAction::On),
-        MachineMaintenanceOperation::PowerOn,
-    );
-    assert_eq!(
-        map_machine_maintenance_operation(PowerAction::ForceOff),
-        MachineMaintenanceOperation::PowerOff,
-    );
-    assert_eq!(
-        map_machine_maintenance_operation(PowerAction::GracefulShutdown),
-        MachineMaintenanceOperation::PowerOff,
-    );
-    assert_eq!(
-        map_machine_maintenance_operation(PowerAction::ForceRestart),
-        MachineMaintenanceOperation::Reset,
-    );
+    carbide_test_support::value_scenarios!(|action| map_machine_maintenance_operation(action);
+        "PowerAction maps to MachineMaintenanceOperation" {
+            PowerAction::On => MachineMaintenanceOperation::PowerOn,
+            PowerAction::ForceOff => MachineMaintenanceOperation::PowerOff,
+            PowerAction::GracefulShutdown => MachineMaintenanceOperation::PowerOff,
+            PowerAction::GracefulRestart => MachineMaintenanceOperation::Reset,
+            PowerAction::ForceRestart => MachineMaintenanceOperation::Reset,
+            PowerAction::AcPowercycle => MachineMaintenanceOperation::Reset,
+        }
+    );
 }
🤖 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 3363 - 3385,
Update the map_machine_maintenance_operation test to cover the missing
GracefulRestart and AcPowercycle PowerAction variants, asserting both map to
MachineMaintenanceOperation::Reset. Convert the individual assertions to a
table-driven value-scenario or check_values test that concisely covers all six
variants.

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.

Inline comments:
In `@crates/machine-controller/src/handler.rs`:
- Around line 1306-1309: Update the maintenance transition handling around
maintenance_transition_if_requested so accepting maintenance from Failed
transactionally clears the host’s stale failure details before returning the
maintenance outcome. Match the existing recovery branches’ failure-clearing
behavior, while preserving the current transition and return flow.

In `@crates/machine-controller/src/handler/maintenance.rs`:
- Around line 113-217: Add table-driven tests for maintenance reconciliation
using scenarios! with Outcome or explicit check_cases for Result-returning
variants. Cover every power operation across backend success, empty results,
non-success results, transport failures, missing component manager, missing
credentials, and entry states Ready and Failed; assert both request clearing and
the resulting state for each case, reusing existing maintenance test helpers and
symbols.
- Around line 153-178: Update the power-control flow around the backend call and
maintenance transition to persist a durable dispatch phase and idempotency key
before invoking the non-idempotent operation. Reconcile Pending, Dispatched, and
Completed outcomes after crashes or transaction conflicts, retrying only when
safe and never blindly replaying an ambiguous reset; ensure successful
reconciliation clears maintenance and transitions to Ready.

---

Nitpick comments:
In `@crates/api-core/src/handlers/component_manager.rs`:
- Around line 3363-3385: Update the map_machine_maintenance_operation test to
cover the missing GracefulRestart and AcPowercycle PowerAction variants,
asserting both map to MachineMaintenanceOperation::Reset. Convert the individual
assertions to a table-driven value-scenario or check_values test that concisely
covers all six variants.

In `@crates/machine-controller/src/handler/maintenance.rs`:
- Around line 64-118: Update handle_power_on, handle_power_off, handle_reset,
and invoke_power_operation to accept a MachineMaintenanceOperation instead of
separate PowerAction and operation_label arguments. Derive the corresponding
PowerAction and display/log representation from that operation enum within the
common driver, removing duplicated string labels and preventing action-label
mismatches.
🪄 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: 50426d8d-d99d-4f5a-afc2-a5acccc992f1

📥 Commits

Reviewing files that changed from the base of the PR and between 0cd8acb and bd7731d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (16)
  • crates/api-core/src/handlers/component_manager.rs
  • crates/api-core/src/setup.rs
  • crates/api-core/src/tests/common/api_fixtures/mod.rs
  • crates/api-db/migrations/20260717120000_machine_maintenance_requested.sql
  • crates/api-db/src/machine.rs
  • crates/api-model/src/machine/json.rs
  • crates/api-model/src/machine/mod.rs
  • crates/api-model/src/machine/slas.rs
  • crates/api-model/src/test_support/machine_snapshot.rs
  • crates/component-manager/src/component_manager.rs
  • crates/machine-controller/Cargo.toml
  • crates/machine-controller/src/context.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/src/handler/maintenance.rs
  • crates/machine-controller/src/io.rs
  • crates/machine-controller/tests/integration/env.rs

Comment on lines +1306 to +1309
if let Some(outcome) = maintenance::maintenance_transition_if_requested(mh_snapshot)
{
return Ok(outcome);
}

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.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Clear stale failure details when accepting maintenance from Failed.

This transition preserves the existing failure details. On the next iteration, the generic failure check at Lines 739-765 runs before Maintenance dispatch and can immediately move the host back to Failed, so the requested operation never executes. Clear the failure details transactionally when entering maintenance, as the existing recovery branches do.

As per path instructions, review controller logic for “reconciliation correctness” and “state-machine transitions.”

🤖 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 1306 - 1309, Update
the maintenance transition handling around maintenance_transition_if_requested
so accepting maintenance from Failed transactionally clears the host’s stale
failure details before returning the maintenance outcome. Match the existing
recovery branches’ failure-clearing behavior, while preserving the current
transition and return flow.

Source: Path instructions

Comment on lines +113 to +217
async fn invoke_power_operation(
host_machine_id: &MachineId,
mh_snapshot: &ManagedHostStateSnapshot,
ctx: &mut StateHandlerContext<'_, MachineStateHandlerContextObjects>,
action: PowerAction,
operation_label: &'static str,
) -> Result<StateHandlerOutcome<ManagedHostState>, StateHandlerError> {
let machine = &mh_snapshot.host_snapshot;

let Some(component_manager) = ctx.services.component_manager.as_ref() else {
return finish_maintenance_with_error(
host_machine_id,
ctx,
format!(
"Machine {host_machine_id} maintenance ({operation_label}): component manager not configured"
),
)
.await;
};

let endpoint = match build_compute_tray_endpoint(
host_machine_id,
machine,
ctx.services.credential_manager.as_ref(),
)
.await
{
Ok(endpoint) => endpoint,
Err(cause) => {
return finish_maintenance_with_error(
host_machine_id,
ctx,
format!(
"Machine {host_machine_id} maintenance ({operation_label}): {cause}"
),
)
.await;
}
};

match component_manager
.compute_tray
.power_control(std::slice::from_ref(&endpoint), action)
.await
{
Ok(results) => {
let result = results
.into_iter()
.next()
.unwrap_or(ComputeTrayResult {
bmc_ip: endpoint.bmc_ip,
success: false,
error: Some("component manager returned no result".into()),
});

if result.success {
tracing::info!(
machine_id = %host_machine_id,
operation = operation_label,
backend = component_manager.compute_tray.name(),
"Machine power control succeeded; returning host to Ready"
);
let mut txn = ctx.services.db_pool.begin().await?;
db_machine::clear_machine_maintenance_requested(&mut txn, *host_machine_id)
.await?;
return Ok(StateHandlerOutcome::transition(ManagedHostState::Ready).with_txn(txn));
}

let summary = result
.error
.unwrap_or_else(|| "power control failed".into());
tracing::warn!(
machine_id = %host_machine_id,
operation = operation_label,
backend = component_manager.compute_tray.name(),
summary = %summary,
"Machine power control returned a non-success result",
);
finish_maintenance_with_error(
host_machine_id,
ctx,
format!(
"Machine {host_machine_id} maintenance ({operation_label}): power control failed: {summary}"
),
)
.await
}
Err(error) => {
tracing::warn!(
machine_id = %host_machine_id,
operation = operation_label,
backend = component_manager.compute_tray.name(),
error = %error,
"Machine power control transport error",
);
finish_maintenance_with_error(
host_machine_id,
ctx,
format!(
"Machine {host_machine_id} maintenance ({operation_label}): power control failed: {error}"
),
)
.await
}
}

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.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add table-driven coverage for maintenance reconciliation.

Cover each operation plus backend success, empty result, non-success result, transport failure, missing component manager, missing credentials, and entry from both Ready and Failed. Assert request clearing and final state in every case.

As per coding guidelines, use scenarios! with Outcome or explicit check_cases for Result-returning input variants.

🤖 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/maintenance.rs` around lines 113 - 217,
Add table-driven tests for maintenance reconciliation using scenarios! with
Outcome or explicit check_cases for Result-returning variants. Cover every power
operation across backend success, empty results, non-success results, transport
failures, missing component manager, missing credentials, and entry states Ready
and Failed; assert both request clearing and the resulting state for each case,
reusing existing maintenance test helpers and symbols.

Source: Coding guidelines

Comment on lines +153 to +178
match component_manager
.compute_tray
.power_control(std::slice::from_ref(&endpoint), action)
.await
{
Ok(results) => {
let result = results
.into_iter()
.next()
.unwrap_or(ComputeTrayResult {
bmc_ip: endpoint.bmc_ip,
success: false,
error: Some("component manager returned no result".into()),
});

if result.success {
tracing::info!(
machine_id = %host_machine_id,
operation = operation_label,
backend = component_manager.compute_tray.name(),
"Machine power control succeeded; returning host to Ready"
);
let mut txn = ctx.services.db_pool.begin().await?;
db_machine::clear_machine_maintenance_requested(&mut txn, *host_machine_id)
.await?;
return Ok(StateHandlerOutcome::transition(ManagedHostState::Ready).with_txn(txn));

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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Make power dispatch crash-safe before issuing non-idempotent resets.

The external operation completes before its request clear and state transition are committed. A crash, transaction error, or optimistic conflict in between leaves the host in Maintenance; the next iteration repeats the action, potentially causing repeated resets.

Persist a durable dispatch phase/idempotency key before calling the backend and reconcile Pending/Dispatched/Completed outcomes without blindly replaying ambiguous operations.

As per path instructions, controller logic must provide “idempotency” and “safe recovery from partial failures.”

🤖 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/maintenance.rs` around lines 153 - 178,
Update the power-control flow around the backend call and maintenance transition
to persist a durable dispatch phase and idempotency key before invoking the
non-idempotent operation. Reconcile Pending, Dispatched, and Completed outcomes
after crashes or transaction conflicts, retrying only when safe and never
blindly replaying an ambiguous reset; ensure successful reconciliation clears
maintenance and transitions to Ready.

Source: Path instructions

Add machine maintenance power operations mirroring the existing switch and
power-shelf state-controller maintenance pattern. When
compute_tray_use_state_controller is enabled, ComponentPowerControl for
machines queues a maintenance request instead of issuing Redfish directly.

Model and persistence:
- Introduce MachineMaintenanceOperation (PowerOn, PowerOff, Reset) and
  MachineMaintenanceRequest.
- Add ManagedHostState::Maintenance to execute queued operations.
- Add machines.machine_maintenance_requested JSONB column and DB helpers to
  set/clear the pending request.

Machine state handler:
- New maintenance handler dispatches power actions via compute_tray backend.
- Ready and Failed states transition into Maintenance when a request is
  pending; success returns to Ready, failure clears the request and moves
  to Failed to avoid retry loops.
- Wire component_manager and credential_manager into machine controller
  services for endpoint/credential resolution.

Component manager and API:
- Implement request_machine_maintenance_via_state_controller.
- Replace the unimplemented compute-tray state-controller path in
  component_power_control with the maintenance queue.
- Map PowerAction to MachineMaintenanceOperation consistently with switches.

Signed-off-by: Vinod Chitrali <vchitrali@nvidia.com>
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.

2 participants