From 1dff228fbf8afad60ba453c4768807b123dde644 Mon Sep 17 00:00:00 2001 From: abhi Date: Thu, 23 Jul 2026 17:55:45 +0530 Subject: [PATCH] fix: Use product_name to identify BF4 DPU. --- crates/machine-controller/src/handler.rs | 137 ++++++++++++----------- crates/redfish/src/libredfish/mod.rs | 13 +++ 2 files changed, 82 insertions(+), 68 deletions(-) diff --git a/crates/machine-controller/src/handler.rs b/crates/machine-controller/src/handler.rs index e66b478da8..17c61c5c2d 100644 --- a/crates/machine-controller/src/handler.rs +++ b/crates/machine-controller/src/handler.rs @@ -3831,26 +3831,18 @@ impl DpuMachineStateHandler { return Ok(false); } - let dpf_sdk = self.dpf_sdk.as_deref().ok_or_else(|| { - StateHandlerError::InvalidState( - "DPF-managed machine reached DPU platform configuration without DPF configured" - .to_string(), - ) - })?; - - let deployment_type = dpf_sdk - .deployment_type_for_dpu(dpu_snapshot) - .map_err(|error| { - StateHandlerError::InvalidState(format!( - "failed to determine DPF deployment type for DPU {}: {error}", - dpu_snapshot.id - )) + let product = dpu_snapshot + .status + .hardware_info + .as_ref() + .and_then(|x| x.dmi_data.as_ref()) + .map(|x| x.product_name.to_uppercase()) + .ok_or_else(|| StateHandlerError::MissingData { + object_id: dpu_snapshot.id.to_string(), + missing: "product_name in topology", })?; - Ok(matches!( - deployment_type, - carbide_dpf::DpuDeploymentType::Bf4Generic - )) + Ok(product.contains("B4") || product.contains("BLUEFIELD-4")) } async fn is_secure_boot_disabled( @@ -4293,46 +4285,52 @@ impl DpuMachineStateHandler { db::credential_rotation::CredentialRotationType::DpuUefi, ) .await?; - if let Err(e) = ctx + let credentials_updated = match ctx .services .redfish_client_pool .uefi_setup(dpu_redfish_client.as_ref(), true, dpu_uefi_credentials) .await { - let msg = format!( - "Failed to run uefi_setup call failed for DPU {}: {}", - dpu_snapshot.id, e - ); - tracing::warn!( - machine_id = %dpu_snapshot.id, - error = %e, - "Failed to run uefi_setup for DPU", - ); - let reboot_status = trigger_reboot_if_needed( + Err(e) => { + let msg = format!( + "Failed to run uefi_setup call failed for DPU {}: {}", + dpu_snapshot.id, e + ); + tracing::warn!( + machine_id = %dpu_snapshot.id, + error = %e, + "Failed to run uefi_setup for DPU", + ); + let reboot_status = trigger_reboot_if_needed( + dpu_snapshot, + state, + None, + &self.reachability_params, + ctx, + ) + .await?; + + return Ok(StateHandlerOutcome::wait(format!( + "{msg};\nWaiting for DPU {} to reboot: {reboot_status:#?}", + dpu_snapshot.id + ))); + } + // BIOS does not expose a UEFI password field (e.g. BF4); skip + // the restart and convergence record — nothing was staged. + Ok(None) => false, + // Password staged through Redfish BIOS settings; restart to commit. + Ok(Some(_)) => true, + }; + + if credentials_updated { + handler_restart_dpu( dpu_snapshot, - state, - None, - &self.reachability_params, ctx, + state.host_snapshot.config.dpf.used_for_ingestion, ) .await?; - - return Ok(StateHandlerOutcome::wait(format!( - "{msg};\nWaiting for DPU {} to reboot: {reboot_status:#?}", - dpu_snapshot.id - ))); } - // The UEFI password is staged through Redfish BIOS settings, - // so retain the restart even when BF4 skips platform setup and - // BIOS verification. - handler_restart_dpu( - dpu_snapshot, - ctx, - state.host_snapshot.config.dpf.used_for_ingestion, - ) - .await?; - let next_state = if dpf_managed_bf4 { DpuInitState::WaitingForNetworkConfig } else { @@ -4340,27 +4338,30 @@ impl DpuMachineStateHandler { } .next_state(&state.managed_state, dpu_machine_id)?; - // The DPU's UEFI password is now the site-wide value (just set via - // uefi_setup above): record dpu_uefi convergence so the rotation - // engine tracks this DPU from ingestion onward (mirrors the - // backfill, which keys DPU UEFI by the DPU BMC MAC). The MAC was - // validated as a precondition at the top of this state. Committed - // with the state transition below. - let mut txn = ctx.services.db_pool.begin().await?; - db::credential_rotation::record_device_converged( - &mut txn, - dpu_bmc_mac, - db::credential_rotation::CredentialRotationType::DpuUefi, - ) - .await - .map_err(|e| { - StateHandlerError::GenericError(eyre!( - "record dpu_uefi credential rotation convergence failed: {}", - e - )) - })?; - - Ok(StateHandlerOutcome::transition(next_state).with_txn(txn)) + if credentials_updated { + // The DPU's UEFI password is now the site-wide value (just set via + // uefi_setup above): record dpu_uefi convergence so the rotation + // engine tracks this DPU from ingestion onward (mirrors the + // backfill, which keys DPU UEFI by the DPU BMC MAC). The MAC was + // validated as a precondition at the top of this state. Committed + // with the state transition below. + let mut txn = ctx.services.db_pool.begin().await?; + db::credential_rotation::record_device_converged( + &mut txn, + dpu_bmc_mac, + db::credential_rotation::CredentialRotationType::DpuUefi, + ) + .await + .map_err(|e| { + StateHandlerError::GenericError(eyre!( + "record dpu_uefi credential rotation convergence failed: {}", + e + )) + })?; + Ok(StateHandlerOutcome::transition(next_state).with_txn(txn)) + } else { + Ok(StateHandlerOutcome::transition(next_state)) + } } DpuInitState::PollingBiosSetup => { diff --git a/crates/redfish/src/libredfish/mod.rs b/crates/redfish/src/libredfish/mod.rs index 0a5dbc5a1a..93ea98474e 100644 --- a/crates/redfish/src/libredfish/mod.rs +++ b/crates/redfish/src/libredfish/mod.rs @@ -209,6 +209,18 @@ pub trait RedfishClientPool: Send + Sync + 'static { (_, current_password) = match credentials { Credentials::UsernamePassword { username, password } => (username, password), }; + + // DPU's change_uefi_password always returns Ok(None) on success (no async job). + // Return Ok(Some("")) here so callers can distinguish "updated" from the + // early-exit Ok(None) skip paths above. The host path below must NOT get + // this mapping — hosts may return Ok(None) to mean "no job to poll". + return client + .change_uefi_password(current_password.as_str(), new_password.as_str()) + .await + .map_err(|err| redact_password(err, new_password.as_str())) + .map_err(|err| redact_password(err, current_password.as_str())) + .map_err(RedfishClientCreationError::RedfishError) + .map(|job_id| Some(job_id.unwrap_or_default())); } else { // For hosts, first try with empty current password (assuming no // password is set), using the site default handed in by the caller. @@ -232,6 +244,7 @@ pub trait RedfishClientPool: Send + Sync + 'static { } } + // Host fallback: second attempt using site default as current password. client .change_uefi_password(current_password.as_str(), new_password.as_str()) .await