mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-22 19:23:31 +00:00
1dc19bfec0
Recorded PVE 8 and PVE 9 guest rrddata responses (fixtures under
pkg/proxmox/testdata/rrd/) prove guest RRD never carries the cache-aware
memused/memavailable columns — they exist only in node RRD — so every
consumer branch reading them was dead code that #1634's listing fallback
(7d7d2b6a3) had already routed around.
Drop the two fields from GuestRRDPoint (now time/maxmem only, matching
the recordings), delete the dead VM RRD memory fallback and its
getVMRRDMetrics/getVMRRDMemory helpers plus the vmRRDMemCache they fed,
remove the pointless per-poll guest RRD fetch from the LXC memory path,
and retire the guest RRD lookups from PVEClientInterface. VMMemoryRaw
loses its never-populated RRD diagnostic fields, and guest reliability
scoring no longer treats the node-only rrd-* sources as trusted guest
evidence. The knownDeadGuestRRDFields allowlist in the fixture
alignment test is gone; a new reflection guard in
code_standards_test.go keeps GuestRRDPoint pinned to recorded columns,
and cleanupRRDCache pruning of the guest-agent meminfo cache gains
direct coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
232 lines
6.5 KiB
Go
232 lines
6.5 KiB
Go
package monitoring
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/proxmox"
|
|
)
|
|
|
|
func TestStabilizeGuestLowTrustMemoryCarriesForwardTrustedSnapshot(t *testing.T) {
|
|
const gib = uint64(1024 * 1024 * 1024)
|
|
|
|
now := time.Now()
|
|
prev := &GuestMemorySnapshot{
|
|
Status: "running",
|
|
RetrievedAt: now.Add(-time.Minute),
|
|
MemorySource: "guest-agent-meminfo",
|
|
Memory: models.Memory{
|
|
Total: 8 * int64(gib),
|
|
Used: 3 * int64(gib),
|
|
Usage: safePercentage(float64(3*gib), float64(8*gib)),
|
|
},
|
|
}
|
|
|
|
used, source, notes := stabilizeGuestLowTrustMemory(prev, "running", "status-freemem", 8*gib, 8*gib, now, false)
|
|
if used != 3*gib {
|
|
t.Fatalf("used = %d, want %d", used, 3*gib)
|
|
}
|
|
if source != "previous-snapshot" {
|
|
t.Fatalf("source = %q, want previous-snapshot", source)
|
|
}
|
|
if len(notes) != 1 || notes[0] != "preserved-previous-memory-after-repeated-low-trust-pattern" {
|
|
t.Fatalf("notes = %#v, want repeated low-trust carry-forward note", notes)
|
|
}
|
|
}
|
|
|
|
func TestStabilizeGuestLowTrustMemoryCarriesForwardAcrossUnavailableReconnect(t *testing.T) {
|
|
const gib = uint64(1024 * 1024 * 1024)
|
|
|
|
now := time.Now()
|
|
prev := &GuestMemorySnapshot{
|
|
Status: "running",
|
|
RetrievedAt: now.Add(-30 * time.Second),
|
|
MemorySource: "guest-agent-meminfo",
|
|
Memory: models.Memory{
|
|
Total: 8 * int64(gib),
|
|
Used: 3 * int64(gib),
|
|
Free: 5 * int64(gib),
|
|
Usage: 37.5,
|
|
},
|
|
}
|
|
|
|
used, source, notes := stabilizeGuestLowTrustMemory(prev, "running", "unavailable", 8*gib, 0, now, false)
|
|
if used != 3*gib {
|
|
t.Fatalf("used = %d, want %d", used, 3*gib)
|
|
}
|
|
if source != "previous-snapshot" {
|
|
t.Fatalf("source = %q, want previous-snapshot", source)
|
|
}
|
|
if len(notes) != 1 {
|
|
t.Fatalf("notes = %#v, want carry-forward diagnostic", notes)
|
|
}
|
|
}
|
|
|
|
func TestStabilizeGuestLowTrustMemoryUsesHealthyGuestAgentEvidence(t *testing.T) {
|
|
const gib = uint64(1024 * 1024 * 1024)
|
|
|
|
now := time.Now()
|
|
prev := &GuestMemorySnapshot{
|
|
Status: "running",
|
|
RetrievedAt: now.Add(-2 * time.Minute),
|
|
MemorySource: "previous-snapshot",
|
|
Memory: models.Memory{
|
|
Total: 8 * int64(gib),
|
|
Used: 3 * int64(gib),
|
|
Usage: safePercentage(float64(3*gib), float64(8*gib)),
|
|
},
|
|
}
|
|
|
|
used, source, notes := stabilizeGuestLowTrustMemory(prev, "running", "cluster-resources", 8*gib, 8*gib, now, true)
|
|
if used != 3*gib {
|
|
t.Fatalf("used = %d, want %d", used, 3*gib)
|
|
}
|
|
if source != "previous-snapshot" {
|
|
t.Fatalf("source = %q, want previous-snapshot", source)
|
|
}
|
|
if len(notes) != 1 || notes[0] != "preserved-previous-memory-for-healthy-guest-low-trust-full-usage" {
|
|
t.Fatalf("notes = %#v, want healthy guest carry-forward note", notes)
|
|
}
|
|
}
|
|
|
|
func TestHandleClusterVMResourcePreservesHealthyGuestMemoryFromPreviousSnapshot(t *testing.T) {
|
|
t.Setenv("PULSE_DATA_DIR", t.TempDir())
|
|
|
|
const gib = uint64(1024 * 1024 * 1024)
|
|
|
|
mon := newTestPVEMonitor("test")
|
|
defer mon.alertManager.Stop()
|
|
defer mon.notificationMgr.Stop()
|
|
|
|
previousSample := time.Now().Add(-time.Minute)
|
|
mon.recordGuestSnapshot("test", "qemu", "node1", 101, GuestMemorySnapshot{
|
|
Name: "vm-101",
|
|
Status: "running",
|
|
RetrievedAt: previousSample,
|
|
MemorySource: "previous-snapshot",
|
|
Memory: models.Memory{
|
|
Total: 8 * int64(gib),
|
|
Used: 3 * int64(gib),
|
|
Free: 5 * int64(gib),
|
|
Usage: safePercentage(float64(3*gib), float64(8*gib)),
|
|
},
|
|
})
|
|
|
|
client := &vmMemoryTrustStubClient{
|
|
stubPVEClient: &stubPVEClient{},
|
|
vmStatus: &proxmox.VMStatus{
|
|
Status: "running",
|
|
MaxMem: 8 * gib,
|
|
Mem: 8 * gib,
|
|
Agent: proxmox.VMAgentField{Value: 1},
|
|
},
|
|
}
|
|
|
|
vm, ok := mon.handleClusterVMResource(
|
|
context.Background(),
|
|
"test",
|
|
proxmox.ClusterResource{
|
|
ID: "qemu/101",
|
|
Type: "qemu",
|
|
Node: "node1",
|
|
Name: "vm-101",
|
|
Status: "running",
|
|
VMID: 101,
|
|
MaxMem: 8 * gib,
|
|
Mem: 8 * gib,
|
|
MaxCPU: 2,
|
|
},
|
|
makeGuestID("test", "node1", 101),
|
|
client,
|
|
nil,
|
|
nil,
|
|
)
|
|
if !ok {
|
|
t.Fatal("handleClusterVMResource() returned ok=false")
|
|
}
|
|
if got := uint64(vm.Memory.Used); got != 3*gib {
|
|
t.Fatalf("vm.Memory.Used = %d, want %d", got, 3*gib)
|
|
}
|
|
|
|
key := makeGuestSnapshotKey("test", "qemu", "node1", 101)
|
|
snap, ok := mon.guestSnapshots[key]
|
|
if !ok {
|
|
t.Fatalf("expected guest snapshot %q to be recorded", key)
|
|
}
|
|
if snap.MemorySource != "previous-snapshot" {
|
|
t.Fatalf("snapshot.MemorySource = %q, want previous-snapshot", snap.MemorySource)
|
|
}
|
|
if len(snap.Notes) != 1 || snap.Notes[0] != "preserved-previous-memory-for-healthy-guest-low-trust-full-usage" {
|
|
t.Fatalf("snapshot.Notes = %#v, want healthy guest carry-forward note", snap.Notes)
|
|
}
|
|
}
|
|
|
|
func TestPollVMsWithNodesPreservesHealthyGuestMemoryFromPreviousSnapshot(t *testing.T) {
|
|
t.Setenv("PULSE_DATA_DIR", t.TempDir())
|
|
|
|
const gib = uint64(1024 * 1024 * 1024)
|
|
|
|
mon := newTestPVEMonitor("test")
|
|
defer mon.alertManager.Stop()
|
|
defer mon.notificationMgr.Stop()
|
|
|
|
previousSample := time.Now().Add(-time.Minute)
|
|
mon.recordGuestSnapshot("test", "qemu", "node1", 101, GuestMemorySnapshot{
|
|
Name: "vm-101",
|
|
Status: "running",
|
|
RetrievedAt: previousSample,
|
|
MemorySource: "previous-snapshot",
|
|
Memory: models.Memory{
|
|
Total: 8 * int64(gib),
|
|
Used: 3 * int64(gib),
|
|
Free: 5 * int64(gib),
|
|
Usage: safePercentage(float64(3*gib), float64(8*gib)),
|
|
},
|
|
})
|
|
|
|
client := &vmMemoryTrustStubClient{
|
|
stubPVEClient: &stubPVEClient{},
|
|
vms: []proxmox.VM{{
|
|
VMID: 101,
|
|
Name: "vm-101",
|
|
Node: "node1",
|
|
Status: "running",
|
|
MaxMem: 8 * gib,
|
|
Mem: 8 * gib,
|
|
CPUs: 2,
|
|
}},
|
|
vmStatus: &proxmox.VMStatus{
|
|
Status: "running",
|
|
MaxMem: 8 * gib,
|
|
Mem: 8 * gib,
|
|
Agent: proxmox.VMAgentField{Value: 1},
|
|
},
|
|
}
|
|
|
|
nodes := []proxmox.Node{{Node: "node1", Status: "online"}}
|
|
nodeEffectiveStatus := map[string]string{"node1": "online"}
|
|
mon.pollVMsWithNodes(context.Background(), "test", "", false, client, nodes, nodeEffectiveStatus)
|
|
|
|
vms := mon.state.GetSnapshot().VMs
|
|
if len(vms) != 1 {
|
|
t.Fatalf("expected 1 VM, got %d", len(vms))
|
|
}
|
|
if got := uint64(vms[0].Memory.Used); got != 3*gib {
|
|
t.Fatalf("vms[0].Memory.Used = %d, want %d", got, 3*gib)
|
|
}
|
|
|
|
key := makeGuestSnapshotKey("test", "qemu", "node1", 101)
|
|
snap, ok := mon.guestSnapshots[key]
|
|
if !ok {
|
|
t.Fatalf("expected guest snapshot %q to be recorded", key)
|
|
}
|
|
if snap.MemorySource != "previous-snapshot" {
|
|
t.Fatalf("snapshot.MemorySource = %q, want previous-snapshot", snap.MemorySource)
|
|
}
|
|
if len(snap.Notes) != 1 || snap.Notes[0] != "preserved-previous-memory-for-healthy-guest-low-trust-full-usage" {
|
|
t.Fatalf("snapshot.Notes = %#v, want healthy guest carry-forward note", snap.Notes)
|
|
}
|
|
}
|