mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-27 05:57:27 +00:00
da3fcf69f7
The Machines/Infrastructure drawer and the Docker host drawer built their "Needs attention" list from the alerts embedded in each resource. No backend code ever filled that list, so every resource carried `alerts: []` and both drawers stayed silent while an alert was open: a mock stack had 0 of 142 resources with embedded alerts while 14 alerts were active, and a real dev Pulse showed `alerts: []` on a host whose temperature alert was open. Both drawers now read the websocket activeAlerts map, the source the Proxmox node, guest and Mail Gateway drawers already use, through one matcher, getAlertsForUnifiedResource. It keys a resource on the ids the backend writes for it (unified id, canonical primary and superseded ids, Proxmox source id, metrics target) and, for agent and Docker hosts, agent:<id> and docker:<host source id> plus the disk, RAID, container and service alerts nested under them. Rows show the affected resource and metric like the other drawers, most severe first. The dead embedded path is gone: the frontend ResourceAlert type and its unused hasAlerts filter, the Proxmox summary alertCount nobody rendered, and ResourceFrontend.alerts / ResourceAlertInput on the backend, which also drops "alerts":[] from every resource in each broadcast. Now that incident alerts reach the infrastructure drawer, a health reason counts as stated when it is a whole sentence of an alert's text, so a storage incident alert that joins its summary and impact does not repeat beside either reason.
1356 lines
39 KiB
Go
1356 lines
39 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/agents/filesystem"
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/diskinventory"
|
|
)
|
|
|
|
// ToFrontend converts a State to StateFrontend
|
|
func (s *State) ToFrontend() StateFrontend {
|
|
return s.GetSnapshot().ToFrontend()
|
|
}
|
|
|
|
// ToFrontend converts a Node to NodeFrontend
|
|
func (n Node) ToFrontend() NodeFrontend {
|
|
nf := NodeFrontend{
|
|
ID: n.ID,
|
|
Node: n.Name,
|
|
Name: n.Name,
|
|
DisplayName: n.DisplayName,
|
|
Instance: n.Instance,
|
|
Host: n.Host,
|
|
GuestURL: n.GuestURL,
|
|
Status: n.Status,
|
|
Type: n.Type,
|
|
CPU: n.CPU,
|
|
Mem: n.Memory.Used,
|
|
MaxMem: n.Memory.Total,
|
|
MaxDisk: n.Disk.Total,
|
|
Uptime: n.Uptime,
|
|
LoadAverage: n.LoadAverage,
|
|
KernelVersion: n.KernelVersion,
|
|
PVEVersion: n.PVEVersion,
|
|
CPUInfo: n.CPUInfo,
|
|
LastSeen: timeToUnixMillis(n.LastSeen),
|
|
ConnectionHealth: n.ConnectionHealth,
|
|
IsClusterMember: n.IsClusterMember,
|
|
ClusterName: n.ClusterName,
|
|
TemperatureMonitoringEnabled: n.TemperatureMonitoringEnabled,
|
|
LinkedAgentID: n.LinkedAgentID,
|
|
}
|
|
|
|
// Include full Memory object if it has data
|
|
if n.Memory.Total > 0 {
|
|
nf.Memory = &n.Memory
|
|
}
|
|
|
|
// Include full Disk object if it has data
|
|
if n.Disk.Total > 0 {
|
|
nf.Disk = &n.Disk
|
|
}
|
|
|
|
// Include temperature data if available
|
|
if n.Temperature != nil && n.Temperature.Available {
|
|
nf.Temperature = n.Temperature
|
|
}
|
|
|
|
if nf.DisplayName == "" {
|
|
nf.DisplayName = nf.Name
|
|
}
|
|
|
|
return nf.NormalizeCollections()
|
|
}
|
|
|
|
// ToFrontend converts a VM to VMFrontend
|
|
func (v VM) ToFrontend() VMFrontend {
|
|
vm := VMFrontend{
|
|
ID: v.ID,
|
|
VMID: v.VMID,
|
|
Name: v.Name,
|
|
Node: v.Node,
|
|
Pool: v.Pool,
|
|
Instance: v.Instance,
|
|
Status: v.Status,
|
|
Type: v.Type,
|
|
CPU: v.CPU,
|
|
CPUs: v.CPUs,
|
|
Mem: v.Memory.Used,
|
|
MaxMem: v.Memory.Total,
|
|
NetIn: zeroIfNegative(v.NetworkIn),
|
|
NetOut: zeroIfNegative(v.NetworkOut),
|
|
DiskRead: zeroIfNegative(v.DiskRead),
|
|
DiskWrite: zeroIfNegative(v.DiskWrite),
|
|
Uptime: v.Uptime,
|
|
Template: v.Template,
|
|
Lock: v.Lock,
|
|
LastSeen: timeToUnixMillis(v.LastSeen),
|
|
DiskStatusReason: v.DiskStatusReason,
|
|
GuestAgentStatus: v.GuestAgentStatus,
|
|
GuestAgentExpected: v.GuestAgentExpected,
|
|
}
|
|
|
|
// Convert tags array to string
|
|
if len(v.Tags) > 0 {
|
|
vm.Tags = strings.Join(v.Tags, ",")
|
|
}
|
|
|
|
vm.LastBackup = timeToUnixMillis(v.LastBackup)
|
|
|
|
// Include full Memory object if it has data
|
|
if v.Memory.Total > 0 {
|
|
vm.Memory = &v.Memory
|
|
}
|
|
|
|
// Include full Disk object if it has data
|
|
if v.Disk.Total > 0 {
|
|
vm.DiskObj = &v.Disk
|
|
}
|
|
|
|
// Include individual disks array if available
|
|
if len(v.Disks) > 0 {
|
|
vm.Disks = v.Disks
|
|
}
|
|
|
|
if len(v.IPAddresses) > 0 {
|
|
vm.IPAddresses = append([]string(nil), v.IPAddresses...)
|
|
}
|
|
|
|
if v.OSName != "" {
|
|
vm.OSName = v.OSName
|
|
}
|
|
|
|
if v.OSVersion != "" {
|
|
vm.OSVersion = v.OSVersion
|
|
}
|
|
|
|
if v.AgentVersion != "" {
|
|
vm.AgentVersion = v.AgentVersion
|
|
}
|
|
|
|
if len(v.NetworkInterfaces) > 0 {
|
|
vm.NetworkInterfaces = make([]GuestNetworkInterface, len(v.NetworkInterfaces))
|
|
copy(vm.NetworkInterfaces, v.NetworkInterfaces)
|
|
}
|
|
|
|
return vm.NormalizeCollections()
|
|
}
|
|
|
|
// ToFrontend converts a Container to ContainerFrontend
|
|
func (c Container) ToFrontend() ContainerFrontend {
|
|
ct := ContainerFrontend{
|
|
ID: c.ID,
|
|
VMID: c.VMID,
|
|
Name: c.Name,
|
|
Node: c.Node,
|
|
Pool: c.Pool,
|
|
Instance: c.Instance,
|
|
Status: c.Status,
|
|
Type: c.Type,
|
|
CPU: c.CPU,
|
|
CPUs: c.CPUs,
|
|
Mem: c.Memory.Used,
|
|
MaxMem: c.Memory.Total,
|
|
NetIn: zeroIfNegative(c.NetworkIn),
|
|
NetOut: zeroIfNegative(c.NetworkOut),
|
|
DiskRead: zeroIfNegative(c.DiskRead),
|
|
DiskWrite: zeroIfNegative(c.DiskWrite),
|
|
Uptime: c.Uptime,
|
|
Template: c.Template,
|
|
Lock: c.Lock,
|
|
LastSeen: timeToUnixMillis(c.LastSeen),
|
|
}
|
|
|
|
// OCI containers are classified separately from traditional LXC containers in the UI.
|
|
// Keep the frontend payload stable even if some internal sources only persist Type.
|
|
isOCI := c.IsOCI || strings.EqualFold(strings.TrimSpace(c.Type), "oci")
|
|
if isOCI {
|
|
ct.IsOCI = true
|
|
ct.Type = "oci"
|
|
}
|
|
|
|
// Preserve template metadata (LXC template or OCI image reference).
|
|
if c.OSTemplate != "" {
|
|
ct.OSTemplate = c.OSTemplate
|
|
}
|
|
|
|
// Convert tags array to string
|
|
if len(c.Tags) > 0 {
|
|
ct.Tags = strings.Join(c.Tags, ",")
|
|
}
|
|
|
|
ct.LastBackup = timeToUnixMillis(c.LastBackup)
|
|
|
|
// Include full Memory object if it has data
|
|
if c.Memory.Total > 0 {
|
|
ct.Memory = &c.Memory
|
|
}
|
|
|
|
// Include full Disk object if it has data
|
|
if c.Disk.Total > 0 {
|
|
ct.DiskObj = &c.Disk
|
|
}
|
|
|
|
// Include individual disks array if available
|
|
if len(c.Disks) > 0 {
|
|
ct.Disks = c.Disks
|
|
}
|
|
|
|
if len(c.IPAddresses) > 0 {
|
|
ct.IPAddresses = append([]string(nil), c.IPAddresses...)
|
|
}
|
|
|
|
if len(c.NetworkInterfaces) > 0 {
|
|
ct.NetworkInterfaces = make([]GuestNetworkInterface, len(c.NetworkInterfaces))
|
|
copy(ct.NetworkInterfaces, c.NetworkInterfaces)
|
|
}
|
|
|
|
if c.OSName != "" {
|
|
ct.OSName = c.OSName
|
|
}
|
|
|
|
return ct.NormalizeCollections()
|
|
}
|
|
|
|
// ToFrontend converts a DockerHost to DockerHostFrontend
|
|
func (d DockerHost) ToFrontend() DockerHostFrontend {
|
|
h := DockerHostFrontend{
|
|
ID: d.ID,
|
|
AgentID: d.AgentID,
|
|
Hostname: d.Hostname,
|
|
DisplayName: d.DisplayName,
|
|
CustomDisplayName: d.CustomDisplayName,
|
|
MachineID: d.MachineID,
|
|
OS: d.OS,
|
|
KernelVersion: d.KernelVersion,
|
|
Architecture: d.Architecture,
|
|
Runtime: d.Runtime,
|
|
RuntimeVersion: d.RuntimeVersion,
|
|
DockerVersion: d.DockerVersion,
|
|
CollectionMode: d.CollectionMode,
|
|
CPUs: d.CPUs,
|
|
TotalMemoryBytes: d.TotalMemoryBytes,
|
|
UptimeSeconds: d.UptimeSeconds,
|
|
CPUUsagePercent: d.CPUUsage,
|
|
Status: d.Status,
|
|
LastSeen: timeToUnixMillis(d.LastSeen),
|
|
IntervalSeconds: d.IntervalSeconds,
|
|
AgentVersion: d.AgentVersion,
|
|
Containers: make([]DockerContainerFrontend, len(d.Containers)),
|
|
}
|
|
|
|
if h.DisplayName == "" {
|
|
h.DisplayName = h.Hostname
|
|
}
|
|
|
|
h.PendingUninstall = d.PendingUninstall
|
|
|
|
if d.TokenID != "" {
|
|
h.TokenID = d.TokenID
|
|
h.TokenName = d.TokenName
|
|
h.TokenHint = d.TokenHint
|
|
h.TokenLastUsedAt = optionalTimeToUnixMillis(d.TokenLastUsedAt)
|
|
}
|
|
|
|
for i, ct := range d.Containers {
|
|
h.Containers[i] = ct.ToFrontend()
|
|
}
|
|
|
|
if len(d.Services) > 0 {
|
|
h.Services = make([]DockerServiceFrontend, len(d.Services))
|
|
for i, svc := range d.Services {
|
|
h.Services[i] = svc.ToFrontend()
|
|
}
|
|
}
|
|
|
|
if len(d.Tasks) > 0 {
|
|
h.Tasks = make([]DockerTaskFrontend, len(d.Tasks))
|
|
for i, task := range d.Tasks {
|
|
h.Tasks[i] = task.ToFrontend()
|
|
}
|
|
}
|
|
|
|
if len(d.Nodes) > 0 {
|
|
h.Nodes = make([]DockerNodeFrontend, len(d.Nodes))
|
|
for i, node := range d.Nodes {
|
|
h.Nodes[i] = node.ToFrontend()
|
|
}
|
|
}
|
|
|
|
if len(d.Secrets) > 0 {
|
|
h.Secrets = make([]DockerSecretFrontend, len(d.Secrets))
|
|
for i, secret := range d.Secrets {
|
|
h.Secrets[i] = secret.ToFrontend()
|
|
}
|
|
}
|
|
|
|
if len(d.Configs) > 0 {
|
|
h.Configs = make([]DockerConfigFrontend, len(d.Configs))
|
|
for i, config := range d.Configs {
|
|
h.Configs[i] = config.ToFrontend()
|
|
}
|
|
}
|
|
|
|
if d.Swarm != nil {
|
|
sw := d.Swarm.ToFrontend()
|
|
h.Swarm = &sw
|
|
}
|
|
if d.Security != nil {
|
|
security := DockerHostSecurityFrontend{
|
|
AuthorizationPlugins: append([]string(nil), d.Security.AuthorizationPlugins...),
|
|
MutatingCommandsBlocked: d.Security.MutatingCommandsBlocked,
|
|
MutatingCommandsBlockedReason: d.Security.MutatingCommandsBlockedReason,
|
|
}.NormalizeCollections()
|
|
h.Security = &security
|
|
}
|
|
|
|
if len(d.LoadAverage) > 0 {
|
|
h.LoadAverage = append([]float64(nil), d.LoadAverage...)
|
|
}
|
|
|
|
if (d.Memory != Memory{}) {
|
|
mem := d.Memory
|
|
h.Memory = &mem
|
|
}
|
|
|
|
if len(d.Disks) > 0 {
|
|
h.Disks = append([]Disk(nil), d.Disks...)
|
|
}
|
|
|
|
if len(d.NetworkInterfaces) > 0 {
|
|
h.NetworkInterfaces = make([]HostNetworkInterface, len(d.NetworkInterfaces))
|
|
copy(h.NetworkInterfaces, d.NetworkInterfaces)
|
|
}
|
|
|
|
if d.Command != nil {
|
|
h.Command = toDockerHostCommandFrontend(*d.Command)
|
|
}
|
|
|
|
return h.NormalizeCollections()
|
|
}
|
|
|
|
// ToFrontend converts a KubernetesCluster to its frontend representation.
|
|
func (c KubernetesCluster) ToFrontend() KubernetesClusterFrontend {
|
|
cluster := KubernetesClusterFrontend{
|
|
ID: c.ID,
|
|
AgentID: c.AgentID,
|
|
Name: c.Name,
|
|
DisplayName: c.DisplayName,
|
|
CustomDisplayName: c.CustomDisplayName,
|
|
Server: c.Server,
|
|
Context: c.Context,
|
|
Version: c.Version,
|
|
Status: c.Status,
|
|
LastSeen: timeToUnixMillis(c.LastSeen),
|
|
IntervalSeconds: c.IntervalSeconds,
|
|
AgentVersion: c.AgentVersion,
|
|
TokenID: c.TokenID,
|
|
TokenName: c.TokenName,
|
|
TokenHint: c.TokenHint,
|
|
Hidden: c.Hidden,
|
|
PendingUninstall: c.PendingUninstall,
|
|
}
|
|
|
|
if c.TokenLastUsedAt != nil && !c.TokenLastUsedAt.IsZero() {
|
|
ts := c.TokenLastUsedAt.Unix() * 1000
|
|
cluster.TokenLastUsedAt = &ts
|
|
}
|
|
|
|
if len(c.Nodes) > 0 {
|
|
cluster.Nodes = make([]KubernetesNodeFrontend, len(c.Nodes))
|
|
for i, n := range c.Nodes {
|
|
cluster.Nodes[i] = KubernetesNodeFrontend{
|
|
UID: n.UID,
|
|
Name: n.Name,
|
|
Ready: n.Ready,
|
|
Unschedulable: n.Unschedulable,
|
|
KubeletVersion: n.KubeletVersion,
|
|
ContainerRuntimeVersion: n.ContainerRuntimeVersion,
|
|
OSImage: n.OSImage,
|
|
KernelVersion: n.KernelVersion,
|
|
Architecture: n.Architecture,
|
|
CapacityCPU: n.CapacityCPU,
|
|
CapacityMemoryBytes: n.CapacityMemoryBytes,
|
|
CapacityPods: n.CapacityPods,
|
|
AllocCPU: n.AllocCPU,
|
|
AllocMemoryBytes: n.AllocMemoryBytes,
|
|
AllocPods: n.AllocPods,
|
|
UsageCPUMilliCores: n.UsageCPUMilliCores,
|
|
UsageMemoryBytes: n.UsageMemoryBytes,
|
|
UsageCPUPercent: n.UsageCPUPercent,
|
|
UsageMemoryPercent: n.UsageMemoryPercent,
|
|
Roles: append([]string(nil), n.Roles...),
|
|
}.NormalizeCollections()
|
|
}
|
|
}
|
|
|
|
if len(c.Pods) > 0 {
|
|
cluster.Pods = make([]KubernetesPodFrontend, len(c.Pods))
|
|
for i, p := range c.Pods {
|
|
labels := make(map[string]string, len(p.Labels))
|
|
for k, v := range p.Labels {
|
|
labels[k] = v
|
|
}
|
|
containers := make([]KubernetesPodContainerFrontend, 0, len(p.Containers))
|
|
for _, cn := range p.Containers {
|
|
containers = append(containers, KubernetesPodContainerFrontend{
|
|
Name: cn.Name,
|
|
Image: cn.Image,
|
|
Ready: cn.Ready,
|
|
RestartCount: cn.RestartCount,
|
|
State: cn.State,
|
|
Reason: cn.Reason,
|
|
Message: cn.Message,
|
|
})
|
|
}
|
|
|
|
cluster.Pods[i] = KubernetesPodFrontend{
|
|
UID: p.UID,
|
|
Name: p.Name,
|
|
Namespace: p.Namespace,
|
|
NodeName: p.NodeName,
|
|
Phase: p.Phase,
|
|
Reason: p.Reason,
|
|
Message: p.Message,
|
|
QoSClass: p.QoSClass,
|
|
CreatedAt: timeToUnixMillis(p.CreatedAt),
|
|
StartTime: optionalTimeToUnixMillis(p.StartTime),
|
|
Restarts: p.Restarts,
|
|
UsageCPUMilliCores: p.UsageCPUMilliCores,
|
|
UsageMemoryBytes: p.UsageMemoryBytes,
|
|
UsageCPUPercent: p.UsageCPUPercent,
|
|
UsageMemoryPercent: p.UsageMemoryPercent,
|
|
NetworkRxBytes: p.NetworkRxBytes,
|
|
NetworkTxBytes: p.NetworkTxBytes,
|
|
NetInRate: p.NetInRate,
|
|
NetOutRate: p.NetOutRate,
|
|
EphemeralStorageUsedBytes: p.EphemeralStorageUsedBytes,
|
|
EphemeralStorageCapacityBytes: p.EphemeralStorageCapacityBytes,
|
|
DiskUsagePercent: p.DiskUsagePercent,
|
|
Labels: labels,
|
|
OwnerKind: p.OwnerKind,
|
|
OwnerName: p.OwnerName,
|
|
Containers: containers,
|
|
}.NormalizeCollections()
|
|
}
|
|
}
|
|
|
|
if len(c.Deployments) > 0 {
|
|
cluster.Deployments = make([]KubernetesDeploymentFrontend, len(c.Deployments))
|
|
for i, d := range c.Deployments {
|
|
labels := make(map[string]string, len(d.Labels))
|
|
for k, v := range d.Labels {
|
|
labels[k] = v
|
|
}
|
|
cluster.Deployments[i] = KubernetesDeploymentFrontend{
|
|
UID: d.UID,
|
|
Name: d.Name,
|
|
Namespace: d.Namespace,
|
|
CreatedAt: timeToUnixMillis(d.CreatedAt),
|
|
DesiredReplicas: d.DesiredReplicas,
|
|
UpdatedReplicas: d.UpdatedReplicas,
|
|
ReadyReplicas: d.ReadyReplicas,
|
|
AvailableReplicas: d.AvailableReplicas,
|
|
ObservedGeneration: d.ObservedGeneration,
|
|
Labels: labels,
|
|
}.NormalizeCollections()
|
|
}
|
|
}
|
|
|
|
if cluster.DisplayName == "" && cluster.Name != "" {
|
|
cluster.DisplayName = cluster.Name
|
|
}
|
|
|
|
if cluster.DisplayName == "" {
|
|
cluster.DisplayName = cluster.ID
|
|
}
|
|
|
|
return cluster.NormalizeCollections()
|
|
}
|
|
|
|
func timeToUnixMillis(t time.Time) int64 {
|
|
if t.IsZero() {
|
|
return 0
|
|
}
|
|
return t.Unix() * 1000
|
|
}
|
|
|
|
// optionalTimeToUnixMillis converts a *time.Time to *int64 (Unix milliseconds).
|
|
// Returns nil if the pointer is nil or the time is zero.
|
|
func optionalTimeToUnixMillis(t *time.Time) *int64 {
|
|
if t == nil || t.IsZero() {
|
|
return nil
|
|
}
|
|
ms := t.Unix() * 1000
|
|
return &ms
|
|
}
|
|
|
|
// ToFrontend converts a Host to HostFrontend.
|
|
func (h Host) ToFrontend() HostFrontend {
|
|
host := HostFrontend{
|
|
ID: h.ID,
|
|
Hostname: h.Hostname,
|
|
DisplayName: h.DisplayName,
|
|
Platform: h.Platform,
|
|
OSName: h.OSName,
|
|
OSVersion: h.OSVersion,
|
|
KernelVersion: h.KernelVersion,
|
|
Architecture: h.Architecture,
|
|
CPUCount: h.CPUCount,
|
|
CPUUsage: h.CPUUsage,
|
|
Status: h.Status,
|
|
UptimeSeconds: h.UptimeSeconds,
|
|
IntervalSeconds: h.IntervalSeconds,
|
|
AgentVersion: h.AgentVersion,
|
|
MachineID: h.MachineID,
|
|
TokenID: h.TokenID,
|
|
TokenName: h.TokenName,
|
|
TokenHint: h.TokenHint,
|
|
Tags: append([]string(nil), h.Tags...),
|
|
LastSeen: timeToUnixMillis(h.LastSeen),
|
|
CommandsEnabled: h.CommandsEnabled,
|
|
AppliedConfig: cloneAgentConfigFingerprint(h.AppliedConfig),
|
|
AgentUpdate: cloneAgentUpdateStatus(h.AgentUpdate),
|
|
AgentModules: cloneAgentModuleStatuses(h.AgentModules),
|
|
AgentPrivilege: cloneAgentPrivilegeStatus(h.AgentPrivilege),
|
|
PackageUpdates: cloneHostPackageUpdateStatus(h.PackageUpdates),
|
|
StorageCleanup: cloneHostStorageCleanupStatus(h.StorageCleanup),
|
|
IsLegacy: h.IsLegacy,
|
|
LinkedNodeID: h.LinkedNodeID,
|
|
}
|
|
|
|
// Fall back to Hostname if DisplayName is empty
|
|
if host.DisplayName == "" && h.Hostname != "" {
|
|
host.DisplayName = h.Hostname
|
|
}
|
|
|
|
if len(h.LoadAverage) > 0 {
|
|
host.LoadAverage = append([]float64(nil), h.LoadAverage...)
|
|
}
|
|
|
|
if (h.Memory != Memory{}) {
|
|
mem := h.Memory
|
|
host.Memory = &mem
|
|
}
|
|
|
|
if len(h.Disks) > 0 {
|
|
host.Disks = append([]Disk(nil), h.Disks...)
|
|
}
|
|
|
|
if len(h.DiskIO) > 0 {
|
|
host.DiskIO = append([]DiskIO(nil), h.DiskIO...)
|
|
}
|
|
|
|
if len(h.NetworkInterfaces) > 0 {
|
|
host.NetworkInterfaces = make([]HostNetworkInterface, len(h.NetworkInterfaces))
|
|
copy(host.NetworkInterfaces, h.NetworkInterfaces)
|
|
}
|
|
|
|
if s := hostSensorSummaryToFrontend(h.Sensors); s != nil {
|
|
host.Sensors = s
|
|
}
|
|
|
|
host.TokenLastUsedAt = optionalTimeToUnixMillis(h.TokenLastUsedAt)
|
|
|
|
return host.NormalizeCollections()
|
|
}
|
|
|
|
func cloneAgentConfigFingerprint(value *AgentConfigFingerprint) *AgentConfigFingerprint {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
copy := *value
|
|
return ©
|
|
}
|
|
|
|
func cloneAgentUpdateStatus(value *AgentUpdateStatus) *AgentUpdateStatus {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
copy := *value
|
|
if value.LastCheckedAt != nil {
|
|
checked := *value.LastCheckedAt
|
|
copy.LastCheckedAt = &checked
|
|
}
|
|
if value.LastAttemptAt != nil {
|
|
attempt := *value.LastAttemptAt
|
|
copy.LastAttemptAt = &attempt
|
|
}
|
|
if value.LastSuccessAt != nil {
|
|
success := *value.LastSuccessAt
|
|
copy.LastSuccessAt = &success
|
|
}
|
|
return ©
|
|
}
|
|
|
|
func cloneAgentModuleStatuses(values []AgentModuleStatus) []AgentModuleStatus {
|
|
if len(values) == 0 {
|
|
return nil
|
|
}
|
|
return append([]AgentModuleStatus(nil), values...)
|
|
}
|
|
|
|
func cloneAgentPrivilegeStatus(value *AgentPrivilegeStatus) *AgentPrivilegeStatus {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
copy := *value
|
|
return ©
|
|
}
|
|
|
|
// ToFrontend converts a DockerContainer to DockerContainerFrontend
|
|
func (c DockerContainer) ToFrontend() DockerContainerFrontend {
|
|
container := DockerContainerFrontend{
|
|
ID: c.ID,
|
|
Name: c.Name,
|
|
Image: c.Image,
|
|
State: c.State,
|
|
Status: c.Status,
|
|
Health: c.Health,
|
|
CPUPercent: c.CPUPercent,
|
|
CPUCapacityPercent: c.CPUCapacityPercent,
|
|
MemoryUsage: c.MemoryUsage,
|
|
MemoryLimit: c.MemoryLimit,
|
|
MemoryPercent: c.MemoryPercent,
|
|
UptimeSeconds: c.UptimeSeconds,
|
|
RestartCount: c.RestartCount,
|
|
ExitCode: c.ExitCode,
|
|
OOMKilled: cloneBoolPtr(c.OOMKilled),
|
|
CreatedAt: c.CreatedAt.Unix() * 1000,
|
|
Labels: nil,
|
|
WritableLayerBytes: c.WritableLayerBytes,
|
|
RootFilesystemBytes: c.RootFilesystemBytes,
|
|
}
|
|
|
|
if c.StartedAt != nil {
|
|
ms := c.StartedAt.Unix() * 1000
|
|
container.StartedAt = &ms
|
|
}
|
|
|
|
if c.FinishedAt != nil {
|
|
ms := c.FinishedAt.Unix() * 1000
|
|
container.FinishedAt = &ms
|
|
}
|
|
|
|
if len(c.Ports) > 0 {
|
|
ports := make([]DockerContainerPortFrontend, len(c.Ports))
|
|
for i, port := range c.Ports {
|
|
ports[i] = DockerContainerPortFrontend(port)
|
|
}
|
|
container.Ports = ports
|
|
}
|
|
|
|
if len(c.Labels) > 0 {
|
|
container.Labels = make(map[string]string, len(c.Labels))
|
|
for k, v := range c.Labels {
|
|
container.Labels[k] = v
|
|
}
|
|
}
|
|
|
|
if len(c.Networks) > 0 {
|
|
networks := make([]DockerContainerNetworkFrontend, len(c.Networks))
|
|
for i, net := range c.Networks {
|
|
networks[i] = DockerContainerNetworkFrontend(net)
|
|
}
|
|
container.Networks = networks
|
|
}
|
|
|
|
if c.BlockIO != nil {
|
|
container.BlockIO = &DockerContainerBlockIOFrontend{
|
|
ReadBytes: c.BlockIO.ReadBytes,
|
|
WriteBytes: c.BlockIO.WriteBytes,
|
|
ReadRateBytesPerSecond: c.BlockIO.ReadRateBytesPerSecond,
|
|
WriteRateBytesPerSecond: c.BlockIO.WriteRateBytesPerSecond,
|
|
}
|
|
}
|
|
|
|
if len(c.Mounts) > 0 {
|
|
mounts := make([]DockerContainerMountFrontend, len(c.Mounts))
|
|
for i, mount := range c.Mounts {
|
|
mounts[i] = DockerContainerMountFrontend(mount)
|
|
}
|
|
container.Mounts = mounts
|
|
}
|
|
container.Filesystems = filesystem.Clone(c.Filesystems)
|
|
|
|
if c.Podman != nil {
|
|
container.Podman = &DockerPodmanContainerFrontend{
|
|
PodName: c.Podman.PodName,
|
|
PodID: c.Podman.PodID,
|
|
Infra: c.Podman.Infra,
|
|
ComposeProject: c.Podman.ComposeProject,
|
|
ComposeService: c.Podman.ComposeService,
|
|
ComposeWorkdir: c.Podman.ComposeWorkdir,
|
|
ComposeConfigHash: c.Podman.ComposeConfigHash,
|
|
AutoUpdatePolicy: c.Podman.AutoUpdatePolicy,
|
|
AutoUpdateRestart: c.Podman.AutoUpdateRestart,
|
|
UserNamespace: c.Podman.UserNamespace,
|
|
}
|
|
}
|
|
|
|
if c.UpdateStatus != nil {
|
|
container.UpdateStatus = &DockerContainerUpdateStatusFrontend{
|
|
UpdateAvailable: c.UpdateStatus.UpdateAvailable,
|
|
CurrentDigest: c.UpdateStatus.CurrentDigest,
|
|
LatestDigest: c.UpdateStatus.LatestDigest,
|
|
LastChecked: timeToUnixMillis(c.UpdateStatus.LastChecked),
|
|
Error: c.UpdateStatus.Error,
|
|
}
|
|
}
|
|
|
|
return container.NormalizeCollections()
|
|
}
|
|
|
|
// ToFrontend converts a DockerService to DockerServiceFrontend.
|
|
func (s DockerService) ToFrontend() DockerServiceFrontend {
|
|
service := DockerServiceFrontend{
|
|
ID: s.ID,
|
|
Name: s.Name,
|
|
Stack: s.Stack,
|
|
Image: s.Image,
|
|
Mode: s.Mode,
|
|
DesiredTasks: s.DesiredTasks,
|
|
RunningTasks: s.RunningTasks,
|
|
CompletedTasks: s.CompletedTasks,
|
|
Labels: nil,
|
|
}
|
|
|
|
if len(s.Labels) > 0 {
|
|
service.Labels = make(map[string]string, len(s.Labels))
|
|
for k, v := range s.Labels {
|
|
service.Labels[k] = v
|
|
}
|
|
}
|
|
|
|
if len(s.EndpointPorts) > 0 {
|
|
service.EndpointPorts = make([]DockerServicePortFrontend, len(s.EndpointPorts))
|
|
for i, port := range s.EndpointPorts {
|
|
service.EndpointPorts[i] = port.ToFrontend()
|
|
}
|
|
}
|
|
|
|
if s.UpdateStatus != nil {
|
|
update := s.UpdateStatus.ToFrontend()
|
|
service.UpdateStatus = &update
|
|
}
|
|
|
|
if s.CreatedAt != nil && !s.CreatedAt.IsZero() {
|
|
ts := s.CreatedAt.Unix() * 1000
|
|
service.CreatedAt = &ts
|
|
}
|
|
if s.UpdatedAt != nil && !s.UpdatedAt.IsZero() {
|
|
ts := s.UpdatedAt.Unix() * 1000
|
|
service.UpdatedAt = &ts
|
|
}
|
|
|
|
return service.NormalizeCollections()
|
|
}
|
|
|
|
// ToFrontend converts a DockerServicePort to DockerServicePortFrontend.
|
|
func (p DockerServicePort) ToFrontend() DockerServicePortFrontend {
|
|
return DockerServicePortFrontend(p)
|
|
}
|
|
|
|
// ToFrontend converts a DockerServiceUpdate to DockerServiceUpdateFrontend.
|
|
func (u DockerServiceUpdate) ToFrontend() DockerServiceUpdateFrontend {
|
|
update := DockerServiceUpdateFrontend{
|
|
State: u.State,
|
|
Message: u.Message,
|
|
}
|
|
if u.CompletedAt != nil && !u.CompletedAt.IsZero() {
|
|
ts := u.CompletedAt.Unix() * 1000
|
|
update.CompletedAt = &ts
|
|
}
|
|
return update
|
|
}
|
|
|
|
// ToFrontend converts a DockerTask to DockerTaskFrontend.
|
|
func (t DockerTask) ToFrontend() DockerTaskFrontend {
|
|
task := DockerTaskFrontend{
|
|
ID: t.ID,
|
|
ServiceID: t.ServiceID,
|
|
ServiceName: t.ServiceName,
|
|
Slot: t.Slot,
|
|
NodeID: t.NodeID,
|
|
NodeName: t.NodeName,
|
|
DesiredState: t.DesiredState,
|
|
CurrentState: t.CurrentState,
|
|
Error: t.Error,
|
|
Message: t.Message,
|
|
ContainerID: t.ContainerID,
|
|
ContainerName: t.ContainerName,
|
|
}
|
|
|
|
if !t.CreatedAt.IsZero() {
|
|
ts := t.CreatedAt.Unix() * 1000
|
|
task.CreatedAt = &ts
|
|
}
|
|
if t.UpdatedAt != nil && !t.UpdatedAt.IsZero() {
|
|
ts := t.UpdatedAt.Unix() * 1000
|
|
task.UpdatedAt = &ts
|
|
}
|
|
if t.StartedAt != nil && !t.StartedAt.IsZero() {
|
|
ts := t.StartedAt.Unix() * 1000
|
|
task.StartedAt = &ts
|
|
}
|
|
if t.CompletedAt != nil && !t.CompletedAt.IsZero() {
|
|
ts := t.CompletedAt.Unix() * 1000
|
|
task.CompletedAt = &ts
|
|
}
|
|
|
|
return task
|
|
}
|
|
|
|
// ToFrontend converts a DockerNode to DockerNodeFrontend.
|
|
func (n DockerNode) ToFrontend() DockerNodeFrontend {
|
|
node := DockerNodeFrontend{
|
|
ID: n.ID,
|
|
Hostname: n.Hostname,
|
|
Role: n.Role,
|
|
Availability: n.Availability,
|
|
State: n.State,
|
|
Message: n.Message,
|
|
Address: n.Address,
|
|
ManagerReachability: n.ManagerReachability,
|
|
ManagerAddress: n.ManagerAddress,
|
|
Leader: n.Leader,
|
|
EngineVersion: n.EngineVersion,
|
|
OS: n.OS,
|
|
Architecture: n.Architecture,
|
|
NanoCPUs: n.NanoCPUs,
|
|
MemoryBytes: n.MemoryBytes,
|
|
Labels: make(map[string]string, len(n.Labels)),
|
|
EngineLabels: make(map[string]string, len(n.EngineLabels)),
|
|
}
|
|
for key, value := range n.Labels {
|
|
node.Labels[key] = value
|
|
}
|
|
for key, value := range n.EngineLabels {
|
|
node.EngineLabels[key] = value
|
|
}
|
|
if !n.CreatedAt.IsZero() {
|
|
ts := n.CreatedAt.Unix() * 1000
|
|
node.CreatedAt = &ts
|
|
}
|
|
if n.UpdatedAt != nil && !n.UpdatedAt.IsZero() {
|
|
ts := n.UpdatedAt.Unix() * 1000
|
|
node.UpdatedAt = &ts
|
|
}
|
|
return node
|
|
}
|
|
|
|
// ToFrontend converts a DockerSecret to DockerSecretFrontend.
|
|
func (s DockerSecret) ToFrontend() DockerSecretFrontend {
|
|
secret := DockerSecretFrontend{
|
|
ID: s.ID,
|
|
Name: s.Name,
|
|
DriverName: s.DriverName,
|
|
TemplatingDriver: s.TemplatingDriver,
|
|
}
|
|
if len(s.Labels) > 0 {
|
|
secret.Labels = make(map[string]string, len(s.Labels))
|
|
for key, value := range s.Labels {
|
|
secret.Labels[key] = value
|
|
}
|
|
}
|
|
if !s.CreatedAt.IsZero() {
|
|
ts := s.CreatedAt.Unix() * 1000
|
|
secret.CreatedAt = &ts
|
|
}
|
|
if s.UpdatedAt != nil && !s.UpdatedAt.IsZero() {
|
|
ts := s.UpdatedAt.Unix() * 1000
|
|
secret.UpdatedAt = &ts
|
|
}
|
|
return secret.NormalizeCollections()
|
|
}
|
|
|
|
// ToFrontend converts a DockerConfig to DockerConfigFrontend.
|
|
func (c DockerConfig) ToFrontend() DockerConfigFrontend {
|
|
config := DockerConfigFrontend{
|
|
ID: c.ID,
|
|
Name: c.Name,
|
|
TemplatingDriver: c.TemplatingDriver,
|
|
}
|
|
if len(c.Labels) > 0 {
|
|
config.Labels = make(map[string]string, len(c.Labels))
|
|
for key, value := range c.Labels {
|
|
config.Labels[key] = value
|
|
}
|
|
}
|
|
if !c.CreatedAt.IsZero() {
|
|
ts := c.CreatedAt.Unix() * 1000
|
|
config.CreatedAt = &ts
|
|
}
|
|
if c.UpdatedAt != nil && !c.UpdatedAt.IsZero() {
|
|
ts := c.UpdatedAt.Unix() * 1000
|
|
config.UpdatedAt = &ts
|
|
}
|
|
return config.NormalizeCollections()
|
|
}
|
|
|
|
// ToFrontend converts DockerSwarmInfo to DockerSwarmFrontend.
|
|
func (s DockerSwarmInfo) ToFrontend() DockerSwarmFrontend {
|
|
return DockerSwarmFrontend(s)
|
|
}
|
|
|
|
func hostSensorSummaryToFrontend(src HostSensorSummary) *HostSensorSummaryFrontend {
|
|
if len(src.TemperatureCelsius) == 0 && len(src.FanRPM) == 0 && len(src.PowerWatts) == 0 && len(src.Additional) == 0 && len(src.Custom) == 0 && len(src.GPU) == 0 && src.ThermalState == nil && len(src.SMART) == 0 {
|
|
return nil
|
|
}
|
|
|
|
dest := &HostSensorSummaryFrontend{}
|
|
if len(src.TemperatureCelsius) > 0 {
|
|
dest.TemperatureCelsius = copyStringFloatMap(src.TemperatureCelsius)
|
|
}
|
|
if len(src.FanRPM) > 0 {
|
|
dest.FanRPM = copyStringFloatMap(src.FanRPM)
|
|
}
|
|
if len(src.PowerWatts) > 0 {
|
|
dest.PowerWatts = copyStringFloatMap(src.PowerWatts)
|
|
}
|
|
if len(src.Additional) > 0 {
|
|
dest.Additional = copyStringFloatMap(src.Additional)
|
|
}
|
|
if len(src.Custom) > 0 {
|
|
dest.Custom = make([]HostCustomSensorMetricFrontend, len(src.Custom))
|
|
for i, metric := range src.Custom {
|
|
dest.Custom[i] = HostCustomSensorMetricFrontend{
|
|
ID: metric.ID,
|
|
Name: metric.Name,
|
|
Group: metric.Group,
|
|
Subgroup: metric.Subgroup,
|
|
Kind: metric.Kind,
|
|
Unit: metric.Unit,
|
|
Value: cloneFloat64Ptr(metric.Value),
|
|
Status: metric.Status,
|
|
ObservedAt: metric.ObservedAt,
|
|
EventAt: cloneTimePtr(metric.EventAt),
|
|
Error: metric.Error,
|
|
AlertOnError: metric.AlertOnError,
|
|
Stale: metric.Stale,
|
|
}
|
|
}
|
|
}
|
|
if len(src.GPU) > 0 {
|
|
dest.GPU = make([]HostGPUSensorFrontend, len(src.GPU))
|
|
for i, gpu := range src.GPU {
|
|
dest.GPU[i] = HostGPUSensorFrontend{
|
|
ID: gpu.ID,
|
|
Name: gpu.Name,
|
|
TemperatureCelsius: cloneFloat64Ptr(gpu.TemperatureCelsius),
|
|
UtilizationPercent: cloneFloat64Ptr(gpu.UtilizationPercent),
|
|
MemoryUsedBytes: cloneInt64Ptr(gpu.MemoryUsedBytes),
|
|
MemoryTotalBytes: cloneInt64Ptr(gpu.MemoryTotalBytes),
|
|
}
|
|
}
|
|
}
|
|
if src.ThermalState != nil {
|
|
dest.ThermalState = copyHostThermalState(src.ThermalState)
|
|
}
|
|
if len(src.SMART) > 0 {
|
|
dest.SMART = make([]HostDiskSMARTFrontend, len(src.SMART))
|
|
for i, disk := range src.SMART {
|
|
dest.SMART[i] = HostDiskSMARTFrontend{
|
|
Device: disk.Device,
|
|
Model: disk.Model,
|
|
Serial: disk.Serial,
|
|
WWN: disk.WWN,
|
|
Type: disk.Type,
|
|
Controller: disk.Controller,
|
|
Target: disk.Target,
|
|
SizeBytes: disk.SizeBytes,
|
|
Temperature: disk.Temperature,
|
|
Health: disk.Health,
|
|
Standby: disk.Standby,
|
|
IO: cloneDiskIOForFrontend(disk.IO),
|
|
Collection: diskinventory.CloneStatus(disk.Collection),
|
|
Attributes: cloneSMARTAttributes(disk.Attributes),
|
|
}
|
|
}
|
|
}
|
|
normalized := dest.NormalizeCollections()
|
|
return &normalized
|
|
}
|
|
|
|
func cloneDiskIOForFrontend(src *DiskIO) *DiskIO {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
dest := *src
|
|
return &dest
|
|
}
|
|
|
|
func copyHostThermalState(src *HostThermalState) *HostThermalState {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
dest := *src
|
|
dest.ThermalWarningLevel = cloneIntPtr(src.ThermalWarningLevel)
|
|
dest.PerformanceWarningLevel = cloneIntPtr(src.PerformanceWarningLevel)
|
|
dest.CPUPowerStatus = cloneIntPtr(src.CPUPowerStatus)
|
|
dest.LimitsPercent = copyStringIntMap(src.LimitsPercent)
|
|
return &dest
|
|
}
|
|
|
|
func copyStringFloatMap(src map[string]float64) map[string]float64 {
|
|
if len(src) == 0 {
|
|
return nil
|
|
}
|
|
dest := make(map[string]float64, len(src))
|
|
for k, v := range src {
|
|
dest[k] = v
|
|
}
|
|
return dest
|
|
}
|
|
|
|
func copyStringIntMap(src map[string]int) map[string]int {
|
|
if len(src) == 0 {
|
|
return nil
|
|
}
|
|
dest := make(map[string]int, len(src))
|
|
for k, v := range src {
|
|
dest[k] = v
|
|
}
|
|
return dest
|
|
}
|
|
|
|
func toDockerHostCommandFrontend(cmd DockerHostCommandStatus) *DockerHostCommandFrontend {
|
|
result := &DockerHostCommandFrontend{
|
|
ID: cmd.ID,
|
|
Type: cmd.Type,
|
|
Status: cmd.Status,
|
|
Message: cmd.Message,
|
|
CreatedAt: cmd.CreatedAt.Unix() * 1000,
|
|
UpdatedAt: cmd.UpdatedAt.Unix() * 1000,
|
|
}
|
|
|
|
if cmd.DispatchedAt != nil {
|
|
ms := cmd.DispatchedAt.Unix() * 1000
|
|
result.DispatchedAt = &ms
|
|
}
|
|
if cmd.AcknowledgedAt != nil {
|
|
ms := cmd.AcknowledgedAt.Unix() * 1000
|
|
result.AcknowledgedAt = &ms
|
|
}
|
|
if cmd.CompletedAt != nil {
|
|
ms := cmd.CompletedAt.Unix() * 1000
|
|
result.CompletedAt = &ms
|
|
}
|
|
if cmd.FailedAt != nil {
|
|
ms := cmd.FailedAt.Unix() * 1000
|
|
result.FailedAt = &ms
|
|
}
|
|
if cmd.FailureReason != "" {
|
|
result.FailureReason = cmd.FailureReason
|
|
}
|
|
if cmd.ExpiresAt != nil {
|
|
ms := cmd.ExpiresAt.Unix() * 1000
|
|
result.ExpiresAt = &ms
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// ToFrontend converts Storage to StorageFrontend
|
|
func (s Storage) ToFrontend() StorageFrontend {
|
|
return StorageFrontend{
|
|
ID: s.ID,
|
|
Storage: s.Name,
|
|
Name: s.Name,
|
|
Node: s.Node,
|
|
Instance: s.Instance,
|
|
Nodes: append([]string(nil), s.Nodes...),
|
|
NodeIDs: append([]string(nil), s.NodeIDs...),
|
|
NodeCount: s.NodeCount,
|
|
Type: s.Type,
|
|
Status: s.Status,
|
|
Pool: s.Pool,
|
|
Total: s.Total,
|
|
Used: s.Used,
|
|
Avail: s.Free,
|
|
Free: s.Free,
|
|
Usage: s.Usage,
|
|
Content: s.Content,
|
|
Shared: s.Shared,
|
|
Enabled: s.Enabled,
|
|
Active: s.Active,
|
|
ZFSPool: s.ZFSPool,
|
|
}.NormalizeCollections()
|
|
}
|
|
|
|
// ToFrontend converts a replication job to a frontend representation.
|
|
func (r ReplicationJob) ToFrontend() ReplicationJobFrontend {
|
|
frontend := ReplicationJobFrontend{
|
|
ID: r.ID,
|
|
Instance: r.Instance,
|
|
JobID: r.JobID,
|
|
JobNumber: r.JobNumber,
|
|
Guest: r.Guest,
|
|
GuestID: r.GuestID,
|
|
GuestName: r.GuestName,
|
|
GuestType: r.GuestType,
|
|
GuestNode: r.GuestNode,
|
|
SourceNode: r.SourceNode,
|
|
SourceStorage: r.SourceStorage,
|
|
TargetNode: r.TargetNode,
|
|
TargetStorage: r.TargetStorage,
|
|
Schedule: r.Schedule,
|
|
Type: r.Type,
|
|
Enabled: r.Enabled,
|
|
State: r.State,
|
|
Status: r.Status,
|
|
LastSyncStatus: r.LastSyncStatus,
|
|
LastSyncUnix: r.LastSyncUnix,
|
|
LastSyncDurationSeconds: r.LastSyncDurationSeconds,
|
|
LastSyncDurationHuman: r.LastSyncDurationHuman,
|
|
NextSyncUnix: r.NextSyncUnix,
|
|
DurationSeconds: r.DurationSeconds,
|
|
DurationHuman: r.DurationHuman,
|
|
FailCount: r.FailCount,
|
|
Error: r.Error,
|
|
Comment: r.Comment,
|
|
RemoveJob: r.RemoveJob,
|
|
RateLimitMbps: r.RateLimitMbps,
|
|
}
|
|
|
|
if r.LastSyncTime != nil {
|
|
frontend.LastSyncTime = r.LastSyncTime.UnixMilli()
|
|
}
|
|
|
|
if r.NextSyncTime != nil {
|
|
frontend.NextSyncTime = r.NextSyncTime.UnixMilli()
|
|
}
|
|
|
|
polledAt := r.LastPolled
|
|
if polledAt.IsZero() {
|
|
polledAt = time.Now()
|
|
}
|
|
frontend.PolledAt = polledAt.UnixMilli()
|
|
|
|
return frontend
|
|
}
|
|
|
|
// zeroIfNegative returns 0 for negative values (used for I/O metrics)
|
|
func zeroIfNegative(val int64) int64 {
|
|
if val < 0 {
|
|
return 0
|
|
}
|
|
return val
|
|
}
|
|
|
|
// ResourceToFrontend converts a resources.Resource to ResourceFrontend.
|
|
// This function is in models package to avoid circular imports.
|
|
// It takes individual fields rather than the whole Resource to avoid importing resources package.
|
|
type ResourceConvertInput struct {
|
|
ID string
|
|
Type string
|
|
Technology string
|
|
Name string
|
|
DisplayName string
|
|
PlatformID string
|
|
PlatformType string
|
|
SourceType string
|
|
Sources []string
|
|
ParentID string
|
|
ParentName string
|
|
ChildCount int
|
|
ClusterID string
|
|
Status string
|
|
CPU *ResourceMetricInput
|
|
Memory *ResourceMetricInput
|
|
Disk *ResourceMetricInput
|
|
NetworkRX int64
|
|
NetworkTX int64
|
|
HasNetwork bool
|
|
DiskReadRate *int64
|
|
DiskWriteRate *int64
|
|
Temperature *float64
|
|
Uptime *int64
|
|
Tags []string
|
|
Labels map[string]string
|
|
CustomURL string
|
|
LastSeenUnix int64
|
|
Health json.RawMessage
|
|
IncidentCount int
|
|
IncidentCode string
|
|
IncidentSeverity string
|
|
IncidentSummary string
|
|
IncidentCategory string
|
|
IncidentLabel string
|
|
IncidentPriority int
|
|
IncidentImpactSummary string
|
|
IncidentUrgency string
|
|
IncidentAction string
|
|
Identity *ResourceIdentityInput
|
|
DiscoveryTarget json.RawMessage
|
|
MetricsTarget json.RawMessage
|
|
Canonical json.RawMessage
|
|
Policy json.RawMessage
|
|
AISafeSummary string
|
|
Capabilities json.RawMessage
|
|
CapabilitiesRef string
|
|
PolicyRef string
|
|
AISafeSummaryRef string
|
|
Relationships json.RawMessage
|
|
RecentChanges json.RawMessage
|
|
FacetCounts json.RawMessage
|
|
Incidents json.RawMessage
|
|
Proxmox json.RawMessage
|
|
Storage json.RawMessage
|
|
Agent json.RawMessage
|
|
Docker json.RawMessage
|
|
PBS json.RawMessage
|
|
PMG json.RawMessage
|
|
Kubernetes json.RawMessage
|
|
PhysicalDisk json.RawMessage
|
|
Ceph json.RawMessage
|
|
TrueNAS json.RawMessage
|
|
VMware json.RawMessage
|
|
Availability json.RawMessage
|
|
AvailabilityChecks json.RawMessage
|
|
PlatformData json.RawMessage
|
|
}
|
|
|
|
// ResourceMetricInput represents a metric value for resource conversion.
|
|
type ResourceMetricInput struct {
|
|
Current float64
|
|
Total *int64
|
|
Used *int64
|
|
Free *int64
|
|
Observation MemoryObservation
|
|
}
|
|
|
|
type ResourceIdentityInput struct {
|
|
Hostname string
|
|
MachineID string
|
|
IPs []string
|
|
}
|
|
|
|
// ConvertResourceToFrontend converts input to ResourceFrontend.
|
|
func ConvertResourceToFrontend(input ResourceConvertInput) ResourceFrontend {
|
|
rf := ResourceFrontend{
|
|
ID: input.ID,
|
|
Type: input.Type,
|
|
Technology: input.Technology,
|
|
Name: input.Name,
|
|
DisplayName: input.DisplayName,
|
|
PlatformID: input.PlatformID,
|
|
PlatformType: input.PlatformType,
|
|
SourceType: input.SourceType,
|
|
Sources: append([]string(nil), input.Sources...),
|
|
ParentID: input.ParentID,
|
|
ParentName: input.ParentName,
|
|
ChildCount: input.ChildCount,
|
|
ClusterID: input.ClusterID,
|
|
Status: input.Status,
|
|
Temperature: input.Temperature,
|
|
Uptime: input.Uptime,
|
|
Tags: append([]string(nil), input.Tags...),
|
|
Labels: nil,
|
|
CustomURL: input.CustomURL,
|
|
LastSeen: input.LastSeenUnix,
|
|
Health: input.Health,
|
|
IncidentCount: input.IncidentCount,
|
|
IncidentCode: input.IncidentCode,
|
|
IncidentSeverity: input.IncidentSeverity,
|
|
IncidentSummary: input.IncidentSummary,
|
|
IncidentCategory: input.IncidentCategory,
|
|
IncidentLabel: input.IncidentLabel,
|
|
IncidentPriority: input.IncidentPriority,
|
|
IncidentImpactSummary: input.IncidentImpactSummary,
|
|
IncidentUrgency: input.IncidentUrgency,
|
|
IncidentAction: input.IncidentAction,
|
|
DiscoveryTarget: input.DiscoveryTarget,
|
|
MetricsTarget: input.MetricsTarget,
|
|
Canonical: input.Canonical,
|
|
Policy: input.Policy,
|
|
AISafeSummary: input.AISafeSummary,
|
|
Capabilities: input.Capabilities,
|
|
CapabilitiesRef: input.CapabilitiesRef,
|
|
PolicyRef: input.PolicyRef,
|
|
AISafeSummaryRef: input.AISafeSummaryRef,
|
|
Relationships: input.Relationships,
|
|
RecentChanges: input.RecentChanges,
|
|
FacetCounts: input.FacetCounts,
|
|
Incidents: input.Incidents,
|
|
Proxmox: input.Proxmox,
|
|
Storage: input.Storage,
|
|
Agent: input.Agent,
|
|
Docker: input.Docker,
|
|
PBS: input.PBS,
|
|
PMG: input.PMG,
|
|
Kubernetes: input.Kubernetes,
|
|
PhysicalDisk: input.PhysicalDisk,
|
|
Ceph: input.Ceph,
|
|
TrueNAS: input.TrueNAS,
|
|
VMware: input.VMware,
|
|
Availability: input.Availability,
|
|
AvailabilityChecks: input.AvailabilityChecks,
|
|
PlatformData: input.PlatformData,
|
|
}
|
|
|
|
if len(input.Labels) > 0 {
|
|
rf.Labels = make(map[string]string, len(input.Labels))
|
|
for k, v := range input.Labels {
|
|
rf.Labels[k] = v
|
|
}
|
|
}
|
|
|
|
// Convert metrics
|
|
if input.CPU != nil {
|
|
rf.CPU = &ResourceMetricFrontend{
|
|
Current: input.CPU.Current,
|
|
Total: input.CPU.Total,
|
|
Used: input.CPU.Used,
|
|
Free: input.CPU.Free,
|
|
}
|
|
}
|
|
|
|
if input.Memory != nil {
|
|
rf.Memory = &ResourceMetricFrontend{
|
|
Current: input.Memory.Current,
|
|
Total: input.Memory.Total,
|
|
Used: input.Memory.Used,
|
|
Free: input.Memory.Free,
|
|
Observation: input.Memory.Observation,
|
|
}
|
|
}
|
|
|
|
if input.Disk != nil {
|
|
rf.Disk = &ResourceMetricFrontend{
|
|
Current: input.Disk.Current,
|
|
Total: input.Disk.Total,
|
|
Used: input.Disk.Used,
|
|
Free: input.Disk.Free,
|
|
}
|
|
}
|
|
|
|
if input.HasNetwork {
|
|
rf.Network = &ResourceNetworkFrontend{
|
|
RXBytes: input.NetworkRX,
|
|
TXBytes: input.NetworkTX,
|
|
}
|
|
}
|
|
|
|
if input.DiskReadRate != nil || input.DiskWriteRate != nil {
|
|
rf.DiskIO = &ResourceDiskIOFrontend{
|
|
ReadRate: input.DiskReadRate,
|
|
WriteRate: input.DiskWriteRate,
|
|
}
|
|
}
|
|
|
|
// Convert identity
|
|
if input.Identity != nil {
|
|
rf.Identity = &ResourceIdentityFrontend{
|
|
Hostname: input.Identity.Hostname,
|
|
MachineID: input.Identity.MachineID,
|
|
IPs: append([]string(nil), input.Identity.IPs...),
|
|
}
|
|
}
|
|
|
|
return rf.NormalizeCollections()
|
|
}
|