Files
pulse/internal/models/models_frontend.go
rcourtman 3a4a3fd62b Preserve native filesystem evidence and Patrol action history
Expose confined, identity-bound filesystem observations through the shared
resource pipeline so investigations can distinguish an exhausted container
mount from unrelated host capacity. Keep unavailable measurements explicit.

Isolate alert-history reads from durable writes and reuse one chronological
fold across polling. Catch up through bounded durable event IDs so simultaneous
readers do not replay every retained snapshot. Retain expired actions when
investigation outcomes move back to needs attention, and keep attached
Assistant context focused.

Record live storage diagnosis, healthy and dependency controls, approved and
rejected Docker outcomes, source-bound browser proof and exact test limits.
Missing-access continuity, VM dispatch completion and remaining Assistant
orchestration defects stay open in the redesign plan.
2026-09-07 09:45:31 +01:00

1185 lines
56 KiB
Go

package models
import (
"encoding/json"
"time"
"github.com/rcourtman/pulse-go-rewrite/pkg/agents/filesystem"
"github.com/rcourtman/pulse-go-rewrite/pkg/diskinventory"
)
// Frontend-friendly type aliases with proper JSON tags
// These extend the base types with additional computed fields
// NodeFrontend represents a Node with frontend-friendly field names
type NodeFrontend struct {
ID string `json:"id"`
Node string `json:"node"` // Maps to Name
Name string `json:"name"`
DisplayName string `json:"displayName"`
Instance string `json:"instance"`
Host string `json:"host,omitempty"`
GuestURL string `json:"guestURL,omitempty"` // Optional guest-accessible URL (for navigation)
Status string `json:"status"`
Type string `json:"type"`
CPU float64 `json:"cpu"`
Memory *Memory `json:"memory,omitempty"` // Full memory object with usage percentage
Mem int64 `json:"mem"` // Maps to Memory.Used (kept for backward compat)
MaxMem int64 `json:"maxmem"` // Maps to Memory.Total (kept for backward compat)
Disk *Disk `json:"disk,omitempty"` // Full disk object with usage percentage
MaxDisk int64 `json:"maxdisk"` // Maps to Disk.Total (kept for backward compat)
Uptime int64 `json:"uptime"`
LoadAverage []float64 `json:"loadAverage"`
KernelVersion string `json:"kernelVersion"`
PVEVersion string `json:"pveVersion"`
CPUInfo CPUInfo `json:"cpuInfo"`
Temperature *Temperature `json:"temperature,omitempty"` // CPU/NVMe temperatures
LastSeen int64 `json:"lastSeen"` // Unix timestamp
ConnectionHealth string `json:"connectionHealth"`
IsClusterMember bool `json:"isClusterMember,omitempty"`
ClusterName string `json:"clusterName,omitempty"`
TemperatureMonitoringEnabled *bool `json:"temperatureMonitoringEnabled,omitempty"` // Per-node temperature monitoring override
LinkedAgentID string `json:"linkedAgentId,omitempty"` // ID of linked agent (for "Via agent" badge)
}
func (n NodeFrontend) NormalizeCollections() NodeFrontend {
if n.LoadAverage == nil {
n.LoadAverage = []float64{}
}
return n
}
// VMFrontend represents a VM with frontend-friendly field names
type VMFrontend struct {
ID string `json:"id"`
VMID int `json:"vmid"`
Name string `json:"name"`
Node string `json:"node"`
Pool string `json:"pool,omitempty"`
Instance string `json:"instance"`
Status string `json:"status"`
Type string `json:"type"`
CPU float64 `json:"cpu"`
CPUs int `json:"cpus"`
Memory *Memory `json:"memory,omitempty"` // Full memory object
Mem int64 `json:"mem"` // Maps to Memory.Used
MaxMem int64 `json:"maxmem"` // Maps to Memory.Total
DiskObj *Disk `json:"disk,omitempty"` // Full disk object
Disks []Disk `json:"disks"` // Individual filesystem/disk usage
DiskStatusReason string `json:"diskStatusReason,omitempty"` // Why disk stats are unavailable
GuestAgentStatus string `json:"guestAgentStatus,omitempty"` // QEMU guest-agent runtime status
GuestAgentExpected bool `json:"guestAgentExpected,omitempty"`
OSName string `json:"osName,omitempty"`
OSVersion string `json:"osVersion,omitempty"`
AgentVersion string `json:"agentVersion,omitempty"`
NetworkInterfaces []GuestNetworkInterface `json:"networkInterfaces"`
IPAddresses []string `json:"ipAddresses"`
NetIn int64 `json:"networkIn"` // Maps to NetworkIn (camelCase for frontend)
NetOut int64 `json:"networkOut"` // Maps to NetworkOut (camelCase for frontend)
DiskRead int64 `json:"diskRead"` // Maps to DiskRead (camelCase for frontend)
DiskWrite int64 `json:"diskWrite"` // Maps to DiskWrite (camelCase for frontend)
Uptime int64 `json:"uptime"`
Template bool `json:"template"`
LastBackup int64 `json:"lastBackup,omitempty"` // Unix timestamp
Tags string `json:"tags,omitempty"` // Joined string
Lock string `json:"lock,omitempty"`
LastSeen int64 `json:"lastSeen"` // Unix timestamp
}
func (v VMFrontend) NormalizeCollections() VMFrontend {
if v.Disks == nil {
v.Disks = []Disk{}
}
if v.NetworkInterfaces == nil {
v.NetworkInterfaces = []GuestNetworkInterface{}
}
if v.IPAddresses == nil {
v.IPAddresses = []string{}
}
return v
}
// ContainerFrontend represents a Container with frontend-friendly field names
type ContainerFrontend struct {
ID string `json:"id"`
VMID int `json:"vmid"`
Name string `json:"name"`
Node string `json:"node"`
Pool string `json:"pool,omitempty"`
Instance string `json:"instance"`
Status string `json:"status"`
Type string `json:"type"`
// OCI container support (Proxmox VE 9.1+)
IsOCI bool `json:"isOci,omitempty"` // True if this is an OCI container
OSTemplate string `json:"osTemplate,omitempty"` // Template or OCI image used (e.g., "oci:docker.io/library/alpine:latest")
CPU float64 `json:"cpu"`
CPUs int `json:"cpus"`
Memory *Memory `json:"memory,omitempty"` // Full memory object
Mem int64 `json:"mem"` // Maps to Memory.Used
MaxMem int64 `json:"maxmem"` // Maps to Memory.Total
DiskObj *Disk `json:"disk,omitempty"` // Full disk object
Disks []Disk `json:"disks"` // Individual filesystem/disk usage
NetworkInterfaces []GuestNetworkInterface `json:"networkInterfaces"`
IPAddresses []string `json:"ipAddresses"`
NetIn int64 `json:"networkIn"` // Maps to NetworkIn (camelCase for frontend)
NetOut int64 `json:"networkOut"` // Maps to NetworkOut (camelCase for frontend)
DiskRead int64 `json:"diskRead"` // Maps to DiskRead (camelCase for frontend)
DiskWrite int64 `json:"diskWrite"` // Maps to DiskWrite (camelCase for frontend)
Uptime int64 `json:"uptime"`
Template bool `json:"template"`
LastBackup int64 `json:"lastBackup,omitempty"` // Unix timestamp
Tags string `json:"tags,omitempty"` // Joined string
Lock string `json:"lock,omitempty"`
LastSeen int64 `json:"lastSeen"` // Unix timestamp
OSName string `json:"osName,omitempty"`
}
func (c ContainerFrontend) NormalizeCollections() ContainerFrontend {
if c.Disks == nil {
c.Disks = []Disk{}
}
if c.NetworkInterfaces == nil {
c.NetworkInterfaces = []GuestNetworkInterface{}
}
if c.IPAddresses == nil {
c.IPAddresses = []string{}
}
return c
}
// DockerHostFrontend represents a Docker host with frontend-friendly fields
type DockerHostFrontend struct {
ID string `json:"id"`
AgentID string `json:"agentId"`
Hostname string `json:"hostname"`
DisplayName string `json:"displayName"`
CustomDisplayName string `json:"customDisplayName,omitempty"`
MachineID string `json:"machineId,omitempty"`
OS string `json:"os,omitempty"`
KernelVersion string `json:"kernelVersion,omitempty"`
Architecture string `json:"architecture,omitempty"`
Runtime string `json:"runtime"`
RuntimeVersion string `json:"runtimeVersion,omitempty"`
DockerVersion string `json:"dockerVersion,omitempty"`
CollectionMode string `json:"collectionMode,omitempty"`
CPUs int `json:"cpus"`
TotalMemoryBytes int64 `json:"totalMemoryBytes"`
UptimeSeconds int64 `json:"uptimeSeconds"`
CPUUsagePercent float64 `json:"cpuUsagePercent"`
LoadAverage []float64 `json:"loadAverage"`
Memory *Memory `json:"memory,omitempty"`
Disks []Disk `json:"disks"`
NetworkInterfaces []HostNetworkInterface `json:"networkInterfaces"`
Status string `json:"status"`
LastSeen int64 `json:"lastSeen"`
IntervalSeconds int `json:"intervalSeconds"`
AgentVersion string `json:"agentVersion,omitempty"`
Containers []DockerContainerFrontend `json:"containers"`
Services []DockerServiceFrontend `json:"services"`
Tasks []DockerTaskFrontend `json:"tasks"`
Nodes []DockerNodeFrontend `json:"nodes"`
Secrets []DockerSecretFrontend `json:"secrets"`
Configs []DockerConfigFrontend `json:"configs"`
Swarm *DockerSwarmFrontend `json:"swarm,omitempty"`
Security *DockerHostSecurityFrontend `json:"security,omitempty"`
TokenID string `json:"tokenId,omitempty"`
TokenName string `json:"tokenName,omitempty"`
TokenHint string `json:"tokenHint,omitempty"`
TokenLastUsedAt *int64 `json:"tokenLastUsedAt,omitempty"`
PendingUninstall bool `json:"pendingUninstall"`
Command *DockerHostCommandFrontend `json:"command,omitempty"`
}
func (h DockerHostFrontend) NormalizeCollections() DockerHostFrontend {
if h.LoadAverage == nil {
h.LoadAverage = []float64{}
}
if h.Disks == nil {
h.Disks = []Disk{}
}
if h.NetworkInterfaces == nil {
h.NetworkInterfaces = []HostNetworkInterface{}
}
if h.Containers == nil {
h.Containers = []DockerContainerFrontend{}
}
if h.Services == nil {
h.Services = []DockerServiceFrontend{}
}
if h.Tasks == nil {
h.Tasks = []DockerTaskFrontend{}
}
if h.Nodes == nil {
h.Nodes = []DockerNodeFrontend{}
}
if h.Secrets == nil {
h.Secrets = []DockerSecretFrontend{}
}
if h.Configs == nil {
h.Configs = []DockerConfigFrontend{}
}
for i := range h.Containers {
h.Containers[i] = h.Containers[i].NormalizeCollections()
}
for i := range h.Services {
h.Services[i] = h.Services[i].NormalizeCollections()
}
for i := range h.Secrets {
h.Secrets[i] = h.Secrets[i].NormalizeCollections()
}
for i := range h.Configs {
h.Configs[i] = h.Configs[i].NormalizeCollections()
}
if h.Security != nil {
security := h.Security.NormalizeCollections()
h.Security = &security
}
return h
}
// DockerHostSecurityFrontend exposes docker-host security posture to the UI.
type DockerHostSecurityFrontend struct {
AuthorizationPlugins []string `json:"authorizationPlugins"`
MutatingCommandsBlocked bool `json:"mutatingCommandsBlocked,omitempty"`
MutatingCommandsBlockedReason string `json:"mutatingCommandsBlockedReason,omitempty"`
}
func (s DockerHostSecurityFrontend) NormalizeCollections() DockerHostSecurityFrontend {
if s.AuthorizationPlugins == nil {
s.AuthorizationPlugins = []string{}
}
return s
}
// ConnectedInfrastructureSurfaceFrontend describes one reporting surface that
// Pulse associates with an infrastructure item in the connected-infrastructure
// settings view.
type ConnectedInfrastructureSurfaceFrontend struct {
ID string `json:"id"`
Kind string `json:"kind"`
Label string `json:"label"`
Detail string `json:"detail,omitempty"`
ControlID string `json:"controlId,omitempty"`
Action string `json:"action,omitempty"`
IDLabel string `json:"idLabel,omitempty"`
IDValue string `json:"idValue,omitempty"`
}
// ConnectedInfrastructureItemFrontend is the canonical Connected
// infrastructure projection for the settings surface. It is derived from the
// unified resource model plus reporting-ignore state, so the frontend does not
// need to reconstruct machine rows from raw resource facets.
type ConnectedInfrastructureItemFrontend struct {
ID string `json:"id"`
Name string `json:"name"`
DisplayName string `json:"displayName,omitempty"`
Hostname string `json:"hostname,omitempty"`
Status string `json:"status"`
HealthStatus string `json:"healthStatus,omitempty"`
LastSeen int64 `json:"lastSeen,omitempty"`
RemovedAt int64 `json:"removedAt,omitempty"`
Version string `json:"version,omitempty"`
IsOutdatedBinary bool `json:"isOutdatedBinary,omitempty"`
LinkedNodeID string `json:"linkedNodeId,omitempty"`
LinkedVMID string `json:"linkedVmId,omitempty"`
LinkedContainerID string `json:"linkedContainerId,omitempty"`
CommandsEnabled bool `json:"commandsEnabled,omitempty"`
ScopeAgentID string `json:"scopeAgentId,omitempty"`
UpgradePlatform string `json:"upgradePlatform,omitempty"`
UninstallAgentID string `json:"uninstallAgentId,omitempty"`
UninstallHostname string `json:"uninstallHostname,omitempty"`
Surfaces []ConnectedInfrastructureSurfaceFrontend `json:"surfaces"`
}
func (i ConnectedInfrastructureItemFrontend) NormalizeCollections() ConnectedInfrastructureItemFrontend {
if i.Surfaces == nil {
i.Surfaces = []ConnectedInfrastructureSurfaceFrontend{}
}
return i
}
// KubernetesClusterFrontend represents a Kubernetes cluster for the frontend.
type KubernetesClusterFrontend struct {
ID string `json:"id"`
AgentID string `json:"agentId"`
Name string `json:"name,omitempty"`
DisplayName string `json:"displayName,omitempty"`
CustomDisplayName string `json:"customDisplayName,omitempty"`
Server string `json:"server,omitempty"`
Context string `json:"context,omitempty"`
Version string `json:"version,omitempty"`
Status string `json:"status"`
LastSeen int64 `json:"lastSeen"`
IntervalSeconds int `json:"intervalSeconds"`
AgentVersion string `json:"agentVersion,omitempty"`
TokenID string `json:"tokenId,omitempty"`
TokenName string `json:"tokenName,omitempty"`
TokenHint string `json:"tokenHint,omitempty"`
TokenLastUsedAt *int64 `json:"tokenLastUsedAt,omitempty"`
Hidden bool `json:"hidden"`
PendingUninstall bool `json:"pendingUninstall"`
Nodes []KubernetesNodeFrontend `json:"nodes"`
Pods []KubernetesPodFrontend `json:"pods"`
Deployments []KubernetesDeploymentFrontend `json:"deployments"`
}
func (c KubernetesClusterFrontend) NormalizeCollections() KubernetesClusterFrontend {
if c.Nodes == nil {
c.Nodes = []KubernetesNodeFrontend{}
}
if c.Pods == nil {
c.Pods = []KubernetesPodFrontend{}
}
if c.Deployments == nil {
c.Deployments = []KubernetesDeploymentFrontend{}
}
for i := range c.Nodes {
c.Nodes[i] = c.Nodes[i].NormalizeCollections()
}
for i := range c.Pods {
c.Pods[i] = c.Pods[i].NormalizeCollections()
}
for i := range c.Deployments {
c.Deployments[i] = c.Deployments[i].NormalizeCollections()
}
return c
}
type KubernetesNodeFrontend struct {
UID string `json:"uid"`
Name string `json:"name"`
Ready bool `json:"ready"`
Unschedulable bool `json:"unschedulable,omitempty"`
KubeletVersion string `json:"kubeletVersion,omitempty"`
ContainerRuntimeVersion string `json:"containerRuntimeVersion,omitempty"`
OSImage string `json:"osImage,omitempty"`
KernelVersion string `json:"kernelVersion,omitempty"`
Architecture string `json:"architecture,omitempty"`
CapacityCPU int64 `json:"capacityCpuCores,omitempty"`
CapacityMemoryBytes int64 `json:"capacityMemoryBytes,omitempty"`
CapacityPods int64 `json:"capacityPods,omitempty"`
AllocCPU int64 `json:"allocatableCpuCores,omitempty"`
AllocMemoryBytes int64 `json:"allocatableMemoryBytes,omitempty"`
AllocPods int64 `json:"allocatablePods,omitempty"`
UsageCPUMilliCores int64 `json:"usageCpuMilliCores,omitempty"`
UsageMemoryBytes int64 `json:"usageMemoryBytes,omitempty"`
UsageCPUPercent float64 `json:"usageCpuPercent,omitempty"`
UsageMemoryPercent float64 `json:"usageMemoryPercent,omitempty"`
Roles []string `json:"roles"`
}
func (n KubernetesNodeFrontend) NormalizeCollections() KubernetesNodeFrontend {
if n.Roles == nil {
n.Roles = []string{}
}
return n
}
type KubernetesPodFrontend struct {
UID string `json:"uid"`
Name string `json:"name"`
Namespace string `json:"namespace"`
NodeName string `json:"nodeName,omitempty"`
Phase string `json:"phase,omitempty"`
Reason string `json:"reason,omitempty"`
Message string `json:"message,omitempty"`
QoSClass string `json:"qosClass,omitempty"`
CreatedAt int64 `json:"createdAt,omitempty"`
StartTime *int64 `json:"startTime,omitempty"`
Restarts int `json:"restarts,omitempty"`
UsageCPUMilliCores int `json:"usageCpuMilliCores,omitempty"`
UsageMemoryBytes int64 `json:"usageMemoryBytes,omitempty"`
UsageCPUPercent float64 `json:"usageCpuPercent,omitempty"`
UsageMemoryPercent float64 `json:"usageMemoryPercent,omitempty"`
NetworkRxBytes int64 `json:"networkRxBytes,omitempty"`
NetworkTxBytes int64 `json:"networkTxBytes,omitempty"`
NetInRate float64 `json:"netInRate,omitempty"`
NetOutRate float64 `json:"netOutRate,omitempty"`
EphemeralStorageUsedBytes int64 `json:"ephemeralStorageUsedBytes,omitempty"`
EphemeralStorageCapacityBytes int64 `json:"ephemeralStorageCapacityBytes,omitempty"`
DiskUsagePercent float64 `json:"diskUsagePercent,omitempty"`
Labels map[string]string `json:"labels"`
OwnerKind string `json:"ownerKind,omitempty"`
OwnerName string `json:"ownerName,omitempty"`
Containers []KubernetesPodContainerFrontend `json:"containers"`
}
func (p KubernetesPodFrontend) NormalizeCollections() KubernetesPodFrontend {
if p.Labels == nil {
p.Labels = map[string]string{}
}
if p.Containers == nil {
p.Containers = []KubernetesPodContainerFrontend{}
}
return p
}
type KubernetesPodContainerFrontend struct {
Name string `json:"name"`
Image string `json:"image,omitempty"`
Ready bool `json:"ready"`
RestartCount int32 `json:"restartCount,omitempty"`
State string `json:"state,omitempty"`
Reason string `json:"reason,omitempty"`
Message string `json:"message,omitempty"`
}
type KubernetesDeploymentFrontend struct {
UID string `json:"uid"`
Name string `json:"name"`
Namespace string `json:"namespace"`
CreatedAt int64 `json:"createdAt,omitempty"`
DesiredReplicas int32 `json:"desiredReplicas,omitempty"`
UpdatedReplicas int32 `json:"updatedReplicas,omitempty"`
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
Labels map[string]string `json:"labels"`
}
func (d KubernetesDeploymentFrontend) NormalizeCollections() KubernetesDeploymentFrontend {
if d.Labels == nil {
d.Labels = map[string]string{}
}
return d
}
// DockerContainerFrontend represents a Docker container for the frontend
type DockerContainerFrontend struct {
ID string `json:"id"`
Name string `json:"name"`
Image string `json:"image"`
State string `json:"state"`
Status string `json:"status"`
Health string `json:"health,omitempty"`
CPUPercent float64 `json:"cpuPercent"`
CPUCapacityPercent float64 `json:"cpuCapacityPercent,omitempty"`
MemoryUsage int64 `json:"memoryUsageBytes"`
MemoryLimit int64 `json:"memoryLimitBytes"`
MemoryPercent float64 `json:"memoryPercent"`
UptimeSeconds int64 `json:"uptimeSeconds"`
RestartCount int `json:"restartCount"`
ExitCode int `json:"exitCode"`
OOMKilled *bool `json:"oomKilled,omitempty"`
CreatedAt int64 `json:"createdAt"`
StartedAt *int64 `json:"startedAt,omitempty"`
FinishedAt *int64 `json:"finishedAt,omitempty"`
Ports []DockerContainerPortFrontend `json:"ports"`
Labels map[string]string `json:"labels"`
Networks []DockerContainerNetworkFrontend `json:"networks"`
WritableLayerBytes int64 `json:"writableLayerBytes,omitempty"`
RootFilesystemBytes int64 `json:"rootFilesystemBytes,omitempty"`
BlockIO *DockerContainerBlockIOFrontend `json:"blockIo,omitempty"`
Mounts []DockerContainerMountFrontend `json:"mounts"`
Filesystems []filesystem.Observation `json:"filesystems,omitempty"`
Podman *DockerPodmanContainerFrontend `json:"podman,omitempty"`
UpdateStatus *DockerContainerUpdateStatusFrontend `json:"updateStatus,omitempty"`
}
func (c DockerContainerFrontend) NormalizeCollections() DockerContainerFrontend {
if c.Ports == nil {
c.Ports = []DockerContainerPortFrontend{}
}
if c.Labels == nil {
c.Labels = map[string]string{}
}
if c.Networks == nil {
c.Networks = []DockerContainerNetworkFrontend{}
}
if c.Mounts == nil {
c.Mounts = []DockerContainerMountFrontend{}
}
return c
}
// DockerContainerUpdateStatusFrontend tracks the image update status for a container.
type DockerContainerUpdateStatusFrontend struct {
UpdateAvailable bool `json:"updateAvailable"`
CurrentDigest string `json:"currentDigest,omitempty"`
LatestDigest string `json:"latestDigest,omitempty"`
LastChecked int64 `json:"lastChecked"`
Error string `json:"error,omitempty"` // e.g., "rate limited", "auth required"
}
// DockerContainerPortFrontend represents a container port mapping
type DockerContainerPortFrontend struct {
PrivatePort int `json:"privatePort"`
PublicPort int `json:"publicPort,omitempty"`
Protocol string `json:"protocol"`
IP string `json:"ip,omitempty"`
}
// DockerContainerNetworkFrontend represents container network addresses
type DockerContainerNetworkFrontend struct {
Name string `json:"name"`
IPv4 string `json:"ipv4,omitempty"`
IPv6 string `json:"ipv6,omitempty"`
}
// DockerContainerBlockIOFrontend exposes aggregate block IO counters.
type DockerContainerBlockIOFrontend struct {
ReadBytes uint64 `json:"readBytes,omitempty"`
WriteBytes uint64 `json:"writeBytes,omitempty"`
ReadRateBytesPerSecond *float64 `json:"readRateBytesPerSecond,omitempty"`
WriteRateBytesPerSecond *float64 `json:"writeRateBytesPerSecond,omitempty"`
}
// DockerContainerMountFrontend represents a container mount for the UI.
type DockerContainerMountFrontend struct {
Type string `json:"type,omitempty"`
Source string `json:"source,omitempty"`
Destination string `json:"destination,omitempty"`
Mode string `json:"mode,omitempty"`
RW bool `json:"rw"`
Propagation string `json:"propagation,omitempty"`
Name string `json:"name,omitempty"`
Driver string `json:"driver,omitempty"`
}
// DockerPodmanContainerFrontend exposes podman-specific metadata.
type DockerPodmanContainerFrontend struct {
PodName string `json:"podName,omitempty"`
PodID string `json:"podId,omitempty"`
Infra bool `json:"infra,omitempty"`
ComposeProject string `json:"composeProject,omitempty"`
ComposeService string `json:"composeService,omitempty"`
ComposeWorkdir string `json:"composeWorkdir,omitempty"`
ComposeConfigHash string `json:"composeConfigHash,omitempty"`
AutoUpdatePolicy string `json:"autoUpdatePolicy,omitempty"`
AutoUpdateRestart string `json:"autoUpdateRestart,omitempty"`
UserNamespace string `json:"userNamespace,omitempty"`
}
// DockerServiceFrontend represents a Swarm service for the frontend.
type DockerServiceFrontend struct {
ID string `json:"id"`
Name string `json:"name"`
Stack string `json:"stack,omitempty"`
Image string `json:"image,omitempty"`
Mode string `json:"mode,omitempty"`
DesiredTasks int `json:"desiredTasks,omitempty"`
RunningTasks int `json:"runningTasks,omitempty"`
CompletedTasks int `json:"completedTasks,omitempty"`
Labels map[string]string `json:"labels"`
EndpointPorts []DockerServicePortFrontend `json:"endpointPorts"`
UpdateStatus *DockerServiceUpdateFrontend `json:"updateStatus,omitempty"`
CreatedAt *int64 `json:"createdAt,omitempty"`
UpdatedAt *int64 `json:"updatedAt,omitempty"`
}
func (s DockerServiceFrontend) NormalizeCollections() DockerServiceFrontend {
if s.Labels == nil {
s.Labels = map[string]string{}
}
if s.EndpointPorts == nil {
s.EndpointPorts = []DockerServicePortFrontend{}
}
return s
}
// DockerServicePortFrontend represents a published service port.
type DockerServicePortFrontend struct {
Name string `json:"name,omitempty"`
Protocol string `json:"protocol,omitempty"`
TargetPort uint32 `json:"targetPort,omitempty"`
PublishedPort uint32 `json:"publishedPort,omitempty"`
PublishMode string `json:"publishMode,omitempty"`
}
// DockerServiceUpdateFrontend exposes service update status to the UI.
type DockerServiceUpdateFrontend struct {
State string `json:"state,omitempty"`
Message string `json:"message,omitempty"`
CompletedAt *int64 `json:"completedAt,omitempty"`
}
// DockerTaskFrontend represents a Swarm task replica.
type DockerTaskFrontend struct {
ID string `json:"id"`
ServiceID string `json:"serviceId,omitempty"`
ServiceName string `json:"serviceName,omitempty"`
Slot int `json:"slot,omitempty"`
NodeID string `json:"nodeId,omitempty"`
NodeName string `json:"nodeName,omitempty"`
DesiredState string `json:"desiredState,omitempty"`
CurrentState string `json:"currentState,omitempty"`
Error string `json:"error,omitempty"`
Message string `json:"message,omitempty"`
ContainerID string `json:"containerId,omitempty"`
ContainerName string `json:"containerName,omitempty"`
CreatedAt *int64 `json:"createdAt,omitempty"`
UpdatedAt *int64 `json:"updatedAt,omitempty"`
StartedAt *int64 `json:"startedAt,omitempty"`
CompletedAt *int64 `json:"completedAt,omitempty"`
}
// DockerNodeFrontend represents a Swarm node.
type DockerNodeFrontend struct {
ID string `json:"id"`
Hostname string `json:"hostname,omitempty"`
Role string `json:"role,omitempty"`
Availability string `json:"availability,omitempty"`
State string `json:"state,omitempty"`
Message string `json:"message,omitempty"`
Address string `json:"address,omitempty"`
ManagerReachability string `json:"managerReachability,omitempty"`
ManagerAddress string `json:"managerAddress,omitempty"`
Leader bool `json:"leader,omitempty"`
EngineVersion string `json:"engineVersion,omitempty"`
OS string `json:"os,omitempty"`
Architecture string `json:"architecture,omitempty"`
NanoCPUs int64 `json:"nanoCpus,omitempty"`
MemoryBytes int64 `json:"memoryBytes,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
EngineLabels map[string]string `json:"engineLabels,omitempty"`
CreatedAt *int64 `json:"createdAt,omitempty"`
UpdatedAt *int64 `json:"updatedAt,omitempty"`
}
// DockerSecretFrontend represents Docker Swarm secret metadata for the frontend.
type DockerSecretFrontend struct {
ID string `json:"id"`
Name string `json:"name"`
Labels map[string]string `json:"labels"`
DriverName string `json:"driverName,omitempty"`
TemplatingDriver string `json:"templatingDriver,omitempty"`
CreatedAt *int64 `json:"createdAt,omitempty"`
UpdatedAt *int64 `json:"updatedAt,omitempty"`
}
func (s DockerSecretFrontend) NormalizeCollections() DockerSecretFrontend {
if s.Labels == nil {
s.Labels = map[string]string{}
}
return s
}
// DockerConfigFrontend represents Docker Swarm config metadata for the frontend.
type DockerConfigFrontend struct {
ID string `json:"id"`
Name string `json:"name"`
Labels map[string]string `json:"labels"`
TemplatingDriver string `json:"templatingDriver,omitempty"`
CreatedAt *int64 `json:"createdAt,omitempty"`
UpdatedAt *int64 `json:"updatedAt,omitempty"`
}
func (c DockerConfigFrontend) NormalizeCollections() DockerConfigFrontend {
if c.Labels == nil {
c.Labels = map[string]string{}
}
return c
}
// DockerSwarmFrontend summarises node-level swarm details.
type DockerSwarmFrontend struct {
NodeID string `json:"nodeId,omitempty"`
NodeRole string `json:"nodeRole,omitempty"`
LocalState string `json:"localState,omitempty"`
ControlAvailable bool `json:"controlAvailable,omitempty"`
ClusterID string `json:"clusterId,omitempty"`
ClusterName string `json:"clusterName,omitempty"`
Scope string `json:"scope,omitempty"`
Error string `json:"error,omitempty"`
}
// DockerHostCommandFrontend exposes docker host command state to the UI.
type DockerHostCommandFrontend struct {
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
Message string `json:"message,omitempty"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
DispatchedAt *int64 `json:"dispatchedAt,omitempty"`
AcknowledgedAt *int64 `json:"acknowledgedAt,omitempty"`
CompletedAt *int64 `json:"completedAt,omitempty"`
FailedAt *int64 `json:"failedAt,omitempty"`
FailureReason string `json:"failureReason,omitempty"`
ExpiresAt *int64 `json:"expiresAt,omitempty"`
}
// HostFrontend represents a generic infrastructure host exposed to the UI.
type HostFrontend struct {
ID string `json:"id"`
Hostname string `json:"hostname"`
DisplayName string `json:"displayName"`
Platform string `json:"platform,omitempty"`
OSName string `json:"osName,omitempty"`
OSVersion string `json:"osVersion,omitempty"`
KernelVersion string `json:"kernelVersion,omitempty"`
Architecture string `json:"architecture,omitempty"`
CPUCount int `json:"cpuCount,omitempty"`
CPUUsage float64 `json:"cpuUsage,omitempty"`
LoadAverage []float64 `json:"loadAverage"`
Memory *Memory `json:"memory,omitempty"`
Disks []Disk `json:"disks"`
DiskIO []DiskIO `json:"diskIO"`
NetworkInterfaces []HostNetworkInterface `json:"networkInterfaces"`
Sensors *HostSensorSummaryFrontend `json:"sensors,omitempty"`
Status string `json:"status"`
UptimeSeconds int64 `json:"uptimeSeconds,omitempty"`
LastSeen int64 `json:"lastSeen"`
IntervalSeconds int `json:"intervalSeconds,omitempty"`
AgentVersion string `json:"agentVersion,omitempty"`
MachineID string `json:"machineId,omitempty"`
TokenID string `json:"tokenId,omitempty"`
TokenName string `json:"tokenName,omitempty"`
TokenHint string `json:"tokenHint,omitempty"`
TokenLastUsedAt *int64 `json:"tokenLastUsedAt,omitempty"`
Tags []string `json:"tags"`
CommandsEnabled bool `json:"commandsEnabled,omitempty"` // Whether AI command execution is enabled
AppliedConfig *AgentConfigFingerprint `json:"appliedConfig,omitempty"`
AgentUpdate *AgentUpdateStatus `json:"agentUpdate,omitempty"`
AgentModules []AgentModuleStatus `json:"agentModules,omitempty"`
AgentPrivilege *AgentPrivilegeStatus `json:"agentPrivilege,omitempty"`
PackageUpdates *HostPackageUpdateStatus `json:"packageUpdates,omitempty"`
StorageCleanup *HostStorageCleanupStatus `json:"storageCleanup,omitempty"`
IsLegacy bool `json:"isLegacy,omitempty"` // True if using legacy agent protocol
LinkedNodeID string `json:"linkedNodeId,omitempty"` // ID of linked PVE node (if running on a node)
}
func (h HostFrontend) NormalizeCollections() HostFrontend {
if h.LoadAverage == nil {
h.LoadAverage = []float64{}
}
if h.Disks == nil {
h.Disks = []Disk{}
}
if h.DiskIO == nil {
h.DiskIO = []DiskIO{}
}
if h.NetworkInterfaces == nil {
h.NetworkInterfaces = []HostNetworkInterface{}
}
if h.Tags == nil {
h.Tags = []string{}
}
if h.Sensors != nil {
sensors := h.Sensors.NormalizeCollections()
h.Sensors = &sensors
}
return h
}
// HostSensorSummaryFrontend mirrors HostSensorSummary with primitives for the frontend.
type HostSensorSummaryFrontend struct {
TemperatureCelsius map[string]float64 `json:"temperatureCelsius"`
FanRPM map[string]float64 `json:"fanRpm"`
PowerWatts map[string]float64 `json:"powerWatts"`
Additional map[string]float64 `json:"additional"`
Custom []HostCustomSensorMetricFrontend `json:"custom"`
GPU []HostGPUSensorFrontend `json:"gpu"`
ThermalState *HostThermalState `json:"thermalState,omitempty"`
SMART []HostDiskSMARTFrontend `json:"smart"` // S.M.A.R.T. disk data
}
type HostCustomSensorMetricFrontend struct {
ID string `json:"id"`
Name string `json:"name"`
Group string `json:"group,omitempty"`
Subgroup string `json:"subgroup,omitempty"`
Kind string `json:"kind,omitempty"`
Unit string `json:"unit,omitempty"`
Value *float64 `json:"value,omitempty"`
Status string `json:"status"`
ObservedAt time.Time `json:"observedAt"`
EventAt *time.Time `json:"eventAt,omitempty"`
Error string `json:"error,omitempty"`
AlertOnError bool `json:"alertOnError,omitempty"`
Stale bool `json:"stale,omitempty"`
}
type HostGPUSensorFrontend struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
TemperatureCelsius *float64 `json:"temperatureCelsius,omitempty"`
UtilizationPercent *float64 `json:"utilizationPercent,omitempty"`
MemoryUsedBytes *int64 `json:"memoryUsedBytes,omitempty"`
MemoryTotalBytes *int64 `json:"memoryTotalBytes,omitempty"`
}
func (s HostSensorSummaryFrontend) NormalizeCollections() HostSensorSummaryFrontend {
if s.TemperatureCelsius == nil {
s.TemperatureCelsius = map[string]float64{}
}
if s.FanRPM == nil {
s.FanRPM = map[string]float64{}
}
if s.PowerWatts == nil {
s.PowerWatts = map[string]float64{}
}
if s.Additional == nil {
s.Additional = map[string]float64{}
}
if s.Custom == nil {
s.Custom = []HostCustomSensorMetricFrontend{}
}
if s.GPU == nil {
s.GPU = []HostGPUSensorFrontend{}
}
if s.SMART == nil {
s.SMART = []HostDiskSMARTFrontend{}
}
return s
}
// HostDiskSMARTFrontend represents S.M.A.R.T. data for a disk from a host agent.
type HostDiskSMARTFrontend struct {
Device string `json:"device"` // Device name (e.g., sda)
Model string `json:"model,omitempty"` // Disk model
Serial string `json:"serial,omitempty"` // Serial number
WWN string `json:"wwn,omitempty"` // World Wide Name
Type string `json:"type,omitempty"` // Transport type: sata, sas, nvme
Controller string `json:"controller,omitempty"` // Controller association
Target string `json:"target,omitempty"` // Controller target/HCTL
SizeBytes int64 `json:"sizeBytes,omitempty"`
Temperature int `json:"temperature"` // Temperature in Celsius
Health string `json:"health,omitempty"` // PASSED, FAILED, UNKNOWN
Standby bool `json:"standby,omitempty"` // True if disk was in standby
IO *DiskIO `json:"io,omitempty"`
Collection *diskinventory.CollectionStatus `json:"collection,omitempty"`
Attributes *SMARTAttributes `json:"attributes,omitempty"`
}
// StorageFrontend represents Storage with frontend-friendly field names
type StorageFrontend struct {
ID string `json:"id"`
Storage string `json:"storage"` // Maps to Name
Name string `json:"name"`
Node string `json:"node"`
Instance string `json:"instance"`
Nodes []string `json:"nodes"`
NodeIDs []string `json:"nodeIds"`
NodeCount int `json:"nodeCount,omitempty"`
Type string `json:"type"`
Status string `json:"status"`
Pool string `json:"pool,omitempty"`
Total int64 `json:"total"`
Used int64 `json:"used"`
Avail int64 `json:"avail"` // Maps to Free
Free int64 `json:"free"`
Usage float64 `json:"usage"`
Content string `json:"content"`
Shared bool `json:"shared"`
Enabled bool `json:"enabled"`
Active bool `json:"active"`
ZFSPool *ZFSPool `json:"zfsPool,omitempty"`
}
func (s StorageFrontend) NormalizeCollections() StorageFrontend {
if s.Nodes == nil {
s.Nodes = []string{}
}
if s.NodeIDs == nil {
s.NodeIDs = []string{}
}
if s.ZFSPool != nil {
pool := s.ZFSPool.NormalizeCollections()
s.ZFSPool = &pool
}
return s
}
// CephClusterFrontend represents a Ceph cluster with frontend-friendly field names
type CephClusterFrontend struct {
ID string `json:"id"`
Instance string `json:"instance"`
Name string `json:"name"`
FSID string `json:"fsid,omitempty"`
Health string `json:"health"`
HealthMessage string `json:"healthMessage,omitempty"`
TotalBytes int64 `json:"totalBytes"`
UsedBytes int64 `json:"usedBytes"`
AvailableBytes int64 `json:"availableBytes"`
UsagePercent float64 `json:"usagePercent"`
NumMons int `json:"numMons"`
NumMgrs int `json:"numMgrs"`
NumOSDs int `json:"numOsds"`
NumOSDsUp int `json:"numOsdsUp"`
NumOSDsIn int `json:"numOsdsIn"`
NumPGs int `json:"numPGs"`
Pools []CephPool `json:"pools"`
Services []CephServiceStatus `json:"services"`
LastUpdated int64 `json:"lastUpdated"`
}
func (c CephClusterFrontend) NormalizeCollections() CephClusterFrontend {
if c.Pools == nil {
c.Pools = []CephPool{}
}
if c.Services == nil {
c.Services = []CephServiceStatus{}
}
return c
}
// ReplicationJobFrontend represents a replication job for the frontend.
type ReplicationJobFrontend struct {
ID string `json:"id"`
Instance string `json:"instance"`
JobID string `json:"jobId"`
JobNumber int `json:"jobNumber,omitempty"`
Guest string `json:"guest,omitempty"`
GuestID int `json:"guestId,omitempty"`
GuestName string `json:"guestName,omitempty"`
GuestType string `json:"guestType,omitempty"`
GuestNode string `json:"guestNode,omitempty"`
SourceNode string `json:"sourceNode,omitempty"`
SourceStorage string `json:"sourceStorage,omitempty"`
TargetNode string `json:"targetNode,omitempty"`
TargetStorage string `json:"targetStorage,omitempty"`
Schedule string `json:"schedule,omitempty"`
Type string `json:"type,omitempty"`
Enabled bool `json:"enabled"`
State string `json:"state,omitempty"`
Status string `json:"status,omitempty"`
LastSyncStatus string `json:"lastSyncStatus,omitempty"`
LastSyncTime int64 `json:"lastSyncTime,omitempty"`
LastSyncUnix int64 `json:"lastSyncUnix,omitempty"`
LastSyncDurationSeconds int `json:"lastSyncDurationSeconds,omitempty"`
LastSyncDurationHuman string `json:"lastSyncDurationHuman,omitempty"`
NextSyncTime int64 `json:"nextSyncTime,omitempty"`
NextSyncUnix int64 `json:"nextSyncUnix,omitempty"`
DurationSeconds int `json:"durationSeconds,omitempty"`
DurationHuman string `json:"durationHuman,omitempty"`
FailCount int `json:"failCount,omitempty"`
Error string `json:"error,omitempty"`
Comment string `json:"comment,omitempty"`
RemoveJob string `json:"removeJob,omitempty"`
RateLimitMbps *float64 `json:"rateLimitMbps,omitempty"`
PolledAt int64 `json:"polledAt,omitempty"`
}
// StateFrontend represents the state with frontend-friendly field names
type StateFrontend struct {
ActiveAlerts []Alert `json:"activeAlerts"` // Active alerts
RecentlyResolved []ResolvedAlert `json:"recentlyResolved"` // Recently resolved alerts
Metrics []Metric `json:"metrics"` // Time-series metrics
Performance Performance `json:"performance"` // Polling/runtime performance
ConnectionHealth map[string]bool `json:"connectionHealth"` // Keep as is
Stats Stats `json:"stats"` // Runtime statistics
LastUpdate int64 `json:"lastUpdate"` // Unix timestamp
TemperatureMonitoringEnabled bool `json:"temperatureMonitoringEnabled"` // Global temperature monitoring setting
PVETagColors map[string]string `json:"pveTagColors"` // Tag name -> "#rrggbb" from Proxmox datacenter config
PVETagStyles map[string]PVETagStyle `json:"pveTagStyles"` // PVE instance -> Proxmox tag style
// Unified resources - the new way to access all monitored entities
Resources []ResourceFrontend `json:"resources"`
ConnectedInfrastructure []ConnectedInfrastructureItemFrontend `json:"connectedInfrastructure"`
// CapabilityCatalog maps content-hash ids to capability blobs referenced by
// ResourceFrontend.CapabilitiesRef. Estates carry only a handful of
// distinct capability sets, so the catalog replaces per-resource inline
// duplication in broadcast payloads. The websocket delta engine diffs
// top-level state fields generically, so catalog changes ride the same
// frame as the resource refs that need them.
CapabilityCatalog map[string]json.RawMessage `json:"capabilityCatalog,omitempty"`
// PolicyCatalog and AISafeSummaryCatalog dedupe the equally small sets of
// distinct non-default policy postures and templated AI-safe summaries the
// same way, referenced by ResourceFrontend.PolicyRef and
// ResourceFrontend.AISafeSummaryRef.
PolicyCatalog map[string]json.RawMessage `json:"policyCatalog,omitempty"`
AISafeSummaryCatalog map[string]string `json:"aiSafeSummaryCatalog,omitempty"`
}
// EmptyStateFrontend returns a canonical empty frontend state with stable
// collection/object semantics for API and websocket consumers.
func EmptyStateFrontend() StateFrontend {
return StateFrontend{}.NormalizeCollections()
}
// NormalizeCollections ensures StateFrontend preserves stable empty collection
// semantics instead of leaking nil slices or maps to clients.
func (s StateFrontend) NormalizeCollections() StateFrontend {
if s.ActiveAlerts == nil {
s.ActiveAlerts = []Alert{}
}
if s.RecentlyResolved == nil {
s.RecentlyResolved = []ResolvedAlert{}
}
if s.Metrics == nil {
s.Metrics = []Metric{}
}
if s.ConnectionHealth == nil {
s.ConnectionHealth = map[string]bool{}
}
if s.PVETagColors == nil {
s.PVETagColors = map[string]string{}
}
if s.PVETagStyles == nil {
s.PVETagStyles = map[string]PVETagStyle{}
}
if s.Performance.APICallDuration == nil {
s.Performance.APICallDuration = map[string]float64{}
}
if s.Resources == nil {
s.Resources = []ResourceFrontend{}
}
if s.ConnectedInfrastructure == nil {
s.ConnectedInfrastructure = []ConnectedInfrastructureItemFrontend{}
}
for i := range s.ConnectedInfrastructure {
s.ConnectedInfrastructure[i] = s.ConnectedInfrastructure[i].NormalizeCollections()
}
for i := range s.Resources {
s.Resources[i] = s.Resources[i].NormalizeCollections()
}
return s
}
// ResourceFrontend is the frontend representation of a unified Resource.
// This mirrors resources.Resource but with time.Time converted to Unix milliseconds.
type ResourceFrontend struct {
// Identity
ID string `json:"id"`
Type string `json:"type"`
Technology string `json:"technology,omitempty"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
// Platform/Source
PlatformID string `json:"platformId"`
PlatformType string `json:"platformType"`
SourceType string `json:"sourceType"`
Sources []string `json:"sources,omitempty"`
// Hierarchy
ParentID string `json:"parentId,omitempty"`
ParentName string `json:"parentName,omitempty"`
ChildCount int `json:"childCount,omitempty"`
ClusterID string `json:"clusterId,omitempty"`
// Universal Metrics
Status string `json:"status"`
CPU *ResourceMetricFrontend `json:"cpu,omitempty"`
Memory *ResourceMetricFrontend `json:"memory,omitempty"`
Disk *ResourceMetricFrontend `json:"disk,omitempty"`
Network *ResourceNetworkFrontend `json:"network,omitempty"`
DiskIO *ResourceDiskIOFrontend `json:"diskIO,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
Uptime *int64 `json:"uptime,omitempty"`
// Metadata
Tags []string `json:"tags"`
Labels map[string]string `json:"labels"`
CustomURL string `json:"customUrl,omitempty"`
LastSeen int64 `json:"lastSeen"` // Unix milliseconds
Health json.RawMessage `json:"health,omitempty"`
Alerts []ResourceAlertFrontend `json:"alerts"`
IncidentCount int `json:"incidentCount,omitempty"`
IncidentCode string `json:"incidentCode,omitempty"`
IncidentSeverity string `json:"incidentSeverity,omitempty"`
IncidentSummary string `json:"incidentSummary,omitempty"`
IncidentCategory string `json:"incidentCategory,omitempty"`
IncidentLabel string `json:"incidentLabel,omitempty"`
IncidentPriority int `json:"incidentPriority,omitempty"`
IncidentImpactSummary string `json:"incidentImpactSummary,omitempty"`
IncidentUrgency string `json:"incidentUrgency,omitempty"`
IncidentAction string `json:"incidentAction,omitempty"`
// Identity for deduplication
Identity *ResourceIdentityFrontend `json:"identity,omitempty"`
DiscoveryTarget json.RawMessage `json:"discoveryTarget,omitempty"`
MetricsTarget json.RawMessage `json:"metricsTarget,omitempty"`
Canonical json.RawMessage `json:"canonicalIdentity,omitempty"`
Policy json.RawMessage `json:"policy,omitempty"`
AISafeSummary string `json:"aiSafeSummary,omitempty"`
Capabilities json.RawMessage `json:"capabilities,omitempty"`
// CapabilitiesRef references an entry in StateFrontend.CapabilityCatalog.
// Broadcast payloads dedupe the small set of distinct capability blobs
// through the catalog instead of inlining ~3KB per resource; consumers
// resolve the ref at ingestion. Inline Capabilities remains valid for
// producers that do not carry a catalog.
CapabilitiesRef string `json:"capabilitiesRef,omitempty"`
// PolicyRef and AISafeSummaryRef reference entries in
// StateFrontend.PolicyCatalog / AISafeSummaryCatalog under the same
// dedupe contract; inline Policy / AISafeSummary remain valid for
// producers that do not carry catalogs.
PolicyRef string `json:"policyRef,omitempty"`
AISafeSummaryRef string `json:"aiSafeSummaryRef,omitempty"`
Relationships json.RawMessage `json:"relationships,omitempty"`
RecentChanges json.RawMessage `json:"recentChanges,omitempty"`
FacetCounts json.RawMessage `json:"facetCounts,omitempty"`
Incidents json.RawMessage `json:"incidents,omitempty"`
Proxmox json.RawMessage `json:"proxmox,omitempty"`
Storage json.RawMessage `json:"storage,omitempty"`
Agent json.RawMessage `json:"agent,omitempty"`
Docker json.RawMessage `json:"docker,omitempty"`
PBS json.RawMessage `json:"pbs,omitempty"`
PMG json.RawMessage `json:"pmg,omitempty"`
Kubernetes json.RawMessage `json:"kubernetes,omitempty"`
PhysicalDisk json.RawMessage `json:"physicalDisk,omitempty"`
Ceph json.RawMessage `json:"ceph,omitempty"`
TrueNAS json.RawMessage `json:"truenas,omitempty"`
VMware json.RawMessage `json:"vmware,omitempty"`
Availability json.RawMessage `json:"availability,omitempty"`
AvailabilityChecks json.RawMessage `json:"availabilityChecks,omitempty"`
// Platform-specific data (JSON blob)
PlatformData json.RawMessage `json:"platformData,omitempty"`
}
func (r ResourceFrontend) NormalizeCollections() ResourceFrontend {
if r.Tags == nil {
r.Tags = []string{}
}
if r.Labels == nil {
r.Labels = map[string]string{}
}
if r.Alerts == nil {
r.Alerts = []ResourceAlertFrontend{}
}
if r.Identity != nil {
identity := r.Identity.NormalizeCollections()
r.Identity = &identity
}
return r
}
// ResourceMetricFrontend represents a metric value for the frontend.
type ResourceMetricFrontend struct {
Current float64 `json:"current"`
Total *int64 `json:"total,omitempty"`
Used *int64 `json:"used,omitempty"`
Free *int64 `json:"free,omitempty"`
}
// ResourceNetworkFrontend represents network metrics for the frontend.
type ResourceNetworkFrontend struct {
RXBytes int64 `json:"rxBytes"`
TXBytes int64 `json:"txBytes"`
}
// ResourceDiskIOFrontend represents aggregate disk I/O rates for the frontend.
type ResourceDiskIOFrontend struct {
ReadRate *int64 `json:"readRate,omitempty"`
WriteRate *int64 `json:"writeRate,omitempty"`
}
// ResourceAlertFrontend represents an alert on a resource.
type ResourceAlertFrontend struct {
ID string `json:"id"`
Type string `json:"type"`
Level string `json:"level"`
Message string `json:"message"`
Value float64 `json:"value"`
Threshold float64 `json:"threshold"`
StartTime int64 `json:"startTime"` // Unix milliseconds
}
// ResourceIdentityFrontend contains identity info for deduplication.
type ResourceIdentityFrontend struct {
Hostname string `json:"hostname,omitempty"`
MachineID string `json:"machineId,omitempty"`
IPs []string `json:"ips"`
}
func (i ResourceIdentityFrontend) NormalizeCollections() ResourceIdentityFrontend {
if i.IPs == nil {
i.IPs = []string{}
}
return i
}