From 796aa339e0c406bc4f7fcce5f506451015e2dbe0 Mon Sep 17 00:00:00 2001 From: Chet Nichols III Date: Sat, 20 Jun 2026 23:25:11 -0700 Subject: [PATCH] feat: honor a declared primary NIC in the explored boot default #2657 made a declared ExpectedHostNic.primary authoritative in machine_interfaces, the managed store every real boot path reads. This closes the matching gap in the explored default: the boot interface site-explorer records in explored_endpoints before any machine owns the endpoint, which a preingestion admin action (machine_setup, Restore) reads. That default came from an automatic, DPU-centric pick that filters to DPU host-PF MACs, so it could never name an integrated NIC -- a host that declared an integrated NIC as primary got a DPU NIC (or nothing) as its explored default. Now the declaration wins there too. fetch_host_primary_interface_mac takes the declared primary and returns it when this report has that NIC as a full pair (any type -- integrated or DPU host-PF); absent a declaration, today's lowest-PCI DPU-PF pick stands. The site-explorer caller feeds it the matched expected machine's declared primary, reusing #2657's declared_primary_mac(). The explored default and the managed store now agree on the declaration across the ownership handoff. - fetch_host_primary_interface_mac gains a declared_primary arg; a declared NIC present in the report wins whatever its type, via the existing find_interface_id_for_mac (which scans every system ethernet interface). - The exploration loop passes the matched expected machine's declared primary; the explored-default capture (id resolve / last-known-good) is untouched. - No declaration -> the explored default is unchanged from today. Tests extend test_fetch_host_primary_interface_mac: a declared DPU host-PF wins over the lowest-PCI pick, a declared integrated NIC becomes the default, a declared MAC absent from the report falls back, and the no-declaration pick is unchanged. Part of #2662 (epic #2660). Signed-off-by: Chet Nichols III --- crates/api-model/src/site_explorer/mod.rs | 18 ++++++++ crates/site-explorer/src/lib.rs | 8 +++- crates/site-explorer/tests/site_explorer.rs | 46 ++++++++++++++++++++- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/crates/api-model/src/site_explorer/mod.rs b/crates/api-model/src/site_explorer/mod.rs index 32e0e51b5b..438b18e3bb 100644 --- a/crates/api-model/src/site_explorer/mod.rs +++ b/crates/api-model/src/site_explorer/mod.rs @@ -307,10 +307,28 @@ impl ExploredEndpoint { } impl EndpointExplorationReport { + /// The boot interface MAC for this endpoint's explored default -- the boot + /// interface site-explorer records before any machine owns the endpoint. + /// + /// A declared `ExpectedHostNic.primary` wins when this report has that NIC, + /// whatever its type (an integrated NIC as readily as a DPU host-PF), so the + /// explored default agrees with the managed store's declared primary across + /// the ownership handoff. Absent a declaration, it falls back to the + /// automatic pick: the lowest-PCI DPU host-PF interface. pub fn fetch_host_primary_interface_mac( &self, explored_dpus: &[ExploredDpu], + declared_primary: Option, ) -> Option { + // A declared primary wins as long as the report has it as a full pair + // (`find_interface_id_for_mac` scans every system ethernet interface, + // integrated NICs included). + if let Some(declared) = declared_primary + && self.find_interface_id_for_mac(declared).is_some() + { + return Some(declared); + } + let system = self.systems.first()?; // Gather explored DPUs mac. diff --git a/crates/site-explorer/src/lib.rs b/crates/site-explorer/src/lib.rs index 30fbd68458..29a75d9a1c 100644 --- a/crates/site-explorer/src/lib.rs +++ b/crates/site-explorer/src/lib.rs @@ -1323,9 +1323,15 @@ impl SiteExplorer { // If we know the booting interface of the host, we should use this for deciding // primary interface. let mut is_sorted = false; + // A declared `ExpectedHostNic.primary` (when the matched expected + // machine sets one) wins over the automatic DPU-PF pick, so the + // explored default names the same NIC the managed store will. + let declared_primary = expected_explored_endpoint_index + .matched_expected_machine(&ep.address) + .and_then(|expected| expected.data.declared_primary_mac()); if let Some(mac_address) = ep .report - .fetch_host_primary_interface_mac(&dpus_explored_for_host) + .fetch_host_primary_interface_mac(&dpus_explored_for_host, declared_primary) { // Capture the boot interface's [stable] Redfish interface id // alongside its MAC. Only persist when both resolve from the diff --git a/crates/site-explorer/tests/site_explorer.rs b/crates/site-explorer/tests/site_explorer.rs index f62f085d8e..295855e744 100644 --- a/crates/site-explorer/tests/site_explorer.rs +++ b/crates/site-explorer/tests/site_explorer.rs @@ -2252,11 +2252,55 @@ async fn test_fetch_host_primary_interface_mac( }); } + // No declaration: the automatic pick stands -- the lowest-PCI DPU host-PF + // (the second mock DPU, given the device paths set above). let expected_mac: MacAddress = mock_dpus[1].host_mac_address; let mac = host_report - .fetch_host_primary_interface_mac(&explored_dpus) + .fetch_host_primary_interface_mac(&explored_dpus, None) .unwrap(); assert_eq!(mac, expected_mac); + + // A declared primary on a DPU host-PF wins over the automatic pick -- here + // the first DPU, which the PCI ordering would NOT have chosen. + let declared_dpu_pf = mock_dpus[0].host_mac_address; + assert_eq!( + host_report + .fetch_host_primary_interface_mac(&explored_dpus, Some(declared_dpu_pf)) + .unwrap(), + declared_dpu_pf, + ); + + // The headline case: a declared *integrated* NIC -- which the DPU-only + // automatic pick can never name -- becomes the explored default. + let integrated_nic = host_report + .systems + .first() + .unwrap() + .ethernet_interfaces + .iter() + .filter_map(|e| e.mac_address) + .find(|mac| { + !explored_dpus + .iter() + .any(|d| d.host_pf_mac_address == Some(*mac)) + }) + .expect("the fixture host should have a non-DPU integrated NIC"); + assert_eq!( + host_report + .fetch_host_primary_interface_mac(&explored_dpus, Some(integrated_nic)) + .unwrap(), + integrated_nic, + ); + + // A declared MAC absent from this report is ignored -- the automatic pick + // stands. + let absent_mac: MacAddress = "de:ad:be:ef:00:01".parse().unwrap(); + assert_eq!( + host_report + .fetch_host_primary_interface_mac(&explored_dpus, Some(absent_mac)) + .unwrap(), + expected_mac, + ); Ok(()) }