mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 12:42:49 +00:00
8b56fc72e4
Extend the #1962 linked-agent memory fallback to Proxmox LXC containers (#2148). A correlated online Pulse agent running inside a container now supplies the container's memory metric instead of the cache-inclusive cluster/resources fallback, which can badly under-report shared-memory workloads. The agent sample is only trusted when its total matches the container's configured limit, so an agent without lxcfs cannot leak host memory into the guest row. Threads the previous-guest agent map through the efficient and node-by-node container builders and reads agent-owned memory from merged ContainerView resources. Change-source: pulse-maintainer
223 lines
6.5 KiB
Go
223 lines
6.5 KiB
Go
package monitoring
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
|
|
)
|
|
|
|
type previousGuestContext struct {
|
|
vms []models.VM
|
|
vmsByID map[string]models.VM
|
|
containers []models.Container
|
|
containersByID map[string]models.Container
|
|
containerOCIByVMID map[int]bool
|
|
hostAgentsByVMID map[string]models.Host
|
|
}
|
|
|
|
func (m *Monitor) previousGuestContextForInstance(instanceName string) previousGuestContext {
|
|
ctx := previousGuestContext{
|
|
vms: make([]models.VM, 0),
|
|
vmsByID: make(map[string]models.VM),
|
|
containers: make([]models.Container, 0),
|
|
containersByID: make(map[string]models.Container),
|
|
containerOCIByVMID: make(map[int]bool),
|
|
hostAgentsByVMID: make(map[string]models.Host),
|
|
}
|
|
|
|
readState := m.GetUnifiedReadStateOrSnapshot()
|
|
if readState == nil {
|
|
return ctx
|
|
}
|
|
|
|
for _, vm := range readState.VMs() {
|
|
if vm == nil || vm.Instance() != instanceName {
|
|
continue
|
|
}
|
|
modelVM := previousVMFromView(vm)
|
|
ctx.vms = append(ctx.vms, modelVM)
|
|
if modelVM.ID != "" {
|
|
ctx.vmsByID[modelVM.ID] = modelVM
|
|
}
|
|
guestID := makeGuestID(modelVM.Instance, modelVM.Node, modelVM.VMID)
|
|
if guestID != "" {
|
|
ctx.vmsByID[guestID] = modelVM
|
|
if memory, ok := vm.LinkedAgentMemory(); ok {
|
|
ctx.hostAgentsByVMID[guestID] = models.Host{LinkedVMID: guestID, Status: "online", Memory: memory}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, ct := range readState.Containers() {
|
|
if ct == nil || ct.Instance() != instanceName {
|
|
continue
|
|
}
|
|
container := previousContainerFromView(ct)
|
|
ctx.containers = append(ctx.containers, container)
|
|
if container.ID != "" {
|
|
ctx.containersByID[container.ID] = container
|
|
}
|
|
guestID := makeGuestID(container.Instance, container.Node, container.VMID)
|
|
if guestID != "" {
|
|
ctx.containersByID[guestID] = container
|
|
if memory, ok := ct.LinkedAgentMemory(); ok {
|
|
ctx.hostAgentsByVMID[guestID] = models.Host{LinkedVMID: guestID, Status: "online", Memory: memory}
|
|
}
|
|
}
|
|
if container.VMID > 0 && (strings.EqualFold(strings.TrimSpace(container.Type), "oci") || container.IsOCI) {
|
|
ctx.containerOCIByVMID[container.VMID] = true
|
|
}
|
|
}
|
|
|
|
for _, host := range readState.Hosts() {
|
|
if host == nil {
|
|
continue
|
|
}
|
|
modelHost := previousHostFromView(host)
|
|
if modelHost.Status != "online" {
|
|
continue
|
|
}
|
|
if modelHost.LinkedVMID != "" {
|
|
ctx.hostAgentsByVMID[modelHost.LinkedVMID] = modelHost
|
|
}
|
|
if modelHost.LinkedContainerID != "" {
|
|
ctx.hostAgentsByVMID[modelHost.LinkedContainerID] = modelHost
|
|
}
|
|
}
|
|
|
|
return ctx
|
|
}
|
|
|
|
func (m *Monitor) previousNodesForInstance(instanceName string) (map[string]models.Memory, []models.Node) {
|
|
prevNodeMemory := make(map[string]models.Memory)
|
|
prevInstanceNodes := make([]models.Node, 0)
|
|
|
|
readState := m.GetUnifiedReadStateOrSnapshot()
|
|
if readState == nil {
|
|
return prevNodeMemory, prevInstanceNodes
|
|
}
|
|
|
|
for _, existingNode := range readState.Nodes() {
|
|
if existingNode == nil || existingNode.Instance() != instanceName {
|
|
continue
|
|
}
|
|
modelNode := previousNodeFromView(existingNode)
|
|
prevNodeMemory[modelNode.ID] = modelNode.Memory
|
|
prevInstanceNodes = append(prevInstanceNodes, modelNode)
|
|
}
|
|
return prevNodeMemory, prevInstanceNodes
|
|
}
|
|
|
|
func previousVMFromView(vm *unifiedresources.VMView) models.VM {
|
|
if vm == nil {
|
|
return models.VM{}
|
|
}
|
|
instance := vm.Instance()
|
|
node := vm.Node()
|
|
vmid := vm.VMID()
|
|
return models.VM{
|
|
ID: makeGuestID(instance, node, vmid),
|
|
Instance: instance,
|
|
Node: node,
|
|
VMID: vmid,
|
|
Name: vm.Name(),
|
|
Type: "qemu",
|
|
Status: vm.RuntimeStatus(),
|
|
IPAddresses: vm.IPAddresses(),
|
|
OSName: vm.OSName(),
|
|
OSVersion: vm.OSVersion(),
|
|
AgentVersion: vm.AgentVersion(),
|
|
Lock: vm.Lock(),
|
|
Disk: models.Disk{
|
|
Used: vm.DiskUsed(),
|
|
Total: vm.DiskTotal(),
|
|
Free: max(0, vm.DiskTotal()-vm.DiskUsed()),
|
|
Usage: vm.DiskPercent(),
|
|
},
|
|
NetworkInterfaces: guestNetworkInterfacesFromReadStateView(vm.NetworkInterfaces()),
|
|
Disks: guestDisksFromReadStateView(vm.Disks()),
|
|
DiskStatusReason: vm.DiskStatusReason(),
|
|
LastSeen: vm.LastSeen(),
|
|
}
|
|
}
|
|
|
|
func previousContainerFromView(ct *unifiedresources.ContainerView) models.Container {
|
|
if ct == nil {
|
|
return models.Container{}
|
|
}
|
|
instance := ct.Instance()
|
|
node := ct.Node()
|
|
vmid := ct.VMID()
|
|
return models.Container{
|
|
ID: makeGuestID(instance, node, vmid),
|
|
Instance: instance,
|
|
Node: node,
|
|
VMID: vmid,
|
|
Name: ct.Name(),
|
|
Status: ct.RuntimeStatus(),
|
|
Type: ct.ContainerType(),
|
|
IsOCI: ct.IsOCI(),
|
|
LastSeen: ct.LastSeen(),
|
|
IPAddresses: ct.IPAddresses(),
|
|
NetworkInterfaces: guestNetworkInterfacesFromReadStateView(ct.NetworkInterfaces()),
|
|
OSName: ct.OSName(),
|
|
OSTemplate: ct.OSTemplate(),
|
|
HasDocker: ct.HasDocker(),
|
|
DockerCheckedAt: ct.DockerCheckedAt(),
|
|
Disks: guestDisksFromReadStateView(ct.Disks()),
|
|
Lock: ct.Lock(),
|
|
Memory: models.Memory{
|
|
Used: ct.MemoryUsed(),
|
|
Total: ct.MemoryTotal(),
|
|
Usage: ct.MemoryPercent() / 100,
|
|
},
|
|
}
|
|
}
|
|
|
|
func previousHostFromView(host *unifiedresources.HostView) models.Host {
|
|
if host == nil {
|
|
return models.Host{}
|
|
}
|
|
return models.Host{
|
|
ID: host.ID(),
|
|
Hostname: host.Hostname(),
|
|
Status: string(host.Status()),
|
|
LinkedVMID: host.LinkedVMID(),
|
|
LinkedContainerID: host.LinkedContainerID(),
|
|
LastSeen: host.LastSeen(),
|
|
Disks: guestDisksFromReadStateView(host.Disks()),
|
|
Memory: models.Memory{
|
|
Used: host.MemoryUsed(),
|
|
Total: host.MemoryTotal(),
|
|
Usage: host.MemoryPercent() / 100,
|
|
},
|
|
}
|
|
}
|
|
|
|
func previousNodeFromView(node *unifiedresources.NodeView) models.Node {
|
|
if node == nil {
|
|
return models.Node{}
|
|
}
|
|
return models.Node{
|
|
ID: node.ID(),
|
|
Name: node.NodeName(),
|
|
DisplayName: node.Name(),
|
|
Instance: node.Instance(),
|
|
Host: node.HostURL(),
|
|
Status: string(node.Status()),
|
|
Uptime: node.Uptime(),
|
|
IsClusterMember: node.IsClusterMember(),
|
|
LastSeen: node.LastSeen(),
|
|
LoadAverage: node.LoadAverage(),
|
|
PVEVersion: node.PVEVersion(),
|
|
KernelVersion: node.KernelVersion(),
|
|
NetworkInterfaces: hostNetworkInterfacesFromReadStateView(node.NetworkInterfaces()),
|
|
Memory: models.Memory{
|
|
Used: node.MemoryUsed(),
|
|
Total: node.MemoryTotal(),
|
|
Usage: node.MemoryPercent() / 100,
|
|
},
|
|
}
|
|
}
|