Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ authors = ["NVIDIA Carbide Engineering <carbide-dev@exchange.nvidia.com>"]

[workspace.dependencies]
clap = { version = "4", features = ["derive", "env"] }
libredfish = { git = "https://github.com/NVIDIA/libredfish.git", tag = "v0.44.15" }
libredfish = { git = "https://github.com/NVIDIA/libredfish.git", tag = "v0.44.16" }
librms = { git = "https://github.com/NVIDIA/nv-rms-client.git", tag = "v0.9.0-rc1" }
ansi-to-html = "0.2.2"

Expand Down
45 changes: 45 additions & 0 deletions crates/api-integration-tests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ async fn test_integration() -> eyre::Result<()> {
Ipv4Addr::new(172, 20, 0, 2),
)
.boxed(),
test_machine_a_tron_empty_ndf_mac(
&test_env,
&bmc_address_registry,
// Relay IP in host-inband net (zero-DPU host)
Ipv4Addr::new(10, 10, 11, 2),
)
.boxed(),
]);

tokio::select! {
Expand Down Expand Up @@ -334,6 +341,7 @@ async fn test_metrics_integration() -> eyre::Result<()> {
1,
1,
false,
false, // empty_ndf_mac
&test_env,
&bmc_address_registry,
Ipv4Addr::new(172, 20, 0, 1),
Expand Down Expand Up @@ -481,6 +489,7 @@ async fn test_machine_a_tron_multidpu(
1,
2,
false,
false, // empty_ndf_mac
test_env,
bmc_mock_registry,
admin_dhcp_relay_address,
Expand Down Expand Up @@ -563,6 +572,7 @@ async fn test_machine_a_tron_zerodpu(
1,
0,
false,
false, // empty_ndf_mac
test_env,
bmc_mock_registry,
admin_dhcp_relay_address,
Expand Down Expand Up @@ -612,6 +622,36 @@ async fn test_machine_a_tron_zerodpu(
.await
}

/// A Dell host whose boot NIC reports an empty `NetworkDeviceFunction` MAC while
/// its `EthernetInterface` MAC stays populated -- the NicMode-stripped / iDRAC
/// partition case. The boot interface still resolves as a `Pair` (MAC + the
/// interface id captured at exploration), so `PollingBiosSetup`'s `is_bios_setup`
/// MAC lookup fails and falls back to the interface id. Reaching `Ready` proves
/// the host no longer hangs in `HostInit` / `PollingBiosSetup`.
async fn test_machine_a_tron_empty_ndf_mac(
test_env: &IntegrationTestEnvironment,
bmc_mock_registry: &BmcMockRegistry,
admin_dhcp_relay_address: Ipv4Addr,
) -> eyre::Result<()> {
run_machine_a_tron_test(
HostHardwareType::DellPowerEdgeR750,
1,
0,
false,
true, // empty_ndf_mac
test_env,
bmc_mock_registry,
admin_dhcp_relay_address,
|machine_handle| async move {
machine_handle
.wait_until_machine_up_with_api_state("Ready", Duration::from_secs(90))
.await?;
Ok::<(), eyre::Report>(())
},
)
.await
}

async fn test_machine_a_tron_singledpu_nic_mode(
hw_type: HostHardwareType,
test_env: &IntegrationTestEnvironment,
Expand All @@ -623,6 +663,7 @@ async fn test_machine_a_tron_singledpu_nic_mode(
1,
1,
true,
false, // empty_ndf_mac
test_env,
bmc_mock_registry,
admin_dhcp_relay_address,
Expand Down Expand Up @@ -686,6 +727,7 @@ async fn test_machine_a_tron_dual_stack(
1,
1,
false,
false, // empty_ndf_mac
test_env,
bmc_mock_registry,
admin_dhcp_relay_address,
Expand Down Expand Up @@ -793,6 +835,7 @@ async fn test_machine_a_tron_dual_stack_l2(
1,
1,
false,
false, // empty_ndf_mac
test_env,
bmc_mock_registry,
admin_dhcp_relay_address,
Expand Down Expand Up @@ -853,6 +896,7 @@ async fn run_machine_a_tron_test<F, O>(
host_count: u32,
dpu_per_host_count: u32,
dpus_in_nic_mode: bool,
empty_ndf_mac: bool,
test_env: &IntegrationTestEnvironment,
bmc_mock_registry: &BmcMockRegistry,
admin_dhcp_relay_address: Ipv4Addr,
Expand Down Expand Up @@ -896,6 +940,7 @@ where
scout_run_interval: Duration::from_secs(1),
network_virtualization_type: None,
dpus_in_nic_mode,
empty_ndf_mac,
dpu_firmware_versions: None,
dpu_agent_version: None,
}),
Expand Down
15 changes: 14 additions & 1 deletion crates/bmc-mock/src/hw/dell_poweredge_r750.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ pub struct DellPowerEdgeR750<'a> {
pub product_serial_number: Cow<'a, str>,
pub nics: Vec<(hw::nic::SlotNumber, hw::nic::Nic<'a>)>,
pub embedded_nic: EmbeddedNic,
/// Serve an empty `NetworkDeviceFunction` MAC for the boot NIC (its
/// `EthernetInterface` MAC stays populated), reproducing a NicMode-stripped
/// iDRAC partition where the boot NIC must be resolved by interface id.
pub empty_ndf_mac: bool,
}

pub struct EmbeddedNic {
Expand Down Expand Up @@ -194,8 +198,17 @@ impl DellPowerEdgeR750<'_> {
&network_adapter_id,
&function_id,
);
// The NetworkDeviceFunction MAC normally mirrors the NIC's MAC, but a
// NicMode-stripped boot NIC (or an iDRAC partition) can leave it empty
// while the EthernetInterface still carries the MAC -- serve that
// asymmetry on request so the boot NIC resolves by interface id.
let ndf_mac_address = if self.empty_ndf_mac {
String::new()
} else {
nic.mac_address.to_string()
};
let function = redfish::network_device_function::builder(func_resource)
.ethernet(json!({"MACAddress": &nic.mac_address}))
.ethernet(json!({"MACAddress": ndf_mac_address}))
.oem(redfish::oem::dell::network_device_function::dell_nic_info(
&function_id,
*slot,
Expand Down
5 changes: 5 additions & 0 deletions crates/bmc-mock/src/machine_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ pub struct HostMachineInfo {
pub nvos_mac_addresses: Vec<MacAddress>,
pub switch_serial_number: Option<String>,
pub hw_mac_addr_pool: MacAddressPoolConfig,
/// Serve an empty boot-NIC `NetworkDeviceFunction` MAC (Dell hosts only),
/// exercising boot-NIC resolution by interface id. Defaults to false.
pub empty_ndf_mac: bool,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -177,6 +180,7 @@ impl HostMachineInfo {
switch_serial_number,
dpus,
hw_mac_addr_pool,
empty_ndf_mac: false,
}
}

Expand Down Expand Up @@ -393,6 +397,7 @@ impl HostMachineInfo {
port_1: next_mac(),
port_2: next_mac(),
},
empty_ndf_mac: self.empty_ndf_mac,
}
}

Expand Down
7 changes: 7 additions & 0 deletions crates/machine-a-tron/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ pub struct MachineConfig {

#[serde(default)]
pub dpu_agent_version: Option<String>,

/// If true, this host's boot-NIC `NetworkDeviceFunction` reports an empty MAC
/// while its `EthernetInterface` MAC stays populated -- a Dell-only quirk for
/// exercising boot-NIC resolution by interface id. Honored only by hosts whose
/// hardware models a `NetworkDeviceFunction` (currently `DellPowerEdgeR750`).
#[serde(default)]
pub empty_ndf_mac: bool,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Eq, PartialEq)]
Expand Down
4 changes: 3 additions & 1 deletion crates/machine-a-tron/src/host_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl HostMachine {
nvos_mac_addresses: persisted_host_machine.nvos_mac_addresses.clone(),
switch_serial_number: persisted_host_machine.switch_serial_number.clone(),
hw_mac_addr_pool,
empty_ndf_mac: config.empty_ndf_mac,
};
let dpus = dpu_machines
.into_iter()
Expand Down Expand Up @@ -182,12 +183,13 @@ impl HostMachine {
)
})
.collect::<Vec<_>>();
let host_info = HostMachineInfo::new(
let mut host_info = HostMachineInfo::new(
config.hw_type,
dpu_machines.iter().map(|d| d.dpu_info().clone()).collect(),
mac_pool,
hw_pool_config,
);
host_info.empty_ndf_mac = config.empty_ndf_mac;
let dpus = dpu_machines
.into_iter()
.map(|d| d.start(true))
Expand Down
Loading