feat: add backup collector (pve_not_backed_up_total, pve_not_backed_up_info)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Davíð Steinn Geirsson 2026-03-20 11:33:33 +00:00
parent a62264edf8
commit 5e61f224c4
3 changed files with 107 additions and 0 deletions

69
collector/backup.go Normal file
View file

@ -0,0 +1,69 @@
package collector
import (
"encoding/json"
"fmt"
"log/slog"
"strconv"
"github.com/prometheus/client_golang/prometheus"
)
func init() {
registerCollector("backup", func(logger *slog.Logger) Collector {
return newBackupCollector(logger)
})
}
type backupCollector struct {
logger *slog.Logger
}
func newBackupCollector(logger *slog.Logger) *backupCollector {
return &backupCollector{logger: logger}
}
type backupNotBackedUpResponse struct {
Data []backupNotBackedUpEntry `json:"data"`
}
type backupNotBackedUpEntry struct {
VMID int `json:"vmid"`
Name string `json:"name"`
Type string `json:"type"`
}
var (
notBackedUpTotalDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "not_backed_up_total"),
"Whether a guest is not backed up (1 = not backed up).",
[]string{"id"},
nil,
)
notBackedUpInfoDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "not_backed_up_info"),
"Information about a guest that is not backed up.",
[]string{"id"},
nil,
)
)
func (c *backupCollector) Update(client *Client, ch chan<- prometheus.Metric) error {
body, err := client.Get("/cluster/backup-info/not-backed-up")
if err != nil {
return fmt.Errorf("failed to get /cluster/backup-info/not-backed-up: %w", err)
}
var resp backupNotBackedUpResponse
if err := json.Unmarshal(body, &resp); err != nil {
return fmt.Errorf("failed to parse backup-info response: %w", err)
}
for _, entry := range resp.Data {
id := entry.Type + "/" + strconv.Itoa(entry.VMID)
ch <- prometheus.MustNewConstMetric(notBackedUpTotalDesc, prometheus.GaugeValue, 1, id)
ch <- prometheus.MustNewConstMetric(notBackedUpInfoDesc, prometheus.GaugeValue, 1, id)
}
return nil
}

37
collector/backup_test.go Normal file
View file

@ -0,0 +1,37 @@
package collector
import (
"log/slog"
"strings"
"testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
)
func TestBackupCollector(t *testing.T) {
client := newTestClient(t, map[string]string{
"/cluster/backup-info/not-backed-up": "backup_not_backed_up.json",
})
collector := newBackupCollector(slog.Default())
adapter := &testCollectorAdapter{client: client, collector: collector}
reg := prometheus.NewRegistry()
reg.MustRegister(adapter)
expected := `
# HELP pve_not_backed_up_info Information about a guest that is not backed up.
# TYPE pve_not_backed_up_info gauge
pve_not_backed_up_info{id="qemu/100"} 1
# HELP pve_not_backed_up_total Whether a guest is not backed up (1 = not backed up).
# TYPE pve_not_backed_up_total gauge
pve_not_backed_up_total{id="qemu/100"} 1
`
if err := testutil.GatherAndCompare(reg, strings.NewReader(expected),
"pve_not_backed_up_total", "pve_not_backed_up_info",
); err != nil {
t.Errorf("unexpected metrics: %s", err)
}
}

View file

@ -0,0 +1 @@
{"data":[{"vmid":100,"name":"pve-backup.freyja.sip.is","type":"qemu"}]}