mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-22 19:23:31 +00:00
181 lines
6.4 KiB
Go
181 lines
6.4 KiB
Go
package monitoring
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/proxmox"
|
|
)
|
|
|
|
func TestIssue1613GuestRateIdentitySurvivesMigrationAndIsolatesDuplicates(t *testing.T) {
|
|
beforeMigration := makeGuestRateKey("site-a", "qemu", 100)
|
|
afterMigration := makeGuestRateKey("site-a", "qemu", 100)
|
|
if beforeMigration != afterMigration {
|
|
t.Fatalf("migration changed rate key: %q != %q", beforeMigration, afterMigration)
|
|
}
|
|
if beforeMigration == makeGuestRateKey("site-b", "qemu", 100) {
|
|
t.Fatal("duplicate cluster registrations shared a rate key")
|
|
}
|
|
if beforeMigration == makeGuestRateKey("site-a", "lxc", 100) {
|
|
t.Fatal("QEMU and LXC with the same VMID shared a rate key")
|
|
}
|
|
}
|
|
|
|
func TestIssue1613QEMUStatusUsesPresentLowerCounterAndKeepsMissingListingCounter(t *testing.T) {
|
|
listingAt := time.Unix(1_700_000_000, 0)
|
|
statusAt := listingAt.Add(3 * time.Second)
|
|
state := vmBuildState{
|
|
diskReadBytes: 9_000,
|
|
diskWriteBytes: 8_000,
|
|
networkInBytes: 7_000,
|
|
networkOutBytes: 6_000,
|
|
counterPresence: models.IOCounterPresence{Explicit: true, DiskRead: true, DiskWrite: true, NetworkIn: true, NetworkOut: true},
|
|
counterObservedAt: listingAt,
|
|
counterObservationTimes: models.IOCounterObservationTimes{
|
|
DiskRead: listingAt,
|
|
DiskWrite: listingAt,
|
|
NetworkIn: listingAt,
|
|
NetworkOut: listingAt,
|
|
},
|
|
counterUptime: 10_000,
|
|
}
|
|
mergeVMRuntimeCounters(&state, &proxmox.VMStatus{
|
|
DiskRead: 0,
|
|
NetOut: 500,
|
|
Uptime: 20,
|
|
IOCounters: proxmox.IOCounterPresence{
|
|
Explicit: true,
|
|
DiskRead: true,
|
|
NetworkOut: true,
|
|
},
|
|
ObservedAt: statusAt,
|
|
})
|
|
|
|
if state.diskReadBytes != 0 || state.networkOutBytes != 500 {
|
|
t.Fatalf("present status counters did not replace listing values: %+v", state)
|
|
}
|
|
if state.diskWriteBytes != 8_000 || state.networkInBytes != 7_000 {
|
|
t.Fatalf("missing status counters replaced listing values: %+v", state)
|
|
}
|
|
if !state.counterObservationTimes.DiskRead.Equal(statusAt) ||
|
|
!state.counterObservationTimes.NetworkOut.Equal(statusAt) ||
|
|
!state.counterObservationTimes.DiskWrite.Equal(listingAt) ||
|
|
!state.counterObservationTimes.NetworkIn.Equal(listingAt) {
|
|
t.Fatalf("per-counter receipt authority was not preserved: %+v", state.counterObservationTimes)
|
|
}
|
|
if state.counterUptime != 20 {
|
|
t.Fatalf("status uptime = %d, want 20", state.counterUptime)
|
|
}
|
|
}
|
|
|
|
func TestIssue1613PVEGraceTracksConfiguredPollingCadence(t *testing.T) {
|
|
for _, test := range []struct {
|
|
interval time.Duration
|
|
want time.Duration
|
|
}{
|
|
{interval: 30 * time.Second, want: 60 * time.Second},
|
|
{interval: 60 * time.Second, want: 120 * time.Second},
|
|
{interval: 90 * time.Second, want: 180 * time.Second},
|
|
} {
|
|
monitor := &Monitor{config: &config.Config{PVEPollingInterval: test.interval}}
|
|
if got := monitor.pveNodeOfflineGracePeriod(); got != test.want {
|
|
t.Fatalf("interval %s grace = %s, want %s", test.interval, got, test.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIssue1613HistoryWritesValidZeroAndSkipsUnknownRates(t *testing.T) {
|
|
monitor := &Monitor{metricsHistory: NewMetricsHistory(32, time.Hour)}
|
|
now := time.Now()
|
|
|
|
diskRead, diskWrite, networkIn, networkOut := guestHistoryRates(0, 0, 0, 0, models.IORateValidity{Explicit: true})
|
|
monitor.recordGuestMetric("vm", "unknown", 0, 0, 0, 0, diskRead, diskWrite, networkIn, networkOut, now)
|
|
if points := monitor.metricsHistory.GetGuestMetrics("unknown", "diskread", time.Hour); len(points) != 0 {
|
|
t.Fatalf("unknown disk read rate was written to history: %+v", points)
|
|
}
|
|
|
|
diskRead, diskWrite, networkIn, networkOut = guestHistoryRates(0, 0, 0, 0, models.IORateValidity{
|
|
Explicit: true,
|
|
DiskRead: true,
|
|
DiskWrite: true,
|
|
NetworkIn: true,
|
|
NetworkOut: true,
|
|
})
|
|
monitor.recordGuestMetric("vm", "idle", 0, 0, 0, 0, diskRead, diskWrite, networkIn, networkOut, now)
|
|
points := monitor.metricsHistory.GetGuestMetrics("idle", "diskread", time.Hour)
|
|
if len(points) != 1 || points[0].Value != 0 {
|
|
t.Fatalf("valid idle disk read rate was not written as zero: %+v", points)
|
|
}
|
|
}
|
|
|
|
func TestIssue1613NodeDoesNotGreyBetweenNinetySecondPolls(t *testing.T) {
|
|
instance := &config.PVEInstance{Name: "site-a"}
|
|
monitor := &Monitor{
|
|
config: &config.Config{PVEPollingInterval: 90 * time.Second},
|
|
nodeLastOnline: map[string]time.Time{"site-a-pve-a": time.Now().Add(-100 * time.Second)},
|
|
}
|
|
|
|
_, status := monitor.determineNodeIDAndStatus("site-a", instance, proxmox.Node{
|
|
Node: "pve-a",
|
|
Status: "offline",
|
|
})
|
|
if status != "online" {
|
|
t.Fatalf("node status = %q inside cadence grace, want online", status)
|
|
}
|
|
|
|
monitor.nodeLastOnline["site-a-pve-a"] = time.Now().Add(-181 * time.Second)
|
|
_, status = monitor.determineNodeIDAndStatus("site-a", instance, proxmox.Node{
|
|
Node: "pve-a",
|
|
Status: "offline",
|
|
})
|
|
if status != "offline" {
|
|
t.Fatalf("node status = %q after cadence grace, want offline", status)
|
|
}
|
|
}
|
|
|
|
func TestIssue1613WebsocketStateKeepsUnknownRatesNumeric(t *testing.T) {
|
|
monitor := &Monitor{
|
|
state: models.NewState(),
|
|
resourceStore: &resourceOnlyStore{resources: []unifiedresources.Resource{
|
|
{
|
|
ID: "site-a:pve-a:100",
|
|
Type: unifiedresources.ResourceTypeVM,
|
|
Name: "idle-vm",
|
|
Status: unifiedresources.StatusOnline,
|
|
Sources: []unifiedresources.DataSource{unifiedresources.SourceProxmox},
|
|
Metrics: &unifiedresources.ResourceMetrics{
|
|
DiskRead: &unifiedresources.MetricValue{Value: 0, Unit: "bytes/s"},
|
|
},
|
|
},
|
|
{
|
|
ID: "site-a:pve-a:101",
|
|
Type: unifiedresources.ResourceTypeVM,
|
|
Name: "unknown-vm",
|
|
Status: unifiedresources.StatusOnline,
|
|
Sources: []unifiedresources.DataSource{unifiedresources.SourceProxmox},
|
|
Metrics: &unifiedresources.ResourceMetrics{},
|
|
},
|
|
}},
|
|
}
|
|
frontend := monitor.BuildBroadcastFrontendState()
|
|
payload, err := json.Marshal(frontend)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wire := string(payload)
|
|
if !strings.Contains(wire, `"diskIO":{"readRate":0,"writeRate":0}`) {
|
|
t.Fatalf("websocket payload does not contain numeric valid zero disk rate: %s", wire)
|
|
}
|
|
if strings.Count(wire, `"diskIO"`) != 1 {
|
|
t.Fatalf("unknown rate projected a disk I/O object: %s", wire)
|
|
}
|
|
if strings.Contains(wire, `"diskWrite":null`) || strings.Contains(wire, `"netIn":null`) {
|
|
t.Fatalf("websocket payload contains unstable null I/O values: %s", wire)
|
|
}
|
|
}
|