matchesGPUVendor (sat_overlay.go) matched any PCIe DeviceClass containing the substring "Controller" instead of the actual GPU classes, so a same-vendor non-GPU device (e.g. an NVIDIA-branded NIC/storage controller) with a degraded PCIe link surfaced as a pcie:gpu:<vendor> alarm on the Hardware Summary/webui. isGPUDevice (app_format.go) had the same bug via a different path: an "any NVIDIA-vendor device is a GPU" fallback that could inflate the "GPU: N x <model>" audit summary line with non-GPU companion devices (NVSwitch/NVLink bridges, etc). Both now defer to collector.IsGPUClass (exported from the previously unexported isGPUClass), the canonical exact-match classifier already used by amdgpu.go — removing the duplicated/incorrect class checks instead of reimplementing them. Also dedup two more locally-duplicated classifiers within webui (which doesn't import collector by convention): NIC-class matching (pages.go/page_topo.go) and GPU-class matching in page_validate.go now share one copy per package instead of being reimplemented per file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
112 lines
3.4 KiB
Go
112 lines
3.4 KiB
Go
package collector
|
|
|
|
import (
|
|
"bee/audit/internal/schema"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
statusOK = "OK"
|
|
statusWarning = "Warning"
|
|
statusCritical = "Critical"
|
|
statusUnknown = "Unknown"
|
|
statusEmpty = "Empty"
|
|
)
|
|
|
|
func mapPCIeDeviceClass(raw string) string {
|
|
normalized := strings.ToLower(strings.TrimSpace(raw))
|
|
switch {
|
|
case normalized == "":
|
|
return ""
|
|
case strings.Contains(normalized, "ethernet controller"):
|
|
return "EthernetController"
|
|
case strings.Contains(normalized, "fibre channel"):
|
|
return "FibreChannelController"
|
|
case strings.Contains(normalized, "network controller"), strings.Contains(normalized, "infiniband controller"):
|
|
return "NetworkController"
|
|
case strings.Contains(normalized, "serial attached scsi"), strings.Contains(normalized, "storage controller"):
|
|
return "StorageController"
|
|
case strings.Contains(normalized, "raid"), strings.Contains(normalized, "mass storage"):
|
|
return "MassStorageController"
|
|
case strings.Contains(normalized, "display controller"):
|
|
return "DisplayController"
|
|
case strings.Contains(normalized, "vga"), strings.Contains(normalized, "3d controller"), strings.Contains(normalized, "video controller"):
|
|
return "VideoController"
|
|
case strings.Contains(normalized, "processing accelerators"), strings.Contains(normalized, "processing accelerator"):
|
|
return "ProcessingAccelerator"
|
|
default:
|
|
return raw
|
|
}
|
|
}
|
|
|
|
func isNICClass(class string) bool {
|
|
switch strings.TrimSpace(class) {
|
|
case "EthernetController", "NetworkController":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// IsGPUClass reports whether a normalized PCIe device class (see
|
|
// mapPCIeDeviceClass) identifies a GPU/accelerator die itself, as opposed to
|
|
// a same-vendor companion device (NIC, storage controller, NVLink bridge,
|
|
// audio/USB function, ...) that happens to share the GPU's PCI vendor ID.
|
|
func IsGPUClass(class string) bool {
|
|
switch strings.TrimSpace(class) {
|
|
case "VideoController", "DisplayController", "ProcessingAccelerator":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isRAIDClass(class string) bool {
|
|
switch strings.TrimSpace(class) {
|
|
case "MassStorageController", "StorageController":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// statusSeverity ranks component statuses so merges can only escalate, never
|
|
// downgrade. Unknown ranks with OK: it means "couldn't tell", not "healthy",
|
|
// so it must not silently clear a Warning/Critical raised by an earlier stage.
|
|
func statusSeverity(status string) int {
|
|
switch strings.TrimSpace(status) {
|
|
case statusCritical:
|
|
return 3
|
|
case statusWarning:
|
|
return 2
|
|
case statusOK:
|
|
return 1
|
|
case statusUnknown:
|
|
return 1
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// mergeDeviceStatus applies status/description to dev only if it is at least
|
|
// as severe as whatever is already set. This lets later enrichment stages
|
|
// (e.g. NVIDIA telemetry) report their own findings without silently
|
|
// clobbering a Warning/Critical raised earlier in the collector pipeline
|
|
// (e.g. a PCIe link-speed degradation).
|
|
func mergeDeviceStatus(dev *schema.HardwarePCIeDevice, status, description string) {
|
|
if dev == nil || status == "" {
|
|
return
|
|
}
|
|
current := ""
|
|
if dev.Status != nil {
|
|
current = strings.TrimSpace(*dev.Status)
|
|
}
|
|
if current != "" && current != statusUnknown && statusSeverity(status) <= statusSeverity(current) {
|
|
return
|
|
}
|
|
dev.Status = &status
|
|
if strings.TrimSpace(description) != "" {
|
|
dev.ErrorDescription = &description
|
|
}
|
|
}
|