diff --git a/src/model/manager.rs b/src/model/manager.rs index ceb354f59..65cd2f1ad 100644 --- a/src/model/manager.rs +++ b/src/model/manager.rs @@ -59,6 +59,16 @@ pub struct Manager { #[serde(rename = "UUID")] pub uuid: Option, pub oem: Option, + pub links: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[serde(rename_all = "PascalCase")] +pub struct ManagerLinks { + #[serde(default)] + pub manager_for_servers: Vec, + #[serde(default)] + pub manager_for_chassis: Vec, } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/src/model/system.rs b/src/model/system.rs index e29802ec0..0a548343b 100644 --- a/src/model/system.rs +++ b/src/model/system.rs @@ -179,6 +179,7 @@ pub struct ComputerSystem { pub asset_tag: Option, #[serde(default)] // Some viking ComputerSystem has no Boot property; so use the default pub boot: Boot, + pub bios: Option, pub bios_version: Option, pub ethernet_interfaces: Option, pub id: String, diff --git a/src/network.rs b/src/network.rs index a3d2b76b4..5438f19b9 100644 --- a/src/network.rs +++ b/src/network.rs @@ -211,9 +211,9 @@ impl RedfishClientPool { }; let managers = s.get_managers().await?; - let manager_id = managers.first().ok_or_else(|| RedfishError::GenericError { + let mut manager_id = managers.first().ok_or_else(|| RedfishError::GenericError { error: "No managers found in service root".to_string(), - })?; + })?.clone(); let chassis = s.get_chassis_all().await?; // Delta power shelves expose no `/Systems` resource (a real query 404s) @@ -228,18 +228,44 @@ impl RedfishClientPool { // member blindly targets the wrong system (no BIOS/boot). Falling // back to the first member preserves behavior for every platform // that does not expose `System_0` (e.g. Viking's `DGX`). - let system_id = systems + let at_least_one_system_id = systems .iter() .find(|id| *id == "System_0") .or_else(|| systems.first()) .ok_or_else(|| RedfishError::GenericError { error: "No systems found in service root".to_string(), })?; + + //Find another system with BIOS section + let mut system_with_bios: Option = None; + for system_member in &systems { + // Treat any error as "no BIOS here": we already have + // `at_least_one_system_id` as a fallback, and this also handles the + // test mockup, which drops the connection instead of returning 404 + // when the Bios section is empty. + system_with_bios = s.if_system_has_bios(system_member).await; + if system_with_bios.is_some() { + break; + } + + } + let manager_from_system = system_with_bios + .as_ref() + .and_then(|swb| swb.links.as_ref()) + .and_then(|links| links.managed_by.as_ref()) + .and_then(|mb| mb.get(0)) + .and_then(|d| d.odata_id.trim_matches('/').split('/').next_back()) + .map(|m| m.to_string()); + manager_id = manager_from_system.unwrap_or(manager_id); + + let system_id = system_with_bios.map(|swb| swb.id.to_owned()).unwrap_or(at_least_one_system_id.to_owned()); + // call set_system_id always before calling set_vendor - s.set_system_id(system_id)?; + s.set_system_id(&system_id)?; + } - s.set_manager_id(manager_id)?; + s.set_manager_id(&manager_id)?; s.set_service_root(service_root.clone())?; // Resolve placeholder/ambiguous vendors that can only be settled from diff --git a/src/standard.rs b/src/standard.rs index fe7eadfb6..61dd12d12 100644 --- a/src/standard.rs +++ b/src/standard.rs @@ -40,7 +40,9 @@ use crate::model::{job::Job, oem::nvidia_dpu::NicMode}; use crate::model::{ manager_network_protocol::ManagerNetworkProtocol, update_service::TransferProtocolType, }; -use crate::model::{power, thermal, BootOption, InvalidValueError, Manager, Managers, ODataId}; +use crate::model::{ + power, thermal, BootOption, ComputerSystem, InvalidValueError, Manager, Managers, ODataId, +}; use crate::model::{power::Power, update_service::UpdateService}; use crate::model::{secure_boot::SecureBoot, sensor::GPUSensors}; use crate::model::{sel::LogEntry, ManagerResetType}; @@ -1408,6 +1410,17 @@ impl RedfishStandard { Ok(b) } + pub fn get_manager_with_id<'a>( + &'a self, + manager_id: &'a str, + ) -> crate::RedfishFuture<'a, Result> { + Box::pin(async move { + let (_, manager): (_, Manager) = + self.client.get(&format!("Managers/{}", manager_id)).await?; + Ok(manager) + }) + } + pub async fn fetch_bmc_event_log( &self, url: String, @@ -1510,6 +1523,14 @@ impl RedfishStandard { }) } + pub async fn if_system_has_bios(&self, system_id: &str) -> Option { + self.client + .get::(&format!("Systems/{system_id}")) + .await + .map_or(None, |(_code, cs)| Some(cs)) + .and_then(|cs| if cs.bios.is_some() { Some(cs) } else { None }) + } + pub async fn factory_reset_bios(&self) -> Result<(), RedfishError> { let url = format!("Systems/{}/Bios/Actions/Bios.ResetBios", self.system_id()); self.client