592 lines
25 KiB
Go
592 lines
25 KiB
Go
package ingest
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"reanimator/internal/normalize"
|
|
)
|
|
|
|
const (
|
|
statusUnknown = "UNKNOWN"
|
|
statusEmpty = "EMPTY"
|
|
statusOK = "OK"
|
|
statusWarning = "WARNING"
|
|
statusCritical = "CRITICAL"
|
|
)
|
|
|
|
type HardwareIngestRequest struct {
|
|
Filename *string `json:"filename"`
|
|
SourceType *string `json:"source_type"`
|
|
Protocol *string `json:"protocol"`
|
|
TargetHost string `json:"target_host"`
|
|
CollectedAt string `json:"collected_at"`
|
|
Hardware HardwareSnapshot `json:"hardware"`
|
|
}
|
|
|
|
type HardwareSnapshot struct {
|
|
Board HardwareBoard `json:"board"`
|
|
Firmware []HardwareFirmwareRecord `json:"firmware,omitempty"`
|
|
CPUs []HardwareCPU `json:"cpus,omitempty"`
|
|
Memory []HardwareMemory `json:"memory,omitempty"`
|
|
Storage []HardwareStorage `json:"storage,omitempty"`
|
|
PCIeDevices []HardwarePCIeDevice `json:"pcie_devices,omitempty"`
|
|
PowerSupplies []HardwarePowerSupply `json:"power_supplies,omitempty"`
|
|
Sensors *HardwareSensors `json:"sensors,omitempty"`
|
|
}
|
|
|
|
type HardwareSensors struct {
|
|
Fans []HardwareFanSensor `json:"fans,omitempty"`
|
|
Power []HardwarePowerSensor `json:"power,omitempty"`
|
|
Temperatures []HardwareTempSensor `json:"temperatures,omitempty"`
|
|
Other []HardwareOtherSensor `json:"other,omitempty"`
|
|
}
|
|
|
|
type HardwareFanSensor struct {
|
|
Name string `json:"name"`
|
|
Location *string `json:"location,omitempty"`
|
|
RPM *int `json:"rpm,omitempty"`
|
|
Status *string `json:"status,omitempty"`
|
|
}
|
|
|
|
type HardwarePowerSensor struct {
|
|
Name string `json:"name"`
|
|
Location *string `json:"location,omitempty"`
|
|
VoltageV *float64 `json:"voltage_v,omitempty"`
|
|
CurrentA *float64 `json:"current_a,omitempty"`
|
|
PowerW *float64 `json:"power_w,omitempty"`
|
|
Status *string `json:"status,omitempty"`
|
|
}
|
|
|
|
type HardwareTempSensor struct {
|
|
Name string `json:"name"`
|
|
Location *string `json:"location,omitempty"`
|
|
Celsius *float64 `json:"celsius,omitempty"`
|
|
ThresholdWarningCelsius *float64 `json:"threshold_warning_celsius,omitempty"`
|
|
ThresholdCriticalCelsius *float64 `json:"threshold_critical_celsius,omitempty"`
|
|
Status *string `json:"status,omitempty"`
|
|
}
|
|
|
|
type HardwareOtherSensor struct {
|
|
Name string `json:"name"`
|
|
Location *string `json:"location,omitempty"`
|
|
Value *float64 `json:"value,omitempty"`
|
|
Unit *string `json:"unit,omitempty"`
|
|
Status *string `json:"status,omitempty"`
|
|
}
|
|
|
|
type HardwareBoard struct {
|
|
Manufacturer *string `json:"manufacturer"`
|
|
ProductName *string `json:"product_name"`
|
|
SerialNumber string `json:"serial_number"`
|
|
PartNumber *string `json:"part_number"`
|
|
UUID *string `json:"uuid"`
|
|
}
|
|
|
|
type HardwareFirmwareRecord struct {
|
|
DeviceName string `json:"device_name"`
|
|
Version string `json:"version"`
|
|
}
|
|
|
|
type HardwareCPU struct {
|
|
Socket *int `json:"socket"`
|
|
Model *string `json:"model"`
|
|
Manufacturer *string `json:"manufacturer"`
|
|
TemperatureC *float64 `json:"temperature_c"`
|
|
PowerW *float64 `json:"power_w"`
|
|
Throttled *bool `json:"throttled"`
|
|
CorrectableErrorCount *int `json:"correctable_error_count"`
|
|
UncorrectableErrorCount *int `json:"uncorrectable_error_count"`
|
|
LifeRemainingPct *float64 `json:"life_remaining_pct"`
|
|
LifeUsedPct *float64 `json:"life_used_pct"`
|
|
Status *string `json:"status"`
|
|
StatusCheckedAt *string `json:"status_checked_at"`
|
|
StatusChangedAt *string `json:"status_changed_at"`
|
|
StatusHistory []HardwareStatusHistoryEntry `json:"status_history,omitempty"`
|
|
ErrorDescription *string `json:"error_description"`
|
|
ManufacturedYearWeek *string `json:"manufactured_year_week,omitempty"`
|
|
Present *bool `json:"present"`
|
|
SerialNumber *string `json:"serial_number"`
|
|
Firmware *string `json:"firmware"`
|
|
Cores *int `json:"cores"`
|
|
Threads *int `json:"threads"`
|
|
FrequencyMHz *int `json:"frequency_mhz"`
|
|
MaxFrequencyMHz *int `json:"max_frequency_mhz"`
|
|
}
|
|
|
|
type HardwareMemory struct {
|
|
Slot *string `json:"slot"`
|
|
Present *bool `json:"present"`
|
|
SizeMB *int `json:"size_mb"`
|
|
Type *string `json:"type"`
|
|
MaxSpeedMHz *int `json:"max_speed_mhz"`
|
|
CurrentSpeedMHz *int `json:"current_speed_mhz"`
|
|
TemperatureC *float64 `json:"temperature_c"`
|
|
Manufacturer *string `json:"manufacturer"`
|
|
SerialNumber *string `json:"serial_number"`
|
|
PartNumber *string `json:"part_number"`
|
|
CorrectableECCErrorCount *int `json:"correctable_ecc_error_count"`
|
|
UncorrectableECCErrorCount *int `json:"uncorrectable_ecc_error_count"`
|
|
LifeRemainingPct *float64 `json:"life_remaining_pct"`
|
|
LifeUsedPct *float64 `json:"life_used_pct"`
|
|
SpareBlocksRemainingPct *float64 `json:"spare_blocks_remaining_pct"`
|
|
PerformanceDegraded *bool `json:"performance_degraded"`
|
|
DataLossDetected *bool `json:"data_loss_detected"`
|
|
Status *string `json:"status"`
|
|
StatusCheckedAt *string `json:"status_checked_at"`
|
|
StatusChangedAt *string `json:"status_changed_at"`
|
|
StatusHistory []HardwareStatusHistoryEntry `json:"status_history,omitempty"`
|
|
ErrorDescription *string `json:"error_description"`
|
|
ManufacturedYearWeek *string `json:"manufactured_year_week,omitempty"`
|
|
}
|
|
|
|
type HardwareStorage struct {
|
|
Slot *string `json:"slot"`
|
|
Type *string `json:"type"`
|
|
Model *string `json:"model"`
|
|
SizeGB *int `json:"size_gb"`
|
|
TemperatureC *float64 `json:"temperature_c"`
|
|
PowerOnHours *int64 `json:"power_on_hours"`
|
|
PowerCycles *int64 `json:"power_cycles"`
|
|
UnsafeShutdowns *int64 `json:"unsafe_shutdowns"`
|
|
MediaErrors *int64 `json:"media_errors"`
|
|
ErrorLogEntries *int64 `json:"error_log_entries"`
|
|
WrittenBytes *int64 `json:"written_bytes"`
|
|
ReadBytes *int64 `json:"read_bytes"`
|
|
LifeUsedPct *float64 `json:"life_used_pct"`
|
|
LifeRemainingPct *float64 `json:"life_remaining_pct"`
|
|
AvailableSparePct *float64 `json:"available_spare_pct"`
|
|
ReallocatedSectors *int64 `json:"reallocated_sectors"`
|
|
CurrentPendingSectors *int64 `json:"current_pending_sectors"`
|
|
OfflineUncorrectable *int64 `json:"offline_uncorrectable"`
|
|
SerialNumber *string `json:"serial_number"`
|
|
Manufacturer *string `json:"manufacturer"`
|
|
Firmware *string `json:"firmware"`
|
|
Interface *string `json:"interface"`
|
|
Present *bool `json:"present"`
|
|
Status *string `json:"status"`
|
|
StatusCheckedAt *string `json:"status_checked_at"`
|
|
StatusChangedAt *string `json:"status_changed_at"`
|
|
StatusHistory []HardwareStatusHistoryEntry `json:"status_history,omitempty"`
|
|
ErrorDescription *string `json:"error_description"`
|
|
ManufacturedYearWeek *string `json:"manufactured_year_week,omitempty"`
|
|
}
|
|
|
|
type HardwarePCIeDevice struct {
|
|
Slot *string `json:"slot"`
|
|
VendorID *int `json:"vendor_id"`
|
|
DeviceID *int `json:"device_id"`
|
|
NUMANode *int `json:"numa_node"`
|
|
TemperatureC *float64 `json:"temperature_c"`
|
|
PowerW *float64 `json:"power_w"`
|
|
LifeRemainingPct *float64 `json:"life_remaining_pct"`
|
|
LifeUsedPct *float64 `json:"life_used_pct"`
|
|
ECCCorrectedTotal *int64 `json:"ecc_corrected_total"`
|
|
ECCUncorrectedTotal *int64 `json:"ecc_uncorrected_total"`
|
|
HWSlowdown *bool `json:"hw_slowdown"`
|
|
BatteryChargePct *float64 `json:"battery_charge_pct"`
|
|
BatteryHealthPct *float64 `json:"battery_health_pct"`
|
|
BatteryTemperatureC *float64 `json:"battery_temperature_c"`
|
|
BatteryVoltageV *float64 `json:"battery_voltage_v"`
|
|
BatteryReplaceRequired *bool `json:"battery_replace_required"`
|
|
SFPTemperatureC *float64 `json:"sfp_temperature_c"`
|
|
SFPTxPowerDBm *float64 `json:"sfp_tx_power_dbm"`
|
|
SFPRxPowerDBm *float64 `json:"sfp_rx_power_dbm"`
|
|
SFPVoltageV *float64 `json:"sfp_voltage_v"`
|
|
SFPBiasMA *float64 `json:"sfp_bias_ma"`
|
|
BDF *string `json:"bdf"`
|
|
DeviceClass *string `json:"device_class"`
|
|
Manufacturer *string `json:"manufacturer"`
|
|
Model *string `json:"model"`
|
|
LinkWidth *int `json:"link_width"`
|
|
LinkSpeed *string `json:"link_speed"`
|
|
MaxLinkWidth *int `json:"max_link_width"`
|
|
MaxLinkSpeed *string `json:"max_link_speed"`
|
|
SerialNumber *string `json:"serial_number"`
|
|
Firmware *string `json:"firmware"`
|
|
MACAddresses []string `json:"mac_addresses,omitempty"`
|
|
Present *bool `json:"present"`
|
|
Status *string `json:"status"`
|
|
StatusCheckedAt *string `json:"status_checked_at"`
|
|
StatusChangedAt *string `json:"status_changed_at"`
|
|
StatusHistory []HardwareStatusHistoryEntry `json:"status_history,omitempty"`
|
|
ErrorDescription *string `json:"error_description"`
|
|
ManufacturedYearWeek *string `json:"manufactured_year_week,omitempty"`
|
|
}
|
|
|
|
type HardwarePowerSupply struct {
|
|
Slot *string `json:"slot"`
|
|
Present *bool `json:"present"`
|
|
Model *string `json:"model"`
|
|
Vendor *string `json:"vendor"`
|
|
WattageW *int `json:"wattage_w"`
|
|
SerialNumber *string `json:"serial_number"`
|
|
PartNumber *string `json:"part_number"`
|
|
Firmware *string `json:"firmware"`
|
|
Status *string `json:"status"`
|
|
StatusCheckedAt *string `json:"status_checked_at"`
|
|
StatusChangedAt *string `json:"status_changed_at"`
|
|
StatusHistory []HardwareStatusHistoryEntry `json:"status_history,omitempty"`
|
|
ErrorDescription *string `json:"error_description"`
|
|
ManufacturedYearWeek *string `json:"manufactured_year_week,omitempty"`
|
|
InputType *string `json:"input_type"`
|
|
InputPowerW *float64 `json:"input_power_w"`
|
|
OutputPowerW *float64 `json:"output_power_w"`
|
|
InputVoltage *float64 `json:"input_voltage"`
|
|
TemperatureC *float64 `json:"temperature_c"`
|
|
LifeRemainingPct *float64 `json:"life_remaining_pct"`
|
|
LifeUsedPct *float64 `json:"life_used_pct"`
|
|
}
|
|
|
|
type HardwareStatusHistoryEntry struct {
|
|
Status string `json:"status"`
|
|
ChangedAt string `json:"changed_at"`
|
|
Details *string `json:"details,omitempty"`
|
|
}
|
|
|
|
type HardwareComponent struct {
|
|
ComponentType string `json:"component_type"`
|
|
VendorSerial string `json:"vendor_serial"`
|
|
Vendor *string `json:"vendor,omitempty"`
|
|
Model *string `json:"model,omitempty"`
|
|
Firmware *string `json:"firmware,omitempty"`
|
|
Status string `json:"status"`
|
|
StatusCheckedAt *string `json:"status_checked_at,omitempty"`
|
|
StatusChangedAt *string `json:"status_changed_at,omitempty"`
|
|
StatusHistory []HardwareStatusHistoryEntry `json:"status_history,omitempty"`
|
|
ErrorDescription *string `json:"error_description,omitempty"`
|
|
Present *bool `json:"present,omitempty"`
|
|
Slot *string `json:"slot,omitempty"`
|
|
Attributes map[string]any `json:"attributes,omitempty"`
|
|
Telemetry map[string]any `json:"telemetry,omitempty"`
|
|
}
|
|
|
|
func FlattenHardwareComponents(snapshot HardwareSnapshot) ([]HardwareComponent, []HardwareFirmwareRecord) {
|
|
boardSerial := strings.TrimSpace(snapshot.Board.SerialNumber)
|
|
if boardSerial == "" {
|
|
return nil, normalizeFirmwareRecords(snapshot.Firmware)
|
|
}
|
|
|
|
components := make([]HardwareComponent, 0)
|
|
components = append(components, flattenCPUs(boardSerial, snapshot.CPUs)...)
|
|
components = append(components, flattenMemory(boardSerial, snapshot.Memory)...)
|
|
components = append(components, flattenStorage(boardSerial, snapshot.Storage)...)
|
|
components = append(components, flattenPCIe(boardSerial, snapshot.PCIeDevices)...)
|
|
components = append(components, flattenPSUs(boardSerial, snapshot.PowerSupplies)...)
|
|
|
|
firmware := normalizeFirmwareRecords(snapshot.Firmware)
|
|
return components, firmware
|
|
}
|
|
|
|
func normalizeFirmwareRecords(entries []HardwareFirmwareRecord) []HardwareFirmwareRecord {
|
|
normalized := make([]HardwareFirmwareRecord, 0, len(entries))
|
|
for _, entry := range entries {
|
|
name := strings.TrimSpace(entry.DeviceName)
|
|
version := strings.TrimSpace(entry.Version)
|
|
if name == "" || version == "" {
|
|
continue
|
|
}
|
|
normalized = append(normalized, HardwareFirmwareRecord{DeviceName: name, Version: version})
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
func flattenCPUs(boardSerial string, items []HardwareCPU) []HardwareComponent {
|
|
result := make([]HardwareComponent, 0)
|
|
for _, item := range items {
|
|
status := normalizeStatus(item.Status)
|
|
present := resolvePresent(item.Present, true)
|
|
if !present || status == statusEmpty {
|
|
continue
|
|
}
|
|
serial := normalizeSerial(item.SerialNumber)
|
|
if serial == "" {
|
|
serial = generateCPUVendorSerial(boardSerial, item.Socket)
|
|
}
|
|
if serial == "" {
|
|
continue
|
|
}
|
|
comp := HardwareComponent{
|
|
ComponentType: "cpu",
|
|
VendorSerial: serial,
|
|
Vendor: normalize.VendorDisplayPtr(normalizeString(item.Manufacturer)),
|
|
Model: normalizeString(item.Model),
|
|
Firmware: normalizeString(item.Firmware),
|
|
Status: status,
|
|
StatusCheckedAt: normalizeString(item.StatusCheckedAt),
|
|
StatusChangedAt: normalizeString(item.StatusChangedAt),
|
|
StatusHistory: normalizeStatusHistory(item.StatusHistory),
|
|
ErrorDescription: normalizeString(item.ErrorDescription),
|
|
Present: boolPtr(present),
|
|
Attributes: structToMap(item),
|
|
Telemetry: extractTelemetry(structToMap(item), []string{"cores", "threads", "frequency_mhz", "max_frequency_mhz", "temperature_c", "power_w", "throttled", "correctable_error_count", "uncorrectable_error_count", "life_remaining_pct", "life_used_pct"}),
|
|
}
|
|
result = append(result, comp)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func flattenMemory(boardSerial string, items []HardwareMemory) []HardwareComponent {
|
|
result := make([]HardwareComponent, 0)
|
|
for _, item := range items {
|
|
status := normalizeStatus(item.Status)
|
|
present := resolvePresent(item.Present, true)
|
|
if !present || status == statusEmpty {
|
|
continue
|
|
}
|
|
serial := normalizeSerial(item.SerialNumber)
|
|
if serial == "" {
|
|
continue
|
|
}
|
|
comp := HardwareComponent{
|
|
ComponentType: "memory",
|
|
VendorSerial: serial,
|
|
Vendor: normalize.VendorDisplayPtr(normalizeString(item.Manufacturer)),
|
|
Model: normalizeString(item.PartNumber),
|
|
Firmware: nil,
|
|
Status: status,
|
|
StatusCheckedAt: normalizeString(item.StatusCheckedAt),
|
|
StatusChangedAt: normalizeString(item.StatusChangedAt),
|
|
StatusHistory: normalizeStatusHistory(item.StatusHistory),
|
|
ErrorDescription: normalizeString(item.ErrorDescription),
|
|
Present: boolPtr(present),
|
|
Slot: normalizeString(item.Slot),
|
|
Attributes: structToMap(item),
|
|
Telemetry: extractTelemetry(structToMap(item), []string{"size_mb", "max_speed_mhz", "current_speed_mhz", "temperature_c", "correctable_ecc_error_count", "uncorrectable_ecc_error_count", "life_remaining_pct", "life_used_pct", "spare_blocks_remaining_pct", "performance_degraded", "data_loss_detected"}),
|
|
}
|
|
result = append(result, comp)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func flattenStorage(boardSerial string, items []HardwareStorage) []HardwareComponent {
|
|
result := make([]HardwareComponent, 0)
|
|
for _, item := range items {
|
|
status := normalizeStatus(item.Status)
|
|
present := resolvePresent(item.Present, true)
|
|
if !present || status == statusEmpty {
|
|
continue
|
|
}
|
|
serial := normalizeSerial(item.SerialNumber)
|
|
if serial == "" {
|
|
continue
|
|
}
|
|
comp := HardwareComponent{
|
|
ComponentType: "storage",
|
|
VendorSerial: serial,
|
|
Vendor: normalize.VendorDisplayPtr(normalizeString(item.Manufacturer)),
|
|
Model: normalizeString(item.Model),
|
|
Firmware: normalizeString(item.Firmware),
|
|
Status: status,
|
|
StatusCheckedAt: normalizeString(item.StatusCheckedAt),
|
|
StatusChangedAt: normalizeString(item.StatusChangedAt),
|
|
StatusHistory: normalizeStatusHistory(item.StatusHistory),
|
|
ErrorDescription: normalizeString(item.ErrorDescription),
|
|
Present: boolPtr(present),
|
|
Slot: normalizeString(item.Slot),
|
|
Attributes: structToMap(item),
|
|
Telemetry: extractTelemetry(structToMap(item), []string{"size_gb", "temperature_c", "power_on_hours", "power_cycles", "unsafe_shutdowns", "media_errors", "error_log_entries", "written_bytes", "read_bytes", "life_used_pct", "life_remaining_pct", "available_spare_pct", "reallocated_sectors", "current_pending_sectors", "offline_uncorrectable"}),
|
|
}
|
|
result = append(result, comp)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func flattenPCIe(boardSerial string, items []HardwarePCIeDevice) []HardwareComponent {
|
|
result := make([]HardwareComponent, 0)
|
|
for _, item := range items {
|
|
status := normalizeStatus(item.Status)
|
|
present := resolvePresent(item.Present, true)
|
|
if !present || status == statusEmpty {
|
|
continue
|
|
}
|
|
slot := normalizePCIeSlot(item.Slot, item.BDF)
|
|
serial := normalizeSerial(item.SerialNumber)
|
|
if serial == "" {
|
|
serial = generatePCIEVendorSerial(boardSerial, slot)
|
|
}
|
|
if serial == "" {
|
|
continue
|
|
}
|
|
comp := HardwareComponent{
|
|
ComponentType: "pcie_device",
|
|
VendorSerial: serial,
|
|
Vendor: normalize.VendorDisplayPtr(normalizeString(item.Manufacturer)),
|
|
Model: normalizeString(item.Model),
|
|
Firmware: normalizeString(item.Firmware),
|
|
Status: status,
|
|
StatusCheckedAt: normalizeString(item.StatusCheckedAt),
|
|
StatusChangedAt: normalizeString(item.StatusChangedAt),
|
|
StatusHistory: normalizeStatusHistory(item.StatusHistory),
|
|
ErrorDescription: normalizeString(item.ErrorDescription),
|
|
Present: boolPtr(present),
|
|
Slot: slot,
|
|
Attributes: structToMap(item),
|
|
Telemetry: extractTelemetry(structToMap(item), []string{"vendor_id", "device_id", "numa_node", "temperature_c", "power_w", "life_remaining_pct", "life_used_pct", "ecc_corrected_total", "ecc_uncorrected_total", "hw_slowdown", "battery_charge_pct", "battery_health_pct", "battery_temperature_c", "battery_voltage_v", "battery_replace_required", "sfp_temperature_c", "sfp_tx_power_dbm", "sfp_rx_power_dbm", "sfp_voltage_v", "sfp_bias_ma"}),
|
|
}
|
|
result = append(result, comp)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func flattenPSUs(boardSerial string, items []HardwarePowerSupply) []HardwareComponent {
|
|
result := make([]HardwareComponent, 0)
|
|
for _, item := range items {
|
|
status := normalizeStatus(item.Status)
|
|
present := resolvePresent(item.Present, true)
|
|
if !present || status == statusEmpty {
|
|
continue
|
|
}
|
|
serial := normalizeSerial(item.SerialNumber)
|
|
if serial == "" {
|
|
continue
|
|
}
|
|
comp := HardwareComponent{
|
|
ComponentType: "power_supply",
|
|
VendorSerial: serial,
|
|
Vendor: normalize.VendorDisplayPtr(normalizeString(item.Vendor)),
|
|
Model: normalizeString(item.Model),
|
|
Firmware: normalizeString(item.Firmware),
|
|
Status: status,
|
|
StatusCheckedAt: normalizeString(item.StatusCheckedAt),
|
|
StatusChangedAt: normalizeString(item.StatusChangedAt),
|
|
StatusHistory: normalizeStatusHistory(item.StatusHistory),
|
|
ErrorDescription: normalizeString(item.ErrorDescription),
|
|
Present: boolPtr(present),
|
|
Slot: normalizeString(item.Slot),
|
|
Attributes: structToMap(item),
|
|
Telemetry: extractTelemetry(structToMap(item), []string{"wattage_w", "input_power_w", "output_power_w", "input_voltage", "temperature_c", "life_remaining_pct", "life_used_pct"}),
|
|
}
|
|
result = append(result, comp)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func normalizeSerial(value *string) string {
|
|
if value == nil {
|
|
return ""
|
|
}
|
|
trimmed := strings.TrimSpace(*value)
|
|
if trimmed == "" {
|
|
return ""
|
|
}
|
|
upper := strings.ToUpper(trimmed)
|
|
if upper == "NULL" || upper == "N/A" {
|
|
return ""
|
|
}
|
|
return trimmed
|
|
}
|
|
|
|
func generateCPUVendorSerial(boardSerial string, socket *int) string {
|
|
if boardSerial == "" || socket == nil {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("%s-CPU-%d", boardSerial, *socket)
|
|
}
|
|
|
|
func generatePCIEVendorSerial(boardSerial string, slot *string) string {
|
|
if boardSerial == "" || slot == nil {
|
|
return ""
|
|
}
|
|
trimmed := strings.TrimSpace(*slot)
|
|
if trimmed == "" {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("%s-PCIE-%s", boardSerial, trimmed)
|
|
}
|
|
|
|
func normalizePCIeSlot(slot, bdf *string) *string {
|
|
if normalizedBDF := normalizeString(bdf); normalizedBDF != nil {
|
|
return normalizedBDF
|
|
}
|
|
return normalizeString(slot)
|
|
}
|
|
|
|
func resolvePresent(value *bool, defaultValue bool) bool {
|
|
if value == nil {
|
|
return defaultValue
|
|
}
|
|
return *value
|
|
}
|
|
|
|
func normalizeStatus(value *string) string {
|
|
if value == nil {
|
|
return statusUnknown
|
|
}
|
|
normalized := strings.ToUpper(strings.TrimSpace(*value))
|
|
if normalized == "" {
|
|
return statusUnknown
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
func normalizeStatusHistory(entries []HardwareStatusHistoryEntry) []HardwareStatusHistoryEntry {
|
|
if len(entries) == 0 {
|
|
return nil
|
|
}
|
|
result := make([]HardwareStatusHistoryEntry, 0, len(entries))
|
|
for _, entry := range entries {
|
|
status := normalizeStatus(&entry.Status)
|
|
changedAt := strings.TrimSpace(entry.ChangedAt)
|
|
if changedAt == "" {
|
|
continue
|
|
}
|
|
result = append(result, HardwareStatusHistoryEntry{
|
|
Status: status,
|
|
ChangedAt: changedAt,
|
|
Details: normalizeString(entry.Details),
|
|
})
|
|
}
|
|
if len(result) == 0 {
|
|
return nil
|
|
}
|
|
return result
|
|
}
|
|
|
|
func boolPtr(value bool) *bool {
|
|
return &value
|
|
}
|
|
|
|
func normalizeString(value *string) *string {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
trimmed := strings.TrimSpace(*value)
|
|
if trimmed == "" || strings.EqualFold(trimmed, "NULL") {
|
|
return nil
|
|
}
|
|
return &trimmed
|
|
}
|
|
|
|
func structToMap(value any) map[string]any {
|
|
data, err := json.Marshal(value)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var result map[string]any
|
|
if err := json.Unmarshal(data, &result); err != nil {
|
|
return nil
|
|
}
|
|
return result
|
|
}
|
|
|
|
func extractTelemetry(attributes map[string]any, keys []string) map[string]any {
|
|
if len(attributes) == 0 {
|
|
return nil
|
|
}
|
|
telemetry := make(map[string]any)
|
|
for _, key := range keys {
|
|
if v, ok := attributes[key]; ok {
|
|
switch v.(type) {
|
|
case float64, int, int64, float32, bool:
|
|
telemetry[key] = v
|
|
}
|
|
}
|
|
}
|
|
if len(telemetry) == 0 {
|
|
return nil
|
|
}
|
|
return telemetry
|
|
}
|