package monitoring import ( "math" "strings" "time" "github.com/rcourtman/pulse-go-rewrite/internal/models" "github.com/rcourtman/pulse-go-rewrite/pkg/proxmox" ) const ( vmMemoryCarryForwardMaxAge = 2 * time.Minute vmMemoryHealthyGuestMaxAge = 10 * time.Minute vmMemoryCarryForwardMinUsageDelta = 5.0 vmMemorySourceReliabilityLow = 0 vmMemorySourceReliabilityFallback = 1 vmMemorySourceReliabilityTrusted = 2 ) func vmMemorySourceReliability(source string) int { switch strings.TrimSpace(source) { case "meminfo-available", "meminfo-derived", "guest-agent-meminfo", "rrd-memavailable", "rrd-memused", "host-agent": return vmMemorySourceReliabilityTrusted case "meminfo-total-minus-used", "previous-snapshot": return vmMemorySourceReliabilityFallback case "", "cluster-resources", "listing-mem", "status-mem", "status-freemem", "status-unavailable": return vmMemorySourceReliabilityLow default: return vmMemorySourceReliabilityFallback } } func shouldCarryForwardPreviousVMMemory(prev models.VM, currentStatus, currentSource string, currentTotal, currentUsed uint64, now time.Time) bool { if currentStatus != "running" || prev.Type != "qemu" || prev.Status != "running" { return false } if prev.Memory.Total <= 0 || prev.Memory.Used < 0 { return false } if prev.LastSeen.IsZero() || now.Sub(prev.LastSeen) > vmMemoryCarryForwardMaxAge { return false } prevReliability := vmMemorySourceReliability(prev.MemorySource) currentReliability := vmMemorySourceReliability(currentSource) if prevReliability < vmMemorySourceReliabilityTrusted || currentReliability > vmMemorySourceReliabilityFallback { return false } if prevReliability <= currentReliability { return false } if currentTotal > 0 && prev.Memory.Total > 0 && prev.Memory.Total != int64(currentTotal) { return false } currentUsage := safePercentage(float64(currentUsed), float64(currentTotal)) if prev.Memory.Usage > 0 && math.Abs(prev.Memory.Usage-currentUsage) < vmMemoryCarryForwardMinUsageDelta { return false } return true } func shouldCarryForwardHealthyGuestLowTrustVMMemory(prev models.VM, currentStatus, currentSource string, currentTotal, currentUsed uint64, now time.Time, guestAgentHealthy bool) bool { if !guestAgentHealthy || currentStatus != "running" || prev.Type != "qemu" || prev.Status != "running" { return false } if prev.Memory.Total <= 0 || prev.Memory.Used < 0 { return false } if prev.LastSeen.IsZero() || now.Sub(prev.LastSeen) > vmMemoryHealthyGuestMaxAge { return false } if currentTotal == 0 || prev.Memory.Total != int64(currentTotal) { return false } if vmMemorySourceReliability(currentSource) != vmMemorySourceReliabilityLow { return false } currentUsage := safePercentage(float64(currentUsed), float64(currentTotal)) if currentUsage < 99 { return false } if prev.Memory.Usage >= 90 || math.Abs(prev.Memory.Usage-currentUsage) < vmMemoryCarryForwardMinUsageDelta { return false } prevReliability := vmMemorySourceReliability(prev.MemorySource) if prev.MemorySource != "previous-snapshot" && prevReliability < vmMemorySourceReliabilityTrusted { return false } return true } func guestAgentSignalsHealthy( detailedStatus *proxmox.VMStatus, diskFromAgent bool, ipAddresses []string, networkInterfaces []models.GuestNetworkInterface, osName, osVersion, agentVersion string, ) bool { if detailedStatus != nil && detailedStatus.Agent.Value > 0 { return true } return diskFromAgent || len(ipAddresses) > 0 || len(networkInterfaces) > 0 || osName != "" || osVersion != "" || agentVersion != "" }