From dfde741d9158a73634ff39ee4af87b50b1fb5762 Mon Sep 17 00:00:00 2001 From: Chet Nichols III Date: Wed, 8 Apr 2026 17:15:28 -0700 Subject: [PATCH] feat: add support to create_client_with_vendor, plumb through Trying to approach this as something that makes sense, in the general sense, even though this is the first use-case for it. The motivator here is, since a Lite-On power shelf has no vendor details in the service root, the Redfish client cannot determine the vendor hardware type, so it defaults back to a `RedfishStandard` client, and not a `LiteOnPowerShelf` client. This means that when the client calls `set_machine_password_policy`, it fails, because it's using the `RedfishStandard` implementation, and not the `LiteOnPowerShelf` implementation; the Lite-On imlpementation uses an `AccountService` call, and not the "default" `AccountLockoutCounterResetAfter` call). So right now my choices are to either: - Skip `set_machine_password_policy` entirely for power shelves. - Introduce a way to set a specific vendor on a client, and bypass the service root check. --- src/network.rs | 72 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 15 deletions(-) diff --git a/src/network.rs b/src/network.rs index 274081031..4e1fc0370 100644 --- a/src/network.rs +++ b/src/network.rs @@ -149,15 +149,51 @@ impl RedfishClientPool { .await } - /// Creates a Redfish BMC client for a certain endpoint and adds custom headers to subsequent requests. + /// Creates a Redfish BMC client for a certain endpoint, + /// and adds custom headers to subsequent requests. /// - /// Creating the client will immediately start a HTTP requests - /// to set system_id, manager_id and vendor type. - /// `custom_headers` will be added to any headers used by vendor specific implementations or the http client. + /// Creating the client will immediately start HTTP requests + /// to set system_id, manager_id, and vendor type (the vendor + /// is auto-detected from the service root. + /// + /// `custom_headers` will be added to any headers used by vendor + /// specific implementations or the http client. pub async fn create_client_with_custom_headers( &self, endpoint: Endpoint, custom_headers: Vec<(HeaderName, String)>, + ) -> Result, RedfishError> { + self.create_client_impl(endpoint, None, custom_headers) + .await + } + + /// Creates a Redfish BMC client for a certain endpoint using + /// the provided vendor instead of auto-detecting from the service + /// root. This is needed for BMCs (e.g. Lite-On power shelves) whose + /// service root does not expose vendor information, where we need + /// a client that uses vendor-specific logic. + pub async fn create_client_with_vendor( + &self, + endpoint: Endpoint, + vendor: RedfishVendor, + custom_headers: Vec<(HeaderName, String)>, + ) -> Result, RedfishError> { + self.create_client_impl(endpoint, Some(vendor), custom_headers) + .await + } + + // Creates a complete "client" that takes the endpoint, an optional + // vendor (which falls back to self-detection using the service root), + // and an optional set of custom headers. + // + // If there's ever a need to expose this as pub, it's entirely + // reasonable to do so (and rename it to something descriptive like + // create_complete_client). + async fn create_client_impl( + &self, + endpoint: Endpoint, + vendor: Option, + custom_headers: Vec<(HeaderName, String)>, ) -> Result, RedfishError> { let client = RedfishHttpClient::new(self.http_client.clone(), endpoint, custom_headers); let mut s = RedfishStandard::new(client); @@ -172,22 +208,28 @@ impl RedfishClientPool { })?; let chassis = s.get_chassis_all().await?; - s.set_system_id(system_id)?; // call set_system_id always before calling set_vendor + s.set_system_id(system_id)?; s.set_manager_id(manager_id)?; s.set_service_root(service_root.clone())?; - let Some(mut vendor) = service_root.vendor() else { - return Err(RedfishError::MissingVendor); - }; - if vendor == RedfishVendor::P3809 { - if chassis.contains(&"MGX_NVSwitch_0".to_string()) { - vendor = RedfishVendor::NvidiaGBSwitch; - } else { - vendor = RedfishVendor::NvidiaGH200; + let vendor = match vendor { + Some(v) => v, + None => { + let Some(mut v) = service_root.vendor() else { + return Err(RedfishError::MissingVendor); + }; + if v == RedfishVendor::P3809 { + if chassis.contains(&"MGX_NVSwitch_0".to_string()) { + v = RedfishVendor::NvidiaGBSwitch; + } else { + v = RedfishVendor::NvidiaGH200; + } + } + v } - } - // returns the vendor specific object + }; + s.set_vendor(vendor).await }