metrics(power): export UPS battery charge and load percent
This commit is contained in:
parent
3dda295cd2
commit
e016b5d096
@ -15,6 +15,8 @@ type Sample struct {
|
||||
OnBattery bool
|
||||
LowBattery bool
|
||||
RuntimeSecond int
|
||||
BatteryCharge float64
|
||||
LoadPercent float64
|
||||
ThresholdSec int
|
||||
Trigger bool
|
||||
BreachCount int
|
||||
@ -100,6 +102,10 @@ func (e *Exporter) serveMetrics(w http.ResponseWriter, _ *http.Request) {
|
||||
b.WriteString("# TYPE hecate_ups_low_battery gauge\n")
|
||||
b.WriteString("# HELP hecate_ups_runtime_seconds Battery runtime remaining reported by UPS.\n")
|
||||
b.WriteString("# TYPE hecate_ups_runtime_seconds gauge\n")
|
||||
b.WriteString("# HELP hecate_ups_battery_charge_percent Battery charge percentage reported by UPS.\n")
|
||||
b.WriteString("# TYPE hecate_ups_battery_charge_percent gauge\n")
|
||||
b.WriteString("# HELP hecate_ups_load_percent UPS output load percentage.\n")
|
||||
b.WriteString("# TYPE hecate_ups_load_percent gauge\n")
|
||||
b.WriteString("# HELP hecate_ups_threshold_seconds Red-line threshold for runtime-based shutdown.\n")
|
||||
b.WriteString("# TYPE hecate_ups_threshold_seconds gauge\n")
|
||||
b.WriteString("# HELP hecate_ups_trigger_active Whether this UPS source currently breaches shutdown trigger conditions.\n")
|
||||
@ -123,6 +129,8 @@ func (e *Exporter) serveMetrics(w http.ResponseWriter, _ *http.Request) {
|
||||
b.WriteString(fmt.Sprintf("hecate_ups_on_battery%s %d\n", labels, boolNum(s.OnBattery)))
|
||||
b.WriteString(fmt.Sprintf("hecate_ups_low_battery%s %d\n", labels, boolNum(s.LowBattery)))
|
||||
b.WriteString(fmt.Sprintf("hecate_ups_runtime_seconds%s %d\n", labels, s.RuntimeSecond))
|
||||
b.WriteString(fmt.Sprintf("hecate_ups_battery_charge_percent%s %.2f\n", labels, s.BatteryCharge))
|
||||
b.WriteString(fmt.Sprintf("hecate_ups_load_percent%s %.2f\n", labels, s.LoadPercent))
|
||||
b.WriteString(fmt.Sprintf("hecate_ups_threshold_seconds%s %d\n", labels, s.ThresholdSec))
|
||||
b.WriteString(fmt.Sprintf("hecate_ups_trigger_active%s %d\n", labels, boolNum(s.Trigger)))
|
||||
b.WriteString(fmt.Sprintf("hecate_ups_breach_count%s %d\n", labels, s.BreachCount))
|
||||
|
||||
@ -16,6 +16,8 @@ func TestExporterEmitsCoreMetrics(t *testing.T) {
|
||||
OnBattery: true,
|
||||
LowBattery: false,
|
||||
RuntimeSecond: 412,
|
||||
BatteryCharge: 81.5,
|
||||
LoadPercent: 27.4,
|
||||
ThresholdSec: 354,
|
||||
Trigger: true,
|
||||
BreachCount: 2,
|
||||
@ -34,6 +36,8 @@ func TestExporterEmitsCoreMetrics(t *testing.T) {
|
||||
"hecate_shutdown_triggers_total 1",
|
||||
"hecate_ups_on_battery{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||
"hecate_ups_runtime_seconds{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||
"hecate_ups_battery_charge_percent{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||
"hecate_ups_load_percent{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||
"hecate_ups_threshold_seconds{source=\"db-ups\",target=\"atlasups@localhost\"",
|
||||
}
|
||||
for _, m := range mustContain {
|
||||
|
||||
@ -129,6 +129,8 @@ func (d *Daemon) Run(ctx context.Context) error {
|
||||
OnBattery: sample.OnBattery,
|
||||
LowBattery: sample.LowBattery,
|
||||
RuntimeSecond: sample.RuntimeSeconds,
|
||||
BatteryCharge: sample.BatteryCharge,
|
||||
LoadPercent: sample.LoadPercent,
|
||||
ThresholdSec: threshold,
|
||||
Trigger: trigger,
|
||||
BreachCount: breachCount[target.Name],
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@ -13,6 +14,8 @@ type Sample struct {
|
||||
OnBattery bool
|
||||
LowBattery bool
|
||||
RuntimeSeconds int
|
||||
BatteryCharge float64
|
||||
LoadPercent float64
|
||||
RawStatus string
|
||||
}
|
||||
|
||||
@ -82,5 +85,29 @@ func parseNUT(raw string) (Sample, error) {
|
||||
out.RuntimeSeconds = runtime
|
||||
}
|
||||
}
|
||||
if chargeRaw := kv["battery.charge"]; chargeRaw != "" {
|
||||
if charge, ok := parseNumber(chargeRaw); ok {
|
||||
out.BatteryCharge = charge
|
||||
}
|
||||
}
|
||||
if loadRaw := kv["ups.load"]; loadRaw != "" {
|
||||
if load, ok := parseNumber(loadRaw); ok {
|
||||
out.LoadPercent = load
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
var parseNumberCleaner = regexp.MustCompile(`[^0-9.+-]`)
|
||||
|
||||
func parseNumber(raw string) (float64, bool) {
|
||||
cleaned := strings.TrimSpace(parseNumberCleaner.ReplaceAllString(raw, ""))
|
||||
if cleaned == "" {
|
||||
return 0, false
|
||||
}
|
||||
v, err := strconv.ParseFloat(cleaned, 64)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ import "testing"
|
||||
|
||||
func TestParseNUT(t *testing.T) {
|
||||
raw := `battery.runtime: 384
|
||||
battery.charge: 72
|
||||
ups.load: 19
|
||||
ups.status: OB LB
|
||||
`
|
||||
s, err := parseNUT(raw)
|
||||
@ -19,4 +21,10 @@ ups.status: OB LB
|
||||
if s.RuntimeSeconds != 384 {
|
||||
t.Fatalf("expected runtime 384, got %d", s.RuntimeSeconds)
|
||||
}
|
||||
if s.BatteryCharge != 72 {
|
||||
t.Fatalf("expected battery charge 72, got %.2f", s.BatteryCharge)
|
||||
}
|
||||
if s.LoadPercent != 19 {
|
||||
t.Fatalf("expected load 19, got %.2f", s.LoadPercent)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user