Skip to content

Commit 0b66c81

Browse files
authored
feat: [ami] implement boot_first (#50)
This PR: - Adds a proper `boot_first` implementation for AMI by matching boot options on the alias field (`Pxe`, `Hdd`, `UefiHttp`) and moving the matched entry to the front of the boot order. Previously this delegated to the standard implementation which returned `NotSupported`. - Refactors the duplicated system/boot options fetch logic into shared helpers Signed-off-by: Krish Dandiwala <kdandiwala@nvidia.com>
1 parent 4d5c14b commit 0b66c81

1 file changed

Lines changed: 65 additions & 31 deletions

File tree

src/ami.rs

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,12 @@ impl Redfish for Bmc {
517517
}
518518

519519
async fn boot_first(&self, target: Boot) -> Result<(), RedfishError> {
520-
self.s.boot_first(target).await
520+
let alias = match target {
521+
Boot::Pxe => "Pxe",
522+
Boot::HardDisk => "Hdd",
523+
Boot::UefiHttp => "UefiHttp",
524+
};
525+
self.set_boot_order(alias).await
521526
}
522527

523528
/// AMI BMC requires If-Match header for boot order changes
@@ -742,23 +747,7 @@ impl Redfish for Bmc {
742747
mac_address: &str,
743748
) -> Result<Option<String>, RedfishError> {
744749
let mac = mac_address.to_uppercase();
745-
let system = self.get_system().await?;
746-
747-
let boot_options_id =
748-
system
749-
.boot
750-
.boot_options
751-
.clone()
752-
.ok_or_else(|| RedfishError::MissingKey {
753-
key: "boot.boot_options".to_string(),
754-
url: system.odata.odata_id.clone(),
755-
})?;
756-
757-
let all_boot_options: Vec<BootOption> = self
758-
.get_collection(boot_options_id)
759-
.await
760-
.and_then(|c| c.try_get::<BootOption>())?
761-
.members;
750+
let (system, all_boot_options) = self.get_system_and_boot_options().await?;
762751

763752
let target = all_boot_options.iter().find(|opt| {
764753
let display = opt.display_name.to_uppercase();
@@ -971,19 +960,10 @@ impl Bmc {
971960
self.s.client.patch_with_if_match(&url, data).await
972961
}
973962

974-
/// Get expected and actual first boot option for checking boot order setup.
975-
///
976-
/// AMI boot option format example:
977-
/// DisplayName: "[Slot2]UEFI: HTTP IPv4 Nvidia Network Adapter - B8:E9:24:17:6D:72 P1"
978-
/// BootOptionReference: "Boot0001"
979-
///
980-
async fn get_expected_and_actual_first_boot_option(
963+
async fn get_system_and_boot_options(
981964
&self,
982-
boot_interface_mac: &str,
983-
) -> Result<(Option<String>, Option<String>), RedfishError> {
984-
let mac = boot_interface_mac.to_uppercase();
965+
) -> Result<(ComputerSystem, Vec<BootOption>), RedfishError> {
985966
let system = self.get_system().await?;
986-
987967
let boot_options_id =
988968
system
989969
.boot
@@ -993,14 +973,68 @@ impl Bmc {
993973
key: "boot.boot_options".to_string(),
994974
url: system.odata.odata_id.clone(),
995975
})?;
996-
997976
let all_boot_options: Vec<BootOption> = self
998977
.get_collection(boot_options_id)
999978
.await
1000979
.and_then(|c| c.try_get::<BootOption>())?
1001980
.members;
981+
Ok((system, all_boot_options))
982+
}
983+
984+
/// Finds the first boot option matching the given alias and moves it to the front
985+
/// of the boot order.
986+
async fn set_boot_order(&self, alias: &str) -> Result<(), RedfishError> {
987+
let (system, all_boot_options) = self.get_system_and_boot_options().await?;
988+
989+
let target = all_boot_options
990+
.iter()
991+
.find(|opt| opt.alias.as_deref() == Some(alias));
992+
993+
let target_ref = target
994+
.ok_or_else(|| {
995+
let all_names: Vec<_> = all_boot_options
996+
.iter()
997+
.map(|b| {
998+
format!(
999+
"{}: {} (alias={})",
1000+
b.boot_option_reference,
1001+
b.display_name,
1002+
b.alias.as_deref().unwrap_or("none")
1003+
)
1004+
})
1005+
.collect();
1006+
RedfishError::MissingBootOption(format!(
1007+
"No boot option with alias {:?} found; available: {:#?}",
1008+
alias, all_names
1009+
))
1010+
})?
1011+
.boot_option_reference
1012+
.clone();
1013+
1014+
let mut boot_order = system.boot.boot_order;
1015+
1016+
if boot_order.first() == Some(&target_ref) {
1017+
return Ok(());
1018+
}
1019+
1020+
boot_order.retain(|id| id != &target_ref);
1021+
boot_order.insert(0, target_ref);
1022+
self.change_boot_order(boot_order).await
1023+
}
1024+
1025+
/// Get expected and actual first boot option for checking boot order setup.
1026+
///
1027+
/// AMI boot option format example:
1028+
/// DisplayName: "[Slot2]UEFI: HTTP IPv4 Nvidia Network Adapter - B8:E9:24:17:6D:72 P1"
1029+
/// BootOptionReference: "Boot0001"
1030+
///
1031+
async fn get_expected_and_actual_first_boot_option(
1032+
&self,
1033+
boot_interface_mac: &str,
1034+
) -> Result<(Option<String>, Option<String>), RedfishError> {
1035+
let mac = boot_interface_mac.to_uppercase();
1036+
let (system, all_boot_options) = self.get_system_and_boot_options().await?;
10021037

1003-
// Find expected boot option display name (HTTP IPv4 with matching MAC)
10041038
let expected_first_boot_option = all_boot_options
10051039
.iter()
10061040
.find(|opt| {

0 commit comments

Comments
 (0)