mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 04:33:17 +00:00
fb7dd955ed
Normalization keeps a disk's last-known temperature when the current observation did not collect one (a disk in standby, a host agent past its reporting lease) and marks it non-available. Several readers still took PhysicalDisk.Temperature at face value. The AI chat context listed a standby disk as needing attention for heat. Patrol raised health issues and triage flags from the retained value. The AI query and disk-health tools handed it to the model as the disk's temperature. Performance reports and the reporting runtime snapshot tabulated it as measured, and the disk temperature charts padded a series out to now with it. Each of them now goes through diskinventory.TemperatureCollected. AI surfaces keep a retained value as context, labelled last known with its reason. Reports and the charts leave it out. monitor.go's private copy of the predicate is replaced by the shared helper.
202 lines
6.6 KiB
Go
202 lines
6.6 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/extensions"
|
|
)
|
|
|
|
func (h *ReportingHandlers) getRuntimeStateSnapshot(ctx context.Context, orgID string) (extensions.ReportingStateSnapshot, bool) {
|
|
sourceSnapshot, ok := h.getReportingEnrichmentSnapshot(ctx, orgID)
|
|
if !ok {
|
|
return extensions.EmptyReportingStateSnapshot(), false
|
|
}
|
|
runtimeSnapshot := extensions.EmptyReportingStateSnapshot()
|
|
runtimeSnapshot.Nodes = make([]extensions.ReportingNodeSnapshot, 0, len(sourceSnapshot.Nodes))
|
|
runtimeSnapshot.VMs = make([]extensions.ReportingVMSnapshot, 0, len(sourceSnapshot.VMs))
|
|
runtimeSnapshot.Containers = make([]extensions.ReportingContainerSnapshot, 0, len(sourceSnapshot.Containers))
|
|
runtimeSnapshot.ActiveAlerts = make([]extensions.ReportingAlertSnapshot, 0, len(sourceSnapshot.ActiveAlerts))
|
|
runtimeSnapshot.ResolvedAlerts = make([]extensions.ReportingAlertSnapshot, 0, len(sourceSnapshot.RecentlyResolved))
|
|
runtimeSnapshot.Storage = make([]extensions.ReportingStorageSnapshot, 0)
|
|
runtimeSnapshot.Disks = make([]extensions.ReportingDiskSnapshot, 0)
|
|
runtimeSnapshot.LegacyBackups = make([]extensions.ReportingLegacyBackupSnapshot, 0, len(sourceSnapshot.LegacyBackups.StorageBackups))
|
|
|
|
for _, node := range sourceSnapshot.Nodes {
|
|
var temperature *float64
|
|
if node.Temperature != nil && node.Temperature.CPUPackage > 0 {
|
|
value := node.Temperature.CPUPackage
|
|
temperature = &value
|
|
}
|
|
|
|
runtimeSnapshot.Nodes = append(runtimeSnapshot.Nodes, extensions.ReportingNodeSnapshot{
|
|
ID: node.ID,
|
|
Name: node.Name,
|
|
DisplayName: node.DisplayName,
|
|
Status: node.Status,
|
|
Host: node.Host,
|
|
Instance: node.Instance,
|
|
Uptime: node.Uptime,
|
|
KernelVersion: node.KernelVersion,
|
|
PVEVersion: node.PVEVersion,
|
|
CPUModel: node.CPUInfo.Model,
|
|
CPUCores: node.CPUInfo.Cores,
|
|
CPUSockets: node.CPUInfo.Sockets,
|
|
MemoryTotal: node.Memory.Total,
|
|
DiskTotal: node.Disk.Total,
|
|
LoadAverage: append([]float64(nil), node.LoadAverage...),
|
|
ClusterName: node.ClusterName,
|
|
IsCluster: node.IsClusterMember,
|
|
Temperature: temperature,
|
|
})
|
|
}
|
|
|
|
for _, vm := range sourceSnapshot.VMs {
|
|
runtimeSnapshot.VMs = append(runtimeSnapshot.VMs, extensions.ReportingVMSnapshot{
|
|
ID: vm.ID,
|
|
VMID: vm.VMID,
|
|
Name: vm.Name,
|
|
Status: vm.Status,
|
|
Node: vm.Node,
|
|
Instance: vm.Instance,
|
|
Uptime: vm.Uptime,
|
|
OSName: vm.OSName,
|
|
OSVersion: vm.OSVersion,
|
|
IPAddresses: append([]string(nil), vm.IPAddresses...),
|
|
CPUCores: vm.CPUs,
|
|
MemoryTotal: vm.Memory.Total,
|
|
DiskTotal: vm.Disk.Total,
|
|
Tags: append([]string(nil), vm.Tags...),
|
|
})
|
|
}
|
|
|
|
for _, ct := range sourceSnapshot.Containers {
|
|
runtimeSnapshot.Containers = append(runtimeSnapshot.Containers, extensions.ReportingContainerSnapshot{
|
|
ID: ct.ID,
|
|
VMID: ct.VMID,
|
|
Name: ct.Name,
|
|
Status: ct.Status,
|
|
Node: ct.Node,
|
|
Instance: ct.Instance,
|
|
Uptime: ct.Uptime,
|
|
OSName: ct.OSName,
|
|
IPAddresses: append([]string(nil), ct.IPAddresses...),
|
|
CPUCores: ct.CPUs,
|
|
MemoryTotal: ct.Memory.Total,
|
|
DiskTotal: ct.Disk.Total,
|
|
Tags: append([]string(nil), ct.Tags...),
|
|
})
|
|
}
|
|
|
|
for _, alert := range sourceSnapshot.ActiveAlerts {
|
|
runtimeSnapshot.ActiveAlerts = append(runtimeSnapshot.ActiveAlerts, extensions.ReportingAlertSnapshot{
|
|
ResourceID: alert.ResourceID,
|
|
Node: alert.Node,
|
|
Type: alert.Type,
|
|
Level: alert.Level,
|
|
Message: alert.Message,
|
|
Value: alert.Value,
|
|
Threshold: alert.Threshold,
|
|
StartTime: alert.StartTime,
|
|
})
|
|
}
|
|
|
|
for _, resolved := range sourceSnapshot.RecentlyResolved {
|
|
resolvedTime := resolved.ResolvedTime
|
|
runtimeSnapshot.ResolvedAlerts = append(runtimeSnapshot.ResolvedAlerts, extensions.ReportingAlertSnapshot{
|
|
ResourceID: resolved.ResourceID,
|
|
Node: resolved.Node,
|
|
Type: resolved.Type,
|
|
Level: resolved.Level,
|
|
Message: resolved.Message,
|
|
Value: resolved.Value,
|
|
Threshold: resolved.Threshold,
|
|
StartTime: resolved.StartTime,
|
|
ResolvedTime: &resolvedTime,
|
|
})
|
|
}
|
|
|
|
for _, backup := range sourceSnapshot.LegacyBackups.StorageBackups {
|
|
runtimeSnapshot.LegacyBackups = append(runtimeSnapshot.LegacyBackups, extensions.ReportingLegacyBackupSnapshot{
|
|
VMID: backup.VMID,
|
|
Node: backup.Node,
|
|
Storage: backup.Storage,
|
|
Timestamp: backup.Time,
|
|
Size: backup.Size,
|
|
Protected: backup.Protected,
|
|
VolID: backup.Volid,
|
|
})
|
|
}
|
|
|
|
for _, resource := range sourceSnapshot.Resources {
|
|
switch resource.Type {
|
|
case unifiedresources.ResourceTypeStorage:
|
|
storageNode := resource.ParentName
|
|
if storageNode == "" && len(resource.Identity.Hostnames) > 0 {
|
|
storageNode = resource.Identity.Hostnames[0]
|
|
}
|
|
|
|
var total, used, available int64
|
|
var usagePerc float64
|
|
if resource.Metrics != nil && resource.Metrics.Disk != nil {
|
|
if resource.Metrics.Disk.Total != nil {
|
|
total = *resource.Metrics.Disk.Total
|
|
}
|
|
if resource.Metrics.Disk.Used != nil {
|
|
used = *resource.Metrics.Disk.Used
|
|
}
|
|
if total > 0 {
|
|
available = total - used
|
|
}
|
|
usagePerc = resource.Metrics.Disk.Percent
|
|
if usagePerc == 0 && total > 0 {
|
|
usagePerc = (float64(used) / float64(total)) * 100
|
|
}
|
|
}
|
|
|
|
var storageType, content string
|
|
if resource.Storage != nil {
|
|
storageType = resource.Storage.Type
|
|
content = resource.Storage.Content
|
|
}
|
|
|
|
runtimeSnapshot.Storage = append(runtimeSnapshot.Storage, extensions.ReportingStorageSnapshot{
|
|
Name: resource.Name,
|
|
Node: storageNode,
|
|
Type: storageType,
|
|
Status: string(resource.Status),
|
|
Total: total,
|
|
Used: used,
|
|
Available: available,
|
|
UsagePerc: usagePerc,
|
|
Content: content,
|
|
})
|
|
|
|
case unifiedresources.ResourceTypePhysicalDisk:
|
|
if resource.PhysicalDisk == nil {
|
|
continue
|
|
}
|
|
|
|
diskNode := resource.ParentName
|
|
if diskNode == "" && len(resource.Identity.Hostnames) > 0 {
|
|
diskNode = resource.Identity.Hostnames[0]
|
|
}
|
|
|
|
disk := resource.PhysicalDisk
|
|
runtimeSnapshot.Disks = append(runtimeSnapshot.Disks, extensions.ReportingDiskSnapshot{
|
|
Node: diskNode,
|
|
Device: disk.DevPath,
|
|
Model: disk.Model,
|
|
Serial: disk.Serial,
|
|
Type: disk.DiskType,
|
|
Size: disk.SizeBytes,
|
|
Health: disk.Health,
|
|
Temperature: reportDiskTemperature(*disk),
|
|
WearLevel: disk.Wearout,
|
|
})
|
|
}
|
|
}
|
|
|
|
return runtimeSnapshot, true
|
|
}
|