Skip to content

Commit 6da004b

Browse files
committed
feat: implement HPE set_boot_override via UrlBootFile BIOS attribute
The existing impl PATCHed the standard Redfish `Boot.HttpBootUri` at `/Systems/{id}`, but iLO (at least iLO 6) doesn't actually implement that; the entire BootSourceOverride PATCH path is non-functional. Even non-HTTP targets like `Pxe` return `UnsupportedOperation`, and `HttpBootUri` itself returns `PropertyNotWritableOrUnknown`. The field exists in the response schema for compliance, BUT the firmware isn't wired up to honor `PATCH`es to it. The actual HPE path is the `UrlBootFile` BIOS attribute (with `UrlBootFile2/3/4` secondaries and `PreBootNetwork` for IPv4/IPv6/Auto), which is `PATCH`ed at `/Systems/{id}/Bios/settings/`. This is a very similar pattern that Dell uses with `HttpDev1Uri`, just with an HPE-specific attribute name. Setting it creates a UEFI boot entry called "URL File" pointing at the URI, which applies on next reset. It is equivalent to the documented `HPREST/ilorest` flow `select Bios; set UrlBootFile=...; commit`. Verified on HPE ProLiant DL380a Gen11 gear with: - iLO 6 v1.58 - iLO 6 v1.72 In testing: - `ServerConfigLockState` was `Disabled` across all sampled machines. - `UrlBootFile.ReadOnly` was `false` in the registry. - `PATCH` returned HTTP 200 + `SystemResetRequired`. - Rollback works cleanly a `PATCH` of the `UrlBootFile` back to `""`. Unlike some situations I was running into with Dell gear, the attribute is consistently writable across the fleet sample I checked. Returns `Ok(None)` on success (since HPE doesn't surface a job ID for BIOS attribute changes; pending state lives in `/Bios/settings/` and applies on next reboot). Note! `boot_once` and `boot_first` on HPE are unchanged. Those both use the existing HPE OEM `PersistentBootConfigOrder` mechanism at `/Systems/{id}/Bios/oem/hpe/boot/settings/` and are independent of this code path. Integration test updated. HPE testing now exercises the BIOS-attribute path with an `http_boot_uri` provided (the only HPE-supported mode), and is excluded from the bare-override loop (where `set_boot_override` is called without `http_boot_uri`) since that returns `NotSupported` on HPE by design. Signed-off-by: Chet Nichols III <chetn@nvidia.com>
1 parent 02f9c19 commit 6da004b

2 files changed

Lines changed: 83 additions & 29 deletions

File tree

src/hpe.rs

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use serde_json::Value;
2727
use crate::{
2828
model::{
2929
account_service::ManagerAccount,
30-
boot::{BootOverride, BootSourceOverrideMode},
30+
boot::BootOverride,
3131
certificate::Certificate,
3232
chassis::{Assembly, Chassis, NetworkAdapter},
3333
component_integrity::ComponentIntegrities,
@@ -476,34 +476,64 @@ impl Redfish for Bmc {
476476
Box::pin(async move { self.boot_first(target).await })
477477
}
478478

479+
/// HPE iLO does not implement the standard Redfish `Boot.HttpBootUri`
480+
/// PATCH path. The `HttpBootUri` field appears in the Boot block (as
481+
/// `null` in GET responses) and `UefiHttp` is advertised in
482+
/// `BootSourceOverrideTarget@Redfish.AllowableValues`, but PATCHing
483+
/// either rejects with `PropertyNotWritableOrUnknown` and
484+
/// `UnsupportedOperation` respectively. In fact, the entire
485+
/// `BootSourceOverride` PATCH mechanism is non-functional on iLO 6
486+
/// (at least in v1.58); even `BootSourceOverrideTarget: "Pxe"` is
487+
/// rejected.
488+
///
489+
/// The HPE-specific path for pinning a UEFI HTTP boot URL is the
490+
/// `UrlBootFile` BIOS attribute (with optional `UrlBootFile2/3/4`
491+
/// secondaries and `PreBootNetwork` for IPv4/IPv6/Auto). Setting it
492+
/// creates a UEFI boot entry called "URL File" pointing at the URI;
493+
/// applies on next reset. Equivalent to the iLO HPREST/ilorest
494+
/// `select Bios; set UrlBootFile=...; commit` workflow, which is
495+
/// documented on Gen10/iLO 5 and Gen11/iLO 6.
496+
///
497+
/// Verified working on ProLiant DL380a Gen11 machines (running both
498+
/// iLO 6 v1.58 and v1.72). Unlike Dell, there doesn't seem to be any
499+
/// sort of attribute engine dependency drama going on; UrlBootFile is
500+
/// a straightforward string attribute that's writable across the fleet.
501+
///
502+
/// Returns `Ok(None)` on success: HPE doesn't return a Location/job ID
503+
/// here (unlike Dell). The pending state lives in `/Bios/Settings` and
504+
/// applies on next reboot; the response includes `SystemResetRequired`
505+
/// as confirmation that the change is staged.
506+
/// Callers reverting the change PATCH `UrlBootFile: ""` back.
479507
fn set_boot_override<'a>(
480508
&'a self,
481509
settings: BootOverride,
482510
) -> crate::RedfishFuture<'a, Result<Option<String>, RedfishError>> {
483511
Box::pin(async move {
484-
let mut boot_data: HashMap<String, serde_json::Value> = HashMap::new();
485-
boot_data.insert(
486-
"BootSourceOverrideTarget".to_string(),
487-
settings.target.to_string().into(),
488-
);
489-
boot_data.insert(
490-
"BootSourceOverrideEnabled".to_string(),
491-
settings.enabled.to_string().into(),
492-
);
493-
// HPE iLO defaults to UEFI mode when the caller doesn't specify one.
494-
let mode = settings.mode.unwrap_or(BootSourceOverrideMode::UEFI);
495-
boot_data.insert(
496-
"BootSourceOverrideMode".to_string(),
497-
mode.to_string().into(),
498-
);
499-
if let Some(uri) = settings.http_boot_uri {
500-
boot_data.insert("HttpBootUri".to_string(), uri.into());
501-
}
502-
let url = format!("Systems/{}", self.s.system_id());
503-
self.s
504-
.client
505-
.patch(&url, HashMap::from([("Boot", boot_data)]))
506-
.await?;
512+
let Some(uri) = settings.http_boot_uri else {
513+
// HPE iLO 6 does not accept BootSourceOverrideTarget/Enabled
514+
// PATCHes. Even non-HTTP targets like Pxe are rejected with
515+
// UnsupportedOperation. Without an http_boot_uri to set via
516+
// the UrlBootFile BIOS attribute, no Redfish operation here.
517+
return Err(RedfishError::NotSupported(
518+
"HPE set_boot_override requires http_boot_uri; \
519+
BootSourceOverrideTarget/Enabled PATCHes are not \
520+
functional via Redfish on iLO 6 (verified on v1.58 / \
521+
v1.72: UnsupportedOperation for all override targets)"
522+
.to_string(),
523+
));
524+
};
525+
526+
// HPE iLO uses lowercase `settings/` in its @Redfish.Settings
527+
// pointer (matches the existing helpers in this file, e.g.,
528+
// setup_serial_console / clear_tpm).
529+
let url = format!("Systems/{}/Bios/settings/", self.s.system_id());
530+
let body = serde_json::json!({
531+
"Attributes": {
532+
"UrlBootFile": uri,
533+
"PreBootNetwork": "IPv4",
534+
}
535+
});
536+
self.s.client.patch(&url, body).await?;
507537
Ok(None)
508538
})
509539
}

tests/integration_test.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -432,17 +432,24 @@ async fn run_integration_test(
432432
redfish.boot_first(libredfish::Boot::HardDisk).await?;
433433
}
434434

435-
// Exercise set_boot_override on vendors that support it. The mockup server
436-
// does not validate the PATCH body fwiw -- this just verifies the call path
435+
// Exercise set_boot_override on vendors that support the bare (no URI)
436+
// override variant via the standard Redfish Boot block PATCH. The mockup
437+
// doesn't validate the PATCH body -- this just verifies the call path
437438
// compiles, dispatches to the right impl, and reaches a writable endpoint.
438-
// Mirrors the boot_once/boot_first exclusion above: gbswitch + liteon
439-
// mockups don't model the boot-config endpoints, and dell/lenovo return
440-
// NotSupported (tested separately below).
439+
//
440+
// Excluded:
441+
// gbswitch + liteon: mockups don't model the boot-config endpoints
442+
// dell/dell_multi_dpu: tested separately below (BIOS-attribute path)
443+
// lenovo: returns NotSupported (tested separately below)
444+
// hpe: returns NotSupported when http_boot_uri is absent (BIOS-attribute
445+
// path via UrlBootFile is the only HPE-functional mechanism;
446+
// BootSourceOverride PATCHes are rejected by iLO 6 firmware)
441447
if vendor_dir != "dell"
442448
&& vendor_dir != "dell_multi_dpu"
443449
&& vendor_dir != "lenovo"
444450
&& vendor_dir != "liteon_powershelf"
445451
&& vendor_dir != "nvidia_gbswitch"
452+
&& vendor_dir != "hpe"
446453
{
447454
// Bare override (no mode, no URI). Matches what boot_once/boot_first
448455
// do internally for backwards-compatible callers.
@@ -470,6 +477,23 @@ async fn run_integration_test(
470477
.await?;
471478
}
472479

480+
// HPE uses the UrlBootFile BIOS-attribute path. The mockup accepts the
481+
// PATCH (default 204 No Content), and the impl returns Ok(None) since HPE
482+
// doesn't surface a job ID for BIOS attribute changes. Bare override (no
483+
// URI) returns NotSupported on HPE -- we only test the URI-supplied path.
484+
if vendor_dir == "hpe" {
485+
redfish
486+
.set_boot_override(libredfish::BootOverride {
487+
target: libredfish::BootSourceOverrideTarget::UefiHttp,
488+
enabled: libredfish::BootSourceOverrideEnabled::Continuous,
489+
mode: Some(libredfish::BootSourceOverrideMode::UEFI),
490+
http_boot_uri: Some(
491+
"http://example.invalid/public/blobs/internal/x86_64/ipxe.efi".to_string(),
492+
),
493+
})
494+
.await?;
495+
}
496+
473497
// Dell and Lenovo return NotSupported until follow-up PRs implement the
474498
// BIOS-attribute path (HttpDev1Uri etc. on Dell, equivalent on Lenovo XCC).
475499
if vendor_dir == "dell" || vendor_dir == "dell_multi_dpu" || vendor_dir == "lenovo" {

0 commit comments

Comments
 (0)