diff --git a/crates/api-model/src/hardware_info.rs b/crates/api-model/src/hardware_info.rs index 543aebd41e..f1d4ca4b49 100644 --- a/crates/api-model/src/hardware_info.rs +++ b/crates/api-model/src/hardware_info.rs @@ -362,6 +362,7 @@ impl Display for MachineInventorySoftwareComponent { pub struct MachineNvLinkInfo { pub domain_uuid: NvLinkDomainId, /// Chassis serial from the first GPU `GpuPlatformInfo` at discovery (or operator RPC). + #[serde(default)] pub chassis_serial: String, pub gpus: Vec, } @@ -415,6 +416,36 @@ mod tests { const DPU_BF3_INFO_JSON: &[u8] = include_bytes!("hardware_info/test_data/dpu_bf3_info.json"); const X86_INFO_JSON: &[u8] = include_bytes!("hardware_info/test_data/x86_info.json"); + /// Pre-NMX-C rows stored `nvlink_info` without `chassis_serial` (and GPUs carried `nmx_m_id`). + #[test] + fn machine_nvlink_info_deserializes_legacy_json_without_chassis_serial() { + let domain_uuid: NvLinkDomainId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee".parse().unwrap(); + let legacy_json = r#"{ + "domain_uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "gpus": [{ + "nmx_m_id": "legacy-partition-id", + "tray_index": 0, + "slot_id": 1, + "device_id": 1, + "guid": 12345 + }] + }"#; + + let info: MachineNvLinkInfo = serde_json::from_str(legacy_json).unwrap(); + + assert_eq!(info.domain_uuid, domain_uuid); + assert_eq!(info.chassis_serial, ""); + assert_eq!( + info.gpus, + vec![NvLinkGpu { + tray_index: 0, + slot_id: 1, + device_id: 1, + guid: 12345, + }] + ); + } + #[test] fn test_machine_inventory_json_representation() { let inventory = MachineInventory { diff --git a/crates/nvlink-manager/src/lib.rs b/crates/nvlink-manager/src/lib.rs index b8699ac857..5db5c6f350 100644 --- a/crates/nvlink-manager/src/lib.rs +++ b/crates/nvlink-manager/src/lib.rs @@ -159,7 +159,8 @@ fn build_machine_nvlink_info_from_nmx_c_hello( } } -/// Populates missing `machines.nvlink_info` entries (or nil `domain_uuid`) using NMX-C hello. +/// Populates missing `machines.nvlink_info` entries, nil `domain_uuid`, or empty `chassis_serial` +/// using NMX-C hello. fn populate_machine_nvlink_info_if_needed( machine_nvlink_info: &mut HashMap>, managed_host_snapshots: &HashMap, @@ -172,7 +173,13 @@ fn populate_machine_nvlink_info_if_needed( let existing = machine_nvlink_info .get(machine_id) .and_then(|info| info.as_ref()); - if existing.is_some_and(|info| info.domain_uuid != NvLinkDomainId::nil()) { + let needs_update = match existing { + None => true, + Some(info) => { + info.domain_uuid == NvLinkDomainId::nil() || info.chassis_serial.trim().is_empty() + } + }; + if !needs_update { continue; }