fix(export): keep storage inventory without serials
This commit is contained in:
@@ -358,10 +358,12 @@ func dedupeCanonicalDevices(items []models.HardwareDevice) []models.HardwareDevi
|
||||
prev.score = canonicalScore(prev.item)
|
||||
byKey[key] = prev
|
||||
}
|
||||
// Secondary pass: for items without serial/BDF (noKey), try to merge into an
|
||||
// existing keyed entry with the same model+manufacturer. This handles the case
|
||||
// where a device appears both in PCIeDevices (with BDF) and NetworkAdapters
|
||||
// (without BDF) — e.g. Inspur outboardPCIeCard vs PCIeCard with the same model.
|
||||
// Secondary pass: for PCIe-class items without serial/BDF (noKey), try to merge
|
||||
// into an existing keyed entry with the same model+manufacturer. This handles
|
||||
// the case where a device appears both in PCIeDevices (with BDF) and
|
||||
// NetworkAdapters (without BDF) — e.g. Inspur outboardPCIeCard vs PCIeCard
|
||||
// with the same model. Do not apply this to storage: repeated NVMe slots often
|
||||
// share the same model string and would collapse incorrectly.
|
||||
// deviceIdentity returns the best available model name for secondary matching,
|
||||
// preferring Model over DeviceClass (which may hold a resolved device name).
|
||||
deviceIdentity := func(d models.HardwareDevice) string {
|
||||
@@ -377,6 +379,10 @@ func dedupeCanonicalDevices(items []models.HardwareDevice) []models.HardwareDevi
|
||||
var unmatched []models.HardwareDevice
|
||||
for _, item := range noKey {
|
||||
mergeKind := canonicalMergeKind(item.Kind)
|
||||
if mergeKind != "pcie-class" {
|
||||
unmatched = append(unmatched, item)
|
||||
continue
|
||||
}
|
||||
identity := deviceIdentity(item)
|
||||
mfr := strings.ToLower(strings.TrimSpace(item.Manufacturer))
|
||||
if identity == "" {
|
||||
@@ -721,18 +727,16 @@ func convertStorageFromDevices(devices []models.HardwareDevice, collectedAt stri
|
||||
if isVirtualExportStorageDevice(d) {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(d.SerialNumber) == "" {
|
||||
continue
|
||||
}
|
||||
present := d.Present == nil || *d.Present
|
||||
if !present {
|
||||
if !shouldExportStorageDevice(d) {
|
||||
continue
|
||||
}
|
||||
present := boolFromPresentPtr(d.Present, true)
|
||||
status := inferStorageStatus(models.Storage{Present: present})
|
||||
if strings.TrimSpace(d.Status) != "" {
|
||||
status = normalizeStatus(d.Status, false)
|
||||
status = normalizeStatus(d.Status, !present)
|
||||
}
|
||||
meta := buildStatusMeta(status, d.StatusCheckedAt, d.StatusChangedAt, d.StatusHistory, d.ErrorDescription, collectedAt)
|
||||
presentValue := present
|
||||
result = append(result, ReanimatorStorage{
|
||||
Slot: d.Slot,
|
||||
Type: d.Type,
|
||||
@@ -742,6 +746,7 @@ func convertStorageFromDevices(devices []models.HardwareDevice, collectedAt stri
|
||||
Manufacturer: d.Manufacturer,
|
||||
Firmware: d.Firmware,
|
||||
Interface: d.Interface,
|
||||
Present: &presentValue,
|
||||
TemperatureC: floatFromDetailMap(d.Details, "temperature_c"),
|
||||
PowerOnHours: int64FromDetailMap(d.Details, "power_on_hours"),
|
||||
PowerCycles: int64FromDetailMap(d.Details, "power_cycles"),
|
||||
@@ -1386,14 +1391,16 @@ func convertStorage(storage []models.Storage, collectedAt string) []ReanimatorSt
|
||||
|
||||
result := make([]ReanimatorStorage, 0, len(storage))
|
||||
for _, stor := range storage {
|
||||
// Skip storage without serial number
|
||||
if stor.SerialNumber == "" {
|
||||
if isVirtualLegacyStorageDevice(stor) {
|
||||
continue
|
||||
}
|
||||
if !shouldExportLegacyStorage(stor) {
|
||||
continue
|
||||
}
|
||||
|
||||
status := inferStorageStatus(stor)
|
||||
if strings.TrimSpace(stor.Status) != "" {
|
||||
status = normalizeStatus(stor.Status, false)
|
||||
status = normalizeStatus(stor.Status, !stor.Present)
|
||||
}
|
||||
meta := buildStatusMeta(
|
||||
status,
|
||||
@@ -1403,6 +1410,7 @@ func convertStorage(storage []models.Storage, collectedAt string) []ReanimatorSt
|
||||
stor.ErrorDescription,
|
||||
collectedAt,
|
||||
)
|
||||
present := stor.Present
|
||||
|
||||
result = append(result, ReanimatorStorage{
|
||||
Slot: stor.Slot,
|
||||
@@ -1413,6 +1421,7 @@ func convertStorage(storage []models.Storage, collectedAt string) []ReanimatorSt
|
||||
Manufacturer: stor.Manufacturer,
|
||||
Firmware: stor.Firmware,
|
||||
Interface: stor.Interface,
|
||||
Present: &present,
|
||||
RemainingEndurancePct: stor.RemainingEndurancePct,
|
||||
Status: status,
|
||||
StatusCheckedAt: meta.StatusCheckedAt,
|
||||
@@ -1424,6 +1433,53 @@ func convertStorage(storage []models.Storage, collectedAt string) []ReanimatorSt
|
||||
return result
|
||||
}
|
||||
|
||||
func shouldExportStorageDevice(d models.HardwareDevice) bool {
|
||||
if normalizedSerial(d.SerialNumber) != "" {
|
||||
return true
|
||||
}
|
||||
if strings.TrimSpace(d.Slot) != "" {
|
||||
return true
|
||||
}
|
||||
if hasMeaningfulExporterText(d.Model) {
|
||||
return true
|
||||
}
|
||||
if hasMeaningfulExporterText(d.Type) || hasMeaningfulExporterText(d.Interface) {
|
||||
return true
|
||||
}
|
||||
if d.SizeGB > 0 {
|
||||
return true
|
||||
}
|
||||
return d.Present != nil
|
||||
}
|
||||
|
||||
func shouldExportLegacyStorage(stor models.Storage) bool {
|
||||
if normalizedSerial(stor.SerialNumber) != "" {
|
||||
return true
|
||||
}
|
||||
if strings.TrimSpace(stor.Slot) != "" {
|
||||
return true
|
||||
}
|
||||
if hasMeaningfulExporterText(stor.Model) {
|
||||
return true
|
||||
}
|
||||
if hasMeaningfulExporterText(stor.Type) || hasMeaningfulExporterText(stor.Interface) {
|
||||
return true
|
||||
}
|
||||
if stor.SizeGB > 0 {
|
||||
return true
|
||||
}
|
||||
return stor.Present
|
||||
}
|
||||
|
||||
func isVirtualLegacyStorageDevice(stor models.Storage) bool {
|
||||
return isVirtualExportStorageDevice(models.HardwareDevice{
|
||||
Kind: models.DeviceKindStorage,
|
||||
Slot: stor.Slot,
|
||||
Model: stor.Model,
|
||||
Manufacturer: stor.Manufacturer,
|
||||
})
|
||||
}
|
||||
|
||||
// convertPCIeDevices converts PCIe devices, GPUs, and network adapters to Reanimator format
|
||||
func convertPCIeDevices(hw *models.HardwareConfig, collectedAt string) []ReanimatorPCIe {
|
||||
result := make([]ReanimatorPCIe, 0)
|
||||
|
||||
Reference in New Issue
Block a user