@@ -353,17 +353,9 @@ impl Redfish for Bmc {
353353 }
354354
355355 let url = format ! ( "Systems/{}/Bios/Settings/" , self . s. system_id( ) ) ;
356- let bios_job_id = match self . s . client . patch ( & url, set_machine_attrs) . await ? {
357- ( _, Some ( headers) ) => {
358- let jid = self
359- . parse_job_id_from_response_headers ( & url, headers)
360- . await ?;
361- Some ( jid)
362- }
363- ( _, None ) => {
364- return Err ( RedfishError :: NoHeader ) ;
365- }
366- } ;
356+ let bios_job_id = self
357+ . patch_settings_for_job_id ( & url, set_machine_attrs)
358+ . await ?;
367359
368360 let oem_attrs = if let Some ( dell) = oem_manager_profiles. get ( & RedfishVendor :: Dell ) {
369361 let model = crate :: model_coerce (
@@ -1145,13 +1137,7 @@ impl Redfish for Bmc {
11451137 HashMap :: from ( [ ( "BootOrder" , vec ! [ boot_option. id. clone( ) ] ) ] ) ,
11461138 ) ] ) ;
11471139
1148- let job_id = match self . s . client . patch ( & url, body) . await ? {
1149- ( _, Some ( headers) ) => {
1150- self . parse_job_id_from_response_headers ( & url, headers) . await
1151- }
1152- ( _, None ) => Err ( RedfishError :: NoHeader ) ,
1153- } ?;
1154- return Ok ( Some ( job_id) ) ;
1140+ return self . patch_settings_for_job_id ( & url, body) . await ;
11551141 }
11561142 }
11571143
@@ -2290,6 +2276,58 @@ impl Bmc {
22902276 self . import_system_configuration ( system_configuration) . await
22912277 }
22922278
2279+ /// PATCHes a Dell settings endpoint and resolves the optional config job
2280+ /// id from the response.
2281+ ///
2282+ /// Dell responds to a settings PATCH in one of two ways:
2283+ /// - iDRAC9/iDRAC10 schedule a config job and return `202 Accepted` with a
2284+ /// `location` header pointing at the job; we parse and return its id so
2285+ /// the caller can wait on the job (`Some(job_id)`).
2286+ /// - Newer iDRAC (e.g. 17G PowerEdge R770), when the requested settings are
2287+ /// already staged in the pending-settings buffer, return `200 OK` with no
2288+ /// `location` header (message ID SYS011 "successfully committed", or
2289+ /// SYS413). No job was scheduled, so there is nothing to wait on and we
2290+ /// return `None`.
2291+ ///
2292+ /// We deliberately do NOT parse the response message ID: it varies across
2293+ /// iDRAC versions and conditions (SYS011 vs SYS413, etc.), so gating on a
2294+ /// specific value is brittle. A `200` with no job `location` is unambiguous
2295+ /// on its own; any other status surfaces as [`RedfishError::NoHeader`].
2296+ async fn patch_settings_for_job_id < B > (
2297+ & self ,
2298+ url : & str ,
2299+ body : B ,
2300+ ) -> Result < Option < String > , RedfishError >
2301+ where
2302+ B : Serialize + std:: fmt:: Debug ,
2303+ {
2304+ // Pass `body` by reference so it stays available to log on the no-job
2305+ // path below; serde implements `Serialize`/`Debug` for `&B`.
2306+ let ( status_code, resp_headers) = self . s . client . patch ( url, & body) . await ?;
2307+
2308+ match resp_headers
2309+ . as_ref ( )
2310+ . filter ( |headers| headers. contains_key ( "location" ) )
2311+ {
2312+ Some ( headers) => Ok ( Some (
2313+ self . parse_job_id_from_response_headers ( url, headers. clone ( ) )
2314+ . await ?,
2315+ ) ) ,
2316+ None if status_code == StatusCode :: OK => {
2317+ tracing:: info!(
2318+ bmc_ip = %self . s. client. host( ) ,
2319+ %url,
2320+ %status_code,
2321+ ?resp_headers,
2322+ ?body,
2323+ "Dell settings PATCH applied without scheduling a job (no location header); treating as success"
2324+ ) ;
2325+ Ok ( None )
2326+ }
2327+ None => Err ( RedfishError :: NoHeader ) ,
2328+ }
2329+ }
2330+
22932331 async fn parse_job_id_from_response_headers (
22942332 & self ,
22952333 url : & str ,
0 commit comments