f-37: prometheus: add vms
All checks were successful
Pre Release Workflow / set-release-target (push) Successful in 1s
Pre Release Workflow / build (agent, amd64, linux) (push) Successful in 1m35s
Pre Release Workflow / build (metadata, amd64, linux) (push) Successful in 1m31s
Pre Release Workflow / upload-scripts (run-dnsmasq-in-netns.sh) (push) Successful in 8s
Pre Release Workflow / prerelease (push) Successful in 10s

Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
GnomeZworc 2026-08-14 01:00:00 +02:00
commit 8d04472ec1
Signed by: nicolas.boufideline
GPG key ID: 4406BBBF8845D632
2 changed files with 36 additions and 3 deletions

View file

@ -17,6 +17,7 @@ type AgentCollector struct {
db *badger.DB db *badger.DB
vpcsTotal *prometheus.Desc vpcsTotal *prometheus.Desc
subnetsTotal *prometheus.Desc subnetsTotal *prometheus.Desc
vmsTotal *prometheus.Desc
} }
func NewAgentCollector(db *badger.DB) *AgentCollector { func NewAgentCollector(db *badger.DB) *AgentCollector {
@ -32,17 +33,24 @@ func NewAgentCollector(db *badger.DB) *AgentCollector {
"Number of subnets by state.", "Number of subnets by state.",
[]string{"state"}, nil, []string{"state"}, nil,
), ),
vmsTotal: prometheus.NewDesc(
"syonad_vms_total",
"Number of VMs by state.",
[]string{"state"}, nil,
),
} }
} }
func (c *AgentCollector) Describe(ch chan<- *prometheus.Desc) { func (c *AgentCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.vpcsTotal ch <- c.vpcsTotal
ch <- c.subnetsTotal ch <- c.subnetsTotal
ch <- c.vmsTotal
} }
func (c *AgentCollector) Collect(ch chan<- prometheus.Metric) { func (c *AgentCollector) Collect(ch chan<- prometheus.Metric) {
c.collectStates(ch, "vpc/", c.vpcsTotal) c.collectStates(ch, "vpc/", c.vpcsTotal)
c.collectStates(ch, "subnet/", c.subnetsTotal) c.collectStates(ch, "subnet/", c.subnetsTotal)
c.collectStates(ch, "vm/", c.vmsTotal)
} }
// collectStates counts resources under the given DB prefix by their state value // collectStates counts resources under the given DB prefix by their state value

View file

@ -42,6 +42,8 @@ func collect(t *testing.T, c *AgentCollector) map[string]map[string]float64 {
name = "vpcs" name = "vpcs"
case strings.Contains(desc, "syonad_subnets_total"): case strings.Contains(desc, "syonad_subnets_total"):
name = "subnets" name = "subnets"
case strings.Contains(desc, "syonad_vms_total"):
name = "vms"
default: default:
t.Fatalf("métrique inattendue : %s", desc) t.Fatalf("métrique inattendue : %s", desc)
} }
@ -64,6 +66,8 @@ func TestCollector_CountsByState(t *testing.T) {
"vpc/vpc-2": state.Running, "vpc/vpc-2": state.Running,
"vpc/vpc-3": state.Error, "vpc/vpc-3": state.Error,
"subnet/sn-1": state.Creating, "subnet/sn-1": state.Creating,
"vm/vm-1": state.Running,
"vm/vm-2": state.Error,
} { } {
if err := state.Set(db, resource, s); err != nil { if err := state.Set(db, resource, s); err != nil {
t.Fatalf("préparation du test : %v", err) t.Fatalf("préparation du test : %v", err)
@ -80,13 +84,34 @@ func TestCollector_CountsByState(t *testing.T) {
if got["subnets"]["creating"] != 1 { if got["subnets"]["creating"] != 1 {
t.Errorf("subnets{state=creating} = %v, attendu 1", got["subnets"]["creating"]) t.Errorf("subnets{state=creating} = %v, attendu 1", got["subnets"]["creating"])
} }
for s, want := range map[string]float64{"running": 1, "error": 1, "deleting": 0} {
if got["vms"][s] != want {
t.Errorf("vms{state=%q} = %v, attendu %v", s, got["vms"][s], want)
}
}
}
// Les clés vm/<name>/disk/<dev> ne portent pas de suffixe /state : elles ne
// doivent pas être comptées comme des ressources.
func TestCollector_IgnoresVMDiskKeys(t *testing.T) {
db := newTestDB(t)
if err := state.Set(db, "vm/vm-1", state.Running); err != nil {
t.Fatalf("préparation du test : %v", err)
}
kv.AddInDB(db, "vm/vm-1/disk/sda", "/data/sda.qcow2")
kv.AddInDB(db, "vm/vm-1/disk/vda", "/data/vda.qcow2")
got := collect(t, NewAgentCollector(db))
if got["vms"]["running"] != 1 {
t.Errorf("vms{state=running} = %v, attendu 1", got["vms"]["running"])
}
} }
// Une série par état doit être émise même à zéro : une métrique qui disparaît // Une série par état doit être émise même à zéro : une métrique qui disparaît
// côté Prometheus casse les alertes qui s'en servent. // côté Prometheus casse les alertes qui s'en servent.
func TestCollector_EmitsEveryState(t *testing.T) { func TestCollector_EmitsEveryState(t *testing.T) {
got := collect(t, NewAgentCollector(newTestDB(t))) got := collect(t, NewAgentCollector(newTestDB(t)))
for _, family := range []string{"vpcs", "subnets"} { for _, family := range []string{"vpcs", "subnets", "vms"} {
if len(got[family]) != len(state.All()) { if len(got[family]) != len(state.All()) {
t.Errorf("%s : %d séries, attendu %d", family, len(got[family]), len(state.All())) t.Errorf("%s : %d séries, attendu %d", family, len(got[family]), len(state.All()))
} }
@ -120,7 +145,7 @@ func TestCollector_Describe(t *testing.T) {
for range ch { for range ch {
n++ n++
} }
if n != 2 { if n != 3 {
t.Errorf("%d descripteurs, attendu 2", n) t.Errorf("%d descripteurs, attendu 3", n)
} }
} }