Pulse/internal/resources/resource.go
2025-12-12 21:31:11 +00:00

251 lines
9.7 KiB
Go

// Package resources provides a unified abstraction for all monitored entities
// across different platforms (Proxmox, Docker, Kubernetes, TrueNAS, etc.).
//
// The Resource type is the core abstraction that normalizes platform-specific
// types like Node, Host, DockerHost, VM, Container into a common data model.
// This enables:
// - AI intelligence across all platforms
// - Elimination of duplicate monitoring (one machine = one set of alerts)
// - Extensibility for new platforms
// - Foundation for unified views
package resources
import (
"encoding/json"
"time"
)
// Resource is the universal abstraction for any monitored entity.
// All platform-specific types (Node, VM, Container, DockerHost, etc.) can be
// converted to this common type for unified handling.
type Resource struct {
// Identity
ID string `json:"id"` // Globally unique ID
Type ResourceType `json:"type"` // vm, container, docker-container, pod, host, etc.
Name string `json:"name"` // Human-readable name
DisplayName string `json:"displayName"` // Custom display name (if set)
// Platform/Source
PlatformID string `json:"platformId"` // Which platform instance (e.g., cluster URL)
PlatformType PlatformType `json:"platformType"` // proxmox-pve, docker, kubernetes, etc.
SourceType SourceType `json:"sourceType"` // api, agent, hybrid
// Hierarchy
ParentID string `json:"parentId,omitempty"` // VM → Node, Pod → K8s Node
ClusterID string `json:"clusterId,omitempty"` // Cluster membership
// Universal Metrics (nullable - not all resources have all metrics)
Status ResourceStatus `json:"status"` // online, offline, running, stopped, degraded
CPU *MetricValue `json:"cpu,omitempty"` // CPU usage percentage
Memory *MetricValue `json:"memory,omitempty"` // Memory usage
Disk *MetricValue `json:"disk,omitempty"` // Primary disk usage
Network *NetworkMetric `json:"network,omitempty"` // Network I/O
Temperature *float64 `json:"temperature,omitempty"` // Temperature in Celsius
Uptime *int64 `json:"uptime,omitempty"` // Uptime in seconds
// Universal Metadata
Tags []string `json:"tags,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
LastSeen time.Time `json:"lastSeen"`
Alerts []ResourceAlert `json:"alerts,omitempty"`
// Platform-Specific Data (discriminated by Type)
// This preserves all the rich data while allowing common handling.
// Use GetPlatformData() to unmarshal into the appropriate type.
PlatformData json.RawMessage `json:"platformData,omitempty"`
// Identity information for deduplication
Identity *ResourceIdentity `json:"identity,omitempty"`
// Schema version for future evolution
SchemaVersion int `json:"schemaVersion"`
}
// ResourceType identifies the kind of monitored entity.
type ResourceType string
const (
// Infrastructure - physical or virtual hosts that run workloads
ResourceTypeNode ResourceType = "node" // Proxmox VE node
ResourceTypeHost ResourceType = "host" // Standalone host (via host-agent)
ResourceTypeDockerHost ResourceType = "docker-host" // Docker/Podman host
ResourceTypeK8sCluster ResourceType = "k8s-cluster" // Kubernetes cluster
ResourceTypeK8sNode ResourceType = "k8s-node" // Kubernetes node
ResourceTypeTrueNAS ResourceType = "truenas" // TrueNAS system
// Compute Workloads - individual running instances
ResourceTypeVM ResourceType = "vm" // Proxmox VM
ResourceTypeContainer ResourceType = "container" // LXC container
ResourceTypeOCIContainer ResourceType = "oci-container" // OCI container (Proxmox VE 9.1+)
ResourceTypeDockerContainer ResourceType = "docker-container" // Docker container
ResourceTypePod ResourceType = "pod" // Kubernetes pod
ResourceTypeJail ResourceType = "jail" // BSD jail / TrueNAS jail
// Services - logical groupings of workloads
ResourceTypeDockerService ResourceType = "docker-service" // Docker Swarm service
ResourceTypeK8sDeployment ResourceType = "k8s-deployment" // Kubernetes deployment
ResourceTypeK8sService ResourceType = "k8s-service" // Kubernetes service
// Storage - storage resources
ResourceTypeStorage ResourceType = "storage" // Generic storage
ResourceTypeDatastore ResourceType = "datastore" // PBS datastore
ResourceTypePool ResourceType = "pool" // ZFS/Ceph pool
ResourceTypeDataset ResourceType = "dataset" // ZFS dataset
// Backup Systems
ResourceTypePBS ResourceType = "pbs" // Proxmox Backup Server
ResourceTypePMG ResourceType = "pmg" // Proxmox Mail Gateway
)
// PlatformType identifies the source platform/system.
type PlatformType string
const (
PlatformProxmoxPVE PlatformType = "proxmox-pve"
PlatformProxmoxPBS PlatformType = "proxmox-pbs"
PlatformProxmoxPMG PlatformType = "proxmox-pmg"
PlatformDocker PlatformType = "docker"
PlatformKubernetes PlatformType = "kubernetes"
PlatformTrueNAS PlatformType = "truenas"
PlatformHostAgent PlatformType = "host-agent"
)
// SourceType indicates how data is collected for this resource.
type SourceType string
const (
SourceAPI SourceType = "api" // Data from polling an API
SourceAgent SourceType = "agent" // Data pushed from agent
SourceHybrid SourceType = "hybrid" // Both sources, agent preferred
)
// ResourceStatus represents the operational state of a resource.
type ResourceStatus string
const (
StatusOnline ResourceStatus = "online"
StatusOffline ResourceStatus = "offline"
StatusRunning ResourceStatus = "running"
StatusStopped ResourceStatus = "stopped"
StatusDegraded ResourceStatus = "degraded"
StatusPaused ResourceStatus = "paused"
StatusUnknown ResourceStatus = "unknown"
)
// MetricValue represents a metric with current value and optional limits.
type MetricValue struct {
Current float64 `json:"current"` // Current value (percentage for CPU, bytes for memory/disk)
Total *int64 `json:"total,omitempty"` // Total capacity (bytes) - nil for percentages like CPU
Used *int64 `json:"used,omitempty"` // Used amount (bytes) - nil for percentages
Free *int64 `json:"free,omitempty"` // Free amount (bytes) - nil for percentages
}
// NetworkMetric captures network I/O.
type NetworkMetric struct {
RXBytes int64 `json:"rxBytes"` // Total bytes received
TXBytes int64 `json:"txBytes"` // Total bytes transmitted
}
// ResourceAlert represents an alert associated with a resource.
type ResourceAlert struct {
ID string `json:"id"`
Type string `json:"type"` // cpu, memory, disk, temperature, etc.
Level string `json:"level"` // warning, critical
Message string `json:"message"`
Value float64 `json:"value"`
Threshold float64 `json:"threshold"`
StartTime time.Time `json:"startTime"`
}
// ResourceIdentity contains information used for deduplication.
// When multiple sources report on the same physical machine, we use
// these fields to identify and merge them.
type ResourceIdentity struct {
Hostname string `json:"hostname,omitempty"` // Primary identifier
MachineID string `json:"machineId,omitempty"` // /etc/machine-id or equivalent
IPs []string `json:"ips,omitempty"` // Network addresses
}
// CurrentSchemaVersion is the current version of the Resource schema.
const CurrentSchemaVersion = 1
// GetPlatformData unmarshals the PlatformData into the provided type.
// Example: var nodeData NodePlatformData; r.GetPlatformData(&nodeData)
func (r *Resource) GetPlatformData(v interface{}) error {
if r.PlatformData == nil {
return nil
}
return json.Unmarshal(r.PlatformData, v)
}
// SetPlatformData marshals the provided value into PlatformData.
func (r *Resource) SetPlatformData(v interface{}) error {
data, err := json.Marshal(v)
if err != nil {
return err
}
r.PlatformData = data
return nil
}
// IsInfrastructure returns true if this resource is an infrastructure host
// (node, host, docker-host) rather than a workload (vm, container).
func (r *Resource) IsInfrastructure() bool {
switch r.Type {
case ResourceTypeNode, ResourceTypeHost, ResourceTypeDockerHost, ResourceTypeK8sCluster, ResourceTypeK8sNode, ResourceTypeTrueNAS:
return true
default:
return false
}
}
// IsWorkload returns true if this resource is a workload (vm, container, pod)
// rather than infrastructure.
func (r *Resource) IsWorkload() bool {
switch r.Type {
case ResourceTypeVM, ResourceTypeContainer, ResourceTypeOCIContainer, ResourceTypeDockerContainer, ResourceTypePod, ResourceTypeJail:
return true
default:
return false
}
}
// EffectiveDisplayName returns DisplayName if set, otherwise Name.
func (r *Resource) EffectiveDisplayName() string {
if r.DisplayName != "" {
return r.DisplayName
}
return r.Name
}
// CPUPercent returns the CPU usage as a percentage, or 0 if not available.
func (r *Resource) CPUPercent() float64 {
if r.CPU == nil {
return 0
}
return r.CPU.Current
}
// MemoryPercent returns the memory usage as a percentage, or 0 if not available.
func (r *Resource) MemoryPercent() float64 {
if r.Memory == nil {
return 0
}
// If we have used/total, calculate percentage
if r.Memory.Total != nil && *r.Memory.Total > 0 && r.Memory.Used != nil {
return float64(*r.Memory.Used) / float64(*r.Memory.Total) * 100
}
// Otherwise, Current is the percentage
return r.Memory.Current
}
// DiskPercent returns the disk usage as a percentage, or 0 if not available.
func (r *Resource) DiskPercent() float64 {
if r.Disk == nil {
return 0
}
if r.Disk.Total != nil && *r.Disk.Total > 0 && r.Disk.Used != nil {
return float64(*r.Disk.Used) / float64(*r.Disk.Total) * 100
}
return r.Disk.Current
}