fix(metrics): strip units from GPU legend names; fix fan SDR parsing for new IPMI format
Legend names were "GPU 0 %" — remove unit suffix since chart title already
conveys it. Fan parsing now handles the 5-field IPMI SDR format where the
value+unit ("4340 RPM") are combined in the last column rather than split
across separate fields.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -322,7 +322,9 @@ func sampleFanSpeeds() ([]FanReading, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseFanSpeeds parses "ipmitool sdr type Fan" output.
|
// parseFanSpeeds parses "ipmitool sdr type Fan" output.
|
||||||
// Line format: "FAN1 | 2400.000 | RPM | ok"
|
// Handles two formats:
|
||||||
|
// Old: "FAN1 | 2400.000 | RPM | ok" (value in col[1], unit in col[2])
|
||||||
|
// New: "FAN1 | 41h | ok | 29.1 | 4340 RPM" (value+unit combined in last col)
|
||||||
func parseFanSpeeds(raw string) []FanReading {
|
func parseFanSpeeds(raw string) []FanReading {
|
||||||
var fans []FanReading
|
var fans []FanReading
|
||||||
for _, line := range strings.Split(strings.TrimSpace(raw), "\n") {
|
for _, line := range strings.Split(strings.TrimSpace(raw), "\n") {
|
||||||
@@ -330,25 +332,39 @@ func parseFanSpeeds(raw string) []FanReading {
|
|||||||
if len(parts) < 2 {
|
if len(parts) < 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
unit := ""
|
name := strings.TrimSpace(parts[0])
|
||||||
if len(parts) >= 3 {
|
// Find the first field that contains "RPM" (either as a standalone unit or inline)
|
||||||
unit = strings.TrimSpace(parts[2])
|
rpmVal := 0.0
|
||||||
|
found := false
|
||||||
|
for _, p := range parts[1:] {
|
||||||
|
p = strings.TrimSpace(p)
|
||||||
|
if !strings.Contains(strings.ToUpper(p), "RPM") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.EqualFold(p, "RPM") {
|
||||||
|
continue // unit-only column in old format; value is in previous field
|
||||||
|
}
|
||||||
|
val, err := parseFanRPMValue(p)
|
||||||
|
if err == nil {
|
||||||
|
rpmVal = val
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
valStr := strings.TrimSpace(parts[1])
|
// Old format: unit "RPM" is in col[2], value is in col[1]
|
||||||
if !strings.EqualFold(unit, "RPM") && !strings.Contains(strings.ToUpper(valStr), "RPM") {
|
if !found && len(parts) >= 3 && strings.EqualFold(strings.TrimSpace(parts[2]), "RPM") {
|
||||||
|
valStr := strings.TrimSpace(parts[1])
|
||||||
|
if !strings.EqualFold(valStr, "na") && !strings.EqualFold(valStr, "disabled") && valStr != "" {
|
||||||
|
if val, err := parseFanRPMValue(valStr); err == nil {
|
||||||
|
rpmVal = val
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.EqualFold(valStr, "na") || strings.EqualFold(valStr, "disabled") || valStr == "" {
|
fans = append(fans, FanReading{Name: name, RPM: rpmVal})
|
||||||
continue
|
|
||||||
}
|
|
||||||
val, err := parseFanRPMValue(valStr)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fans = append(fans, FanReading{
|
|
||||||
Name: strings.TrimSpace(parts[0]),
|
|
||||||
RPM: val,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return fans
|
return fans
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -459,7 +459,7 @@ func (h *handler) handleMetricsChartSVG(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
vUtil, l := gr.Util.snapshot()
|
vUtil, l := gr.Util.snapshot()
|
||||||
datasets = append(datasets, vUtil)
|
datasets = append(datasets, vUtil)
|
||||||
names = append(names, fmt.Sprintf("GPU %d %%", idx))
|
names = append(names, fmt.Sprintf("GPU %d", idx))
|
||||||
if len(labels) == 0 {
|
if len(labels) == 0 {
|
||||||
labels = l
|
labels = l
|
||||||
}
|
}
|
||||||
@@ -477,7 +477,7 @@ func (h *handler) handleMetricsChartSVG(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
vMem, l := gr.MemUtil.snapshot()
|
vMem, l := gr.MemUtil.snapshot()
|
||||||
datasets = append(datasets, vMem)
|
datasets = append(datasets, vMem)
|
||||||
names = append(names, fmt.Sprintf("GPU %d %%", idx))
|
names = append(names, fmt.Sprintf("GPU %d", idx))
|
||||||
if len(labels) == 0 {
|
if len(labels) == 0 {
|
||||||
labels = l
|
labels = l
|
||||||
}
|
}
|
||||||
@@ -495,7 +495,7 @@ func (h *handler) handleMetricsChartSVG(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
vPow, l := gr.Power.snapshot()
|
vPow, l := gr.Power.snapshot()
|
||||||
datasets = append(datasets, vPow)
|
datasets = append(datasets, vPow)
|
||||||
names = append(names, fmt.Sprintf("GPU %d W", idx))
|
names = append(names, fmt.Sprintf("GPU %d", idx))
|
||||||
if len(labels) == 0 {
|
if len(labels) == 0 {
|
||||||
labels = l
|
labels = l
|
||||||
}
|
}
|
||||||
@@ -513,7 +513,7 @@ func (h *handler) handleMetricsChartSVG(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
vTemp, l := gr.Temp.snapshot()
|
vTemp, l := gr.Temp.snapshot()
|
||||||
datasets = append(datasets, vTemp)
|
datasets = append(datasets, vTemp)
|
||||||
names = append(names, fmt.Sprintf("GPU %d °C", idx))
|
names = append(names, fmt.Sprintf("GPU %d", idx))
|
||||||
if len(labels) == 0 {
|
if len(labels) == 0 {
|
||||||
labels = l
|
labels = l
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user