Fix PSU power chart: use name-based SDR matching instead of entity ID
MSI servers place PSU_POWER_IN/OUT sensors on entity 3.0, not 10.N (the IPMI "Power Supply" entity). The old parser filtered by entity ID and found nothing, so the dashboard fell back to DCMI which reports roughly half the actual draw. Now delegates to collector.PSUSlotsFromSDR — the same name-based matching already used in the Power Fit benchmark. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
package platform
|
package platform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bee/audit/internal/collector"
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -339,63 +341,44 @@ func compactAmbientTempName(chip, name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// samplePSUPower reads per-PSU input power via IPMI SDR.
|
// samplePSUPower reads per-PSU input power via IPMI SDR.
|
||||||
// It parses `ipmitool sdr elist full` output looking for Power Supply entity
|
// Uses collector.PSUSlotsFromSDR (name-based matching) which works across
|
||||||
// sensors (entity ID "10.N") that report a value in Watts.
|
// vendors where PSU sensors may not carry entity ID "10.N".
|
||||||
// Returns nil when IPMI is unavailable or no PSU Watt sensors exist.
|
// Returns nil when IPMI is unavailable or no PSU Watt sensors exist.
|
||||||
func samplePSUPower() []PSUReading {
|
func samplePSUPower() []PSUReading {
|
||||||
out, err := exec.Command("ipmitool", "sdr", "elist", "full").Output()
|
out, err := exec.Command("ipmitool", "sdr").Output()
|
||||||
if err != nil || len(out) == 0 {
|
if err != nil || len(out) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// map slot → reading (keep highest-watt value per slot in case of duplicates)
|
slots := collector.PSUSlotsFromSDR(string(out))
|
||||||
type entry struct {
|
if len(slots) == 0 {
|
||||||
name string
|
|
||||||
powerW float64
|
|
||||||
}
|
|
||||||
bySlot := map[int]entry{}
|
|
||||||
for _, line := range strings.Split(string(out), "\n") {
|
|
||||||
parts := strings.Split(line, "|")
|
|
||||||
if len(parts) < 5 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
entityID := strings.TrimSpace(parts[3]) // e.g. "10.1"
|
|
||||||
if !strings.HasPrefix(entityID, "10.") {
|
|
||||||
continue // not a Power Supply entity
|
|
||||||
}
|
|
||||||
slotStr := strings.TrimPrefix(entityID, "10.")
|
|
||||||
slot, err := strconv.Atoi(slotStr)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
valueField := strings.TrimSpace(parts[4]) // e.g. "740.00 Watts"
|
|
||||||
if !strings.Contains(strings.ToLower(valueField), "watts") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
valueFields := strings.Fields(valueField)
|
|
||||||
if len(valueFields) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
w, err := strconv.ParseFloat(valueFields[0], 64)
|
|
||||||
if err != nil || w <= 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sensorName := strings.TrimSpace(parts[0])
|
|
||||||
if existing, ok := bySlot[slot]; !ok || w > existing.powerW {
|
|
||||||
bySlot[slot] = entry{name: sensorName, powerW: w}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(bySlot) == 0 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
slots := make([]int, 0, len(bySlot))
|
// Collect slot keys and sort for stable output.
|
||||||
for s := range bySlot {
|
keys := make([]int, 0, len(slots))
|
||||||
slots = append(slots, s)
|
for k := range slots {
|
||||||
|
n, err := strconv.Atoi(k)
|
||||||
|
if err == nil {
|
||||||
|
keys = append(keys, n)
|
||||||
}
|
}
|
||||||
sort.Ints(slots)
|
}
|
||||||
psus := make([]PSUReading, 0, len(slots))
|
sort.Ints(keys)
|
||||||
for _, s := range slots {
|
psus := make([]PSUReading, 0, len(keys))
|
||||||
e := bySlot[s]
|
for _, k := range keys {
|
||||||
psus = append(psus, PSUReading{Slot: s, Name: e.name, PowerW: e.powerW})
|
entry := slots[strconv.Itoa(k)]
|
||||||
|
// Prefer AC input power; fall back to DC output power.
|
||||||
|
var w float64
|
||||||
|
if entry.InputW != nil && *entry.InputW > 0 {
|
||||||
|
w = *entry.InputW
|
||||||
|
} else if entry.OutputW != nil && *entry.OutputW > 0 {
|
||||||
|
w = *entry.OutputW
|
||||||
|
}
|
||||||
|
if w <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
psus = append(psus, PSUReading{Slot: k + 1, Name: fmt.Sprintf("PSU%d", k+1), PowerW: w})
|
||||||
|
}
|
||||||
|
if len(psus) == 0 {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return psus
|
return psus
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user