Pulse/internal/monitoring/node_disk_sources.go
2026-04-01 09:36:58 +01:00

150 lines
3.7 KiB
Go

package monitoring
import (
"strings"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
"github.com/rcourtman/pulse-go-rewrite/pkg/proxmox"
"github.com/rs/zerolog/log"
)
func (m *Monitor) resolveNodeDisk(
instanceName string,
nodeID string,
nodeName string,
node proxmox.Node,
nodeInfo *proxmox.NodeStatus,
) (models.Disk, string) {
if linkedHost := m.linkedHostForNode(nodeID, nodeName); linkedHost != nil {
if disk, ok := models.SummaryDisk(linkedHost.Disks); ok {
resolved := models.Disk{
Total: disk.Total,
Used: disk.Used,
Free: disk.Free,
Usage: disk.Usage,
}
log.Debug().
Str("instance", instanceName).
Str("node", nodeName).
Str("hostAgentID", linkedHost.ID).
Int64("total", resolved.Total).
Int64("used", resolved.Used).
Float64("usage", resolved.Usage).
Msg("Node disk: using linked Pulse host agent disk summary")
return resolved, "host-agent"
}
}
if nodeInfo != nil && nodeInfo.RootFS != nil && nodeInfo.RootFS.Total > 0 {
resolved := models.Disk{
Total: int64(nodeInfo.RootFS.Total),
Used: int64(nodeInfo.RootFS.Used),
Free: int64(nodeInfo.RootFS.Free),
Usage: safePercentage(float64(nodeInfo.RootFS.Used), float64(nodeInfo.RootFS.Total)),
}
log.Debug().
Str("instance", instanceName).
Str("node", nodeName).
Uint64("rootfsUsed", nodeInfo.RootFS.Used).
Uint64("rootfsTotal", nodeInfo.RootFS.Total).
Float64("rootfsUsage", resolved.Usage).
Msg("Node disk: using Proxmox rootfs metrics")
return resolved, "rootfs"
}
if node.MaxDisk > 0 {
resolved := models.Disk{
Total: int64(node.MaxDisk),
Used: int64(node.Disk),
Free: int64(node.MaxDisk - node.Disk),
Usage: safePercentage(float64(node.Disk), float64(node.MaxDisk)),
}
log.Debug().
Str("instance", instanceName).
Str("node", nodeName).
Uint64("disk", node.Disk).
Uint64("maxDisk", node.MaxDisk).
Float64("usage", resolved.Usage).
Msg("Node disk: using /nodes endpoint metrics")
return resolved, "nodes-endpoint"
}
return models.Disk{}, ""
}
func preferredNodeDiskFallbackRank(storage proxmox.Storage) (int, bool) {
name := strings.ToLower(strings.TrimSpace(storage.Storage))
storageType := strings.ToLower(strings.TrimSpace(storage.Type))
path := strings.TrimSpace(storage.Path)
supportsGuestRoots := storageContentIncludes(storage.Content, "images") || storageContentIncludes(storage.Content, "rootdir")
switch name {
case "local-zfs":
return 0, true
case "local-lvm":
return 1, true
case "local":
return 2, true
}
if storage.Shared != 0 {
return 0, false
}
if supportsGuestRoots {
switch storageType {
case "zfspool", "zfs", "local-zfs":
return 3, true
case "lvmthin", "lvm", "local-lvm":
return 4, true
}
if storageType == "dir" && path == "/var/lib/vz" {
return 5, true
}
if strings.HasPrefix(name, "local") {
return 6, true
}
return 7, true
}
return 0, false
}
func storageContentIncludes(content, want string) bool {
for _, part := range strings.Split(content, ",") {
if strings.EqualFold(strings.TrimSpace(part), want) {
return true
}
}
return false
}
func (m *Monitor) linkedHostForNode(nodeID, nodeName string) *models.Host {
state := m.GetState()
linkedHostID := ""
for _, existingNode := range state.Nodes {
if existingNode.ID == nodeID || strings.EqualFold(existingNode.Name, nodeName) {
linkedHostID = strings.TrimSpace(existingNode.LinkedHostAgentID)
break
}
}
if linkedHostID == "" {
return nil
}
for _, host := range state.Hosts {
if strings.TrimSpace(host.ID) != linkedHostID {
continue
}
if !strings.EqualFold(strings.TrimSpace(host.Status), "online") {
return nil
}
resolved := host
return &resolved
}
return nil
}