Skip to content

feat: ensure a correct boot config during machine validation#3369

Merged
chet merged 1 commit into
NVIDIA:mainfrom
chet:gh-issue-3365
Jul 10, 2026
Merged

feat: ensure a correct boot config during machine validation#3369
chet merged 1 commit into
NVIDIA:mainfrom
chet:gh-issue-3365

Conversation

@chet

@chet chet commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Machine validation now ensures the host's boot device config is what it
should be, and corrects it when it isn't. While waiting for the host to boot
into the validation environment, validation verifies the boot config; if it
reads reverted -- however it drifted, whether someone changed it externally,
a BIOS quirk, or the boot NIC dropping off the BMC's inventory during the
reboot's POST -- validation unlocks the BMC, re-drives the boot-order flow
(re-assert the HTTP-boot device, reboot to apply, set and verify the order),
re-locks, and resumes from its reboot step.

Without this, a drifted boot config was unrecoverable at this stage: the host
can't boot into scout, more reboots can't restore a setting that is gone, and
the BMC lockdown enabled earlier in HostInit blocks any fix -- so validation
paced reboots indefinitely.

  • Verification lives in the wait-for-reboot path: an intact config keeps the
    normal reboot pacing; only a reverted one enters correction.
  • The correction reuses the boot-order flow and the host-boot-repair
    unlock/re-lock choreography, honoring the expected-machine lockdown policy.
  • New validation substates (PrepareBootRepair, UnlockForBootRepair,
    RepairBootConfig, LockAfterBootRepair) surface the correction in state
    history and metrics.

Tests updated!

This supports #3365

Signed-off-by: Chet Nichols III chetn@nvidia.com

@chet chet requested a review from a team as a code owner July 10, 2026 16:53
@chet

chet commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full_review, thanks!

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@chet Sounds good — I'll re-review the entire pull request from scratch.

✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Summary by CodeRabbit

  • Bug Fixes
    • Improved machine validation recovery by detecting reverted BIOS setup and automatically performing a boot-device repair sequence.
    • The repair workflow now manages BMC lockdown during remediation, updates boot configuration, and then resumes validation.
  • Monitoring / Visibility
    • Added more granular machine validation stage metrics for each boot-repair sub-step.
  • Tests
    • Added a new end-to-end test covering reverted boot configuration, lockdown unlock/lock behavior, and correct action ordering during repair.

Walkthrough

The validation workflow detects reverted boot configuration, repairs it through new persisted substates, restores BMC lockdown when applicable, and resumes validation. An end-to-end test verifies repair ordering and state transitions.

Changes

Validation boot repair

Layer / File(s) Summary
Persisted validation repair states
crates/api-model/src/machine/mod.rs, crates/api-core/src/tests/common/api_fixtures/mod.rs, crates/machine-controller/src/io.rs
Adds four boot-repair states, assigns the validation SLA, and updates fixture handling and metric labels.
Boot repair orchestration
crates/machine-controller/src/handler/machine_validation.rs
Detects reverted boot configuration, manages BMC lockdown and vendor-specific waits, reapplies boot order, restores lockdown, and resumes validation.
Repair validation coverage
crates/api-core/src/tests/machine_states.rs
Adds end-to-end coverage for repair actions, lockdown transitions, validation resumption, and action ordering.

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

Sequence Diagram(s)

sequenceDiagram
  participant ValidationController
  participant RedfishBMC
  participant BootOrderEngine
  ValidationController->>RedfishBMC: Verify required boot configuration
  RedfishBMC-->>ValidationController: Report reverted configuration
  ValidationController->>RedfishBMC: Disable lockdown
  ValidationController->>BootOrderEngine: Reapply host boot order
  BootOrderEngine-->>ValidationController: Return repair outcome
  ValidationController->>RedfishBMC: Restore lockdown
  ValidationController->>ValidationController: Resume at RebootHost
Loading

Possibly related issues

  • NVIDIA/infra-controller 3365: Directly covers recovery of reverted boot configuration during the machine validation reboot flow.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: validating and correcting boot config during machine validation.
Description check ✅ Passed The description is directly aligned with the implemented boot-config verification and repair flow.
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.

@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/machine-controller/src/handler/machine_validation.rs (1)

240-431: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Consider extending coverage for the untested boot-repair branches.

The new E2E test only exercises "lockdown enabled + non-Supermicro" path. The Supermicro reboot sub-path (Lines 306-321), both RedfishError::NotSupported branches, and the host_profile.disable_lockdown skip (Lines 408-412) aren't covered by any test in this PR. Given the branch count and vendor-specific behavior, a focused table-driven test (or targeted unit tests per STYLE_GUIDE.md's table-driven testing guidance) around handle_machine_validation_state for these substates would reduce regression risk on vendor-specific quirks.

🤖 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/machine_validation.rs` around lines 240
- 431, Add focused table-driven tests for handle_machine_validation_state
covering Supermicro lockdown-disable flow with host reboot,
RedfishError::NotSupported during lockdown status lookup,
RedfishError::NotSupported during lockdown re-enable, and
host_profile.disable_lockdown skipping re-enable; assert each branch’s resulting
state, wait/error behavior, and relevant Redfish interactions.

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/machine_validation.rs`:
- Around line 298-304: In the UnlockHostState::DisableLockdown branch, do not
propagate RedfishError::NotSupported from
lockdown_bmc(EnabledDisabled::Disabled) as a hard failure. Mirror the tolerant
handling implemented in LockAfterBootRepair: treat NotSupported as a successful
transition while continuing to return other errors through
redfish_error("lockdown_bmc", e).

---

Nitpick comments:
In `@crates/machine-controller/src/handler/machine_validation.rs`:
- Around line 240-431: Add focused table-driven tests for
handle_machine_validation_state covering Supermicro lockdown-disable flow with
host reboot, RedfishError::NotSupported during lockdown status lookup,
RedfishError::NotSupported during lockdown re-enable, and
host_profile.disable_lockdown skipping re-enable; assert each branch’s resulting
state, wait/error behavior, and relevant Redfish interactions.
🪄 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: 7c725ff8-c452-4593-b96d-56f456a52d65

📥 Commits

Reviewing files that changed from the base of the PR and between 6ac0588 and 156412e.

📒 Files selected for processing (5)
  • crates/api-core/src/tests/common/api_fixtures/mod.rs
  • crates/api-core/src/tests/machine_states.rs
  • crates/api-model/src/machine/mod.rs
  • crates/machine-controller/src/handler/machine_validation.rs
  • crates/machine-controller/src/io.rs

Comment thread crates/machine-controller/src/handler/machine_validation.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 (2)
crates/machine-controller/src/handler/machine_validation.rs (2)

305-322: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Supermicro reboot/UEFI-wait branch appears untested by the new e2e test.

The vendor.is_supermicro() branch (routing through RebootHostWaitForUefiBoot before RepairBootConfig) is a distinct code path from the direct-to-RepairBootConfig path exercised by test_machine_validation_repairs_reverted_boot_config. Since that test's fixture doesn't appear to configure a Supermicro BMC vendor, this branch's behavior (extra reboot pacing, uefi_boot_wait) is currently unverified end-to-end.

Consider adding a second scenario (or parameterizing the existing test) with a Supermicro vendor fixture to cover this path, given it changes both the state sequence and introduces an extra reboot.

🤖 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/machine_validation.rs` around lines 305
- 322, Add or parameterize an end-to-end machine-validation test to use a
Supermicro BMC vendor fixture, covering the vendor.is_supermicro() branch in the
validation state handler. Assert the resulting RebootHost → WaitForUefiBoot
flow, including reboot pacing and uefi_boot_wait, before RepairBootConfig,
alongside the existing non-Supermicro scenario.

148-187: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Verification-gated repair trigger looks correct.

Detection logic is sound: transient Redfish errors are logged and retried, only a definitive Ok(false) triggers the PrepareBootRepair transition. No correctness issues found here.

Given this flags a hardware/BIOS anomaly (boot NIC dropping off inventory, BIOS quirk) that operators would want to trend/alert on, consider emitting a carbide_instrument::Event (e.g., carbide_validation_boot_repair_total counter) at the point the repair is triggered, rather than only a tracing::warn!. This gives site operators visibility into recurring hardware issues without needing to grep logs.

🤖 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/machine_validation.rs` around lines 148
- 187, Add a Carbide instrumentation event or counter for validation boot
repairs at the definitive Ok(false) branch in the boot-config verification
logic, alongside the existing tracing::warn! and immediately before
transitioning to PrepareBootRepair. Use the project’s established
carbide_instrument::Event pattern and name it consistently, such as
carbide_validation_boot_repair_total, including the machine identifier if
supported.
🤖 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/machine-controller/src/handler/machine_validation.rs`:
- Around line 305-322: Add or parameterize an end-to-end machine-validation test
to use a Supermicro BMC vendor fixture, covering the vendor.is_supermicro()
branch in the validation state handler. Assert the resulting RebootHost →
WaitForUefiBoot flow, including reboot pacing and uefi_boot_wait, before
RepairBootConfig, alongside the existing non-Supermicro scenario.
- Around line 148-187: Add a Carbide instrumentation event or counter for
validation boot repairs at the definitive Ok(false) branch in the boot-config
verification logic, alongside the existing tracing::warn! and immediately before
transitioning to PrepareBootRepair. Use the project’s established
carbide_instrument::Event pattern and name it consistently, such as
carbide_validation_boot_repair_total, including the machine identifier if
supported.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 14403b81-331f-4e1b-afef-c1250cc8094c

📥 Commits

Reviewing files that changed from the base of the PR and between 6ac0588 and 156412e.

📒 Files selected for processing (5)
  • crates/api-core/src/tests/common/api_fixtures/mod.rs
  • crates/api-core/src/tests/machine_states.rs
  • crates/api-model/src/machine/mod.rs
  • crates/machine-controller/src/handler/machine_validation.rs
  • crates/machine-controller/src/io.rs

Machine validation now ensures the host's boot device config is what it
should be, and corrects it when it isn't. While waiting for the host to boot
into the validation environment, validation verifies the boot config; if it
reads reverted -- however it drifted, whether someone changed it externally,
a BIOS quirk, or the boot NIC dropping off the BMC's inventory during the
reboot's POST -- validation unlocks the BMC, re-drives the boot-order flow
(re-assert the HTTP-boot device, reboot to apply, set and verify the order),
re-locks, and resumes from its reboot step.

Without this, a drifted boot config was unrecoverable at this stage: the host
can't boot into scout, more reboots can't restore a setting that is gone, and
the BMC lockdown enabled earlier in HostInit blocks any fix -- so validation
paced reboots indefinitely.

- Verification lives in the wait-for-reboot path: an intact config keeps the
  normal reboot pacing; only a reverted one enters correction.
- The correction reuses the boot-order flow and the host-boot-repair
  unlock/re-lock choreography, honoring the expected-machine lockdown policy.
- New validation substates (PrepareBootRepair, UnlockForBootRepair,
  RepairBootConfig, LockAfterBootRepair) surface the correction in state
  history and metrics.

Tests updated!

This supports NVIDIA#3365

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
@chet

chet commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full_review, thanks!

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@chet Sounds good — I'll re-review the entire pull request from scratch again.

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

🧹 Nitpick comments (3)
crates/machine-controller/src/handler/machine_validation.rs (2)

36-42: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider applying the new validating() helper consistently.

The helper is introduced specifically to avoid repeating the ManagedHostState::Validation { validation_state: ValidationState::MachineValidation { .. } } wrapper, and the new repair arms use it correctly. However, the pre-existing RebootHost arm (~lines 119-129) and handle_machine_validation_requested (~lines 479-485) still hand-construct the identical shape. Migrating those to validating(...) would fully realize the DRY benefit this helper was added for.

🤖 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/machine_validation.rs` around lines 36
- 42, Use the new validating() helper consistently by replacing the duplicated
ManagedHostState::Validation/ValidationState::MachineValidation construction in
the RebootHost arm and handle_machine_validation_requested with validating(...),
preserving each existing MachineValidatingState value.

148-187: 🚀 Performance & Scalability | 🔵 Trivial

Added Redfish call per waiting iteration — confirm BMC load impact at scale.

Every controller tick while waiting for the validation reboot now issues an additional is_bios_setup Redfish query (on top of whatever trigger_reboot_if_needed already does), for every host in this state — not just ones that will actually drift. The window is bounded by reboot duration, so this is likely acceptable, but worth confirming polling frequency and concurrent host count don't create meaningful additional BMC query volume at fleet scale.

🤖 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/machine_validation.rs` around lines 148
- 187, Review the validation waiting loop around the boot-config check and
assess the added is_bios_setup call for fleet-scale BMC load. Confirm the
controller tick frequency, expected concurrent hosts, and reboot-window
duration; if volume is excessive, reduce polling or reuse the result from
trigger_reboot_if_needed rather than issuing a duplicate Redfish query each
iteration.
crates/api-core/src/tests/machine_states.rs (1)

3231-3251: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Strengthen coverage by asserting the intermediate boot-repair substates.

The PR explicitly introduces PrepareBootRepair, UnlockForBootRepair, RepairBootConfig, and LockAfterBootRepair to expose the repair process in state history/metrics, but this test only checks side effects (redfish actions, lockdown flags, final RebootHost resumption) without verifying the host actually transitions through those substates in order. A regression that skips or reorders one of these substates (while still ending up unlocked → repaired → re-locked → resumed) could slip through undetected.

Consider capturing host.current_state() on each iteration of the second loop and asserting the expected substate sequence is observed at least once before resumption.

♻️ Illustrative sketch of stronger substate coverage
     let mut resumed = false;
+    let mut seen_prepare = false;
+    let mut seen_unlock = false;
+    let mut seen_repair = false;
+    let mut seen_lock = false;
     for _ in 0..20 {
         env.run_machine_state_controller_iteration().await;
         let mut txn = env.db_txn().await;
         let host = mh.host().db_machine(&mut txn).await;
+        match host.current_state() {
+            ManagedHostState::Validation {
+                validation_state: ValidationState::MachineValidation {
+                    machine_validation: MachineValidatingState::PrepareBootRepair { .. },
+                },
+            } => seen_prepare = true,
+            ManagedHostState::Validation {
+                validation_state: ValidationState::MachineValidation {
+                    machine_validation: MachineValidatingState::UnlockForBootRepair { .. },
+                },
+            } => seen_unlock = true,
+            ManagedHostState::Validation {
+                validation_state: ValidationState::MachineValidation {
+                    machine_validation: MachineValidatingState::RepairBootConfig { .. },
+                },
+            } => seen_repair = true,
+            ManagedHostState::Validation {
+                validation_state: ValidationState::MachineValidation {
+                    machine_validation: MachineValidatingState::LockAfterBootRepair { .. },
+                },
+            } => seen_lock = true,
+            _ => {}
+        }
         if matches!(
             host.current_state(),
             ManagedHostState::Validation {
                 validation_state: ValidationState::MachineValidation {
                     machine_validation: MachineValidatingState::RebootHost { .. },
                 },
             }
         ) {
             resumed = true;
             break;
         }
     }
+    assert!(seen_prepare && seen_unlock && seen_repair && seen_lock,
+        "expected to observe all boot-repair substates");
🤖 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/machine_states.rs` around lines 3231 - 3251,
Strengthen the second controller-iteration loop by recording each observed host
state and asserting that PrepareBootRepair, UnlockForBootRepair,
RepairBootConfig, and LockAfterBootRepair are each reached in that order before
RebootHost resumption. Update the existing resumed assertion to verify the
ordered substate sequence rather than only the final state, using the relevant
ManagedHostState and machine validation state variants.
🤖 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/machine_states.rs`:
- Around line 3231-3251: Strengthen the second controller-iteration loop by
recording each observed host state and asserting that PrepareBootRepair,
UnlockForBootRepair, RepairBootConfig, and LockAfterBootRepair are each reached
in that order before RebootHost resumption. Update the existing resumed
assertion to verify the ordered substate sequence rather than only the final
state, using the relevant ManagedHostState and machine validation state
variants.

In `@crates/machine-controller/src/handler/machine_validation.rs`:
- Around line 36-42: Use the new validating() helper consistently by replacing
the duplicated ManagedHostState::Validation/ValidationState::MachineValidation
construction in the RebootHost arm and handle_machine_validation_requested with
validating(...), preserving each existing MachineValidatingState value.
- Around line 148-187: Review the validation waiting loop around the boot-config
check and assess the added is_bios_setup call for fleet-scale BMC load. Confirm
the controller tick frequency, expected concurrent hosts, and reboot-window
duration; if volume is excessive, reduce polling or reuse the result from
trigger_reboot_if_needed rather than issuing a duplicate Redfish query each
iteration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9736dd1d-433b-44d5-88b8-e5346eeb7eb1

📥 Commits

Reviewing files that changed from the base of the PR and between bd72756 and 6ca5d12.

📒 Files selected for processing (5)
  • crates/api-core/src/tests/common/api_fixtures/mod.rs
  • crates/api-core/src/tests/machine_states.rs
  • crates/api-model/src/machine/mod.rs
  • crates/machine-controller/src/handler/machine_validation.rs
  • crates/machine-controller/src/io.rs

);
}
}
}

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.

why is this specific to MachineValidating?

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.

I guess I'm really asking: why not make this generic so it can be used in other states?

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.

That's exactly where this is headed -- the issue (#3365) frames this as the first adopter of a shared "host didn't return from a reboot" recovery, with the other reboot-wait sites (instance start, UefiSetup, WaitingForLockdown, firmware updates, re-discovery) listed as future adopters.

Two reasons it starts validation-specific rather than generic: the repair already reuses the shared pieces end-to-end (set_host_boot_order drives the whole correction, and the unlock/re-lock mirrors host boot repair) -- what's state-specific is only the detection point and the resume-into-the-interrupted-stage transitions; and making THOSE generic means designing a return-to continuation across the state model, which deserves its own change rather than riding this one. Validation got it first because it's the spot we watched a host wedge on real hardware. Happy to take the generic step as the follow-up.

@github-actions

Copy link
Copy Markdown

🔍 Container Scan Summary

Service Total Critical High Medium Low Other
boot-artifacts-aarch64 3 0 0 3 0 0
boot-artifacts-x86_64 3 0 0 3 0 0
forge-admin-cli-x86_64 271 13 34 91 7 126
machine-validation-runner 804 40 237 296 36 195
machine_validation 804 40 237 296 36 195
machine_validation-aarch64 804 40 237 296 36 195
nvmetal-carbide 804 40 237 296 36 195
TOTAL 3493 173 982 1281 151 906

Per-CVE detail lives in the per-service grype-* artifacts (JSON + SARIF). Severity counts only — no CVE IDs published here.

@chet chet merged commit a2ab63f into NVIDIA:main Jul 10, 2026
58 checks passed
@chet chet deleted the gh-issue-3365 branch July 10, 2026 20:21
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