mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 17:19:57 +00:00
715 lines
21 KiB
Go
715 lines
21 KiB
Go
package unifiedresources
|
|
|
|
import (
|
|
"hash/fnv"
|
|
"math"
|
|
"math/rand"
|
|
"strings"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/mockmode"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
)
|
|
|
|
func metricsFromProxmoxNode(node models.Node) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
cpuPercent := percentFromUsage(node.CPU)
|
|
metrics.CPU = &MetricValue{Value: cpuPercent, Percent: cpuPercent, Unit: "percent", Source: SourceProxmox}
|
|
|
|
if node.Memory.Total > 0 {
|
|
percent := percentFromUsage(node.Memory.Usage)
|
|
metrics.Memory = &MetricValue{Used: &node.Memory.Used, Total: &node.Memory.Total, Percent: percent, Unit: "bytes", Source: SourceProxmox}
|
|
}
|
|
if node.Disk.Total > 0 {
|
|
percent := percentFromUsage(node.Disk.Usage)
|
|
metrics.Disk = &MetricValue{Used: &node.Disk.Used, Total: &node.Disk.Total, Percent: percent, Unit: "bytes", Source: SourceProxmox}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromHost(host models.Host) *ResourceMetrics {
|
|
return buildHostMetricPayload(
|
|
host.CPUUsage,
|
|
host.Memory,
|
|
host.Disks,
|
|
host.NetInRate,
|
|
host.NetOutRate,
|
|
host.DiskReadRate,
|
|
host.DiskWriteRate,
|
|
SourceAgent,
|
|
)
|
|
}
|
|
|
|
func metricsFromUnraidStorage(host models.Host) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
total, used, _, percent := unraidStorageCapacity(host)
|
|
if total > 0 {
|
|
metrics.Disk = &MetricValue{
|
|
Used: &used,
|
|
Total: &total,
|
|
Percent: percent,
|
|
Unit: "bytes",
|
|
Source: SourceAgent,
|
|
}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromDockerHost(host models.DockerHost) *ResourceMetrics {
|
|
return buildHostMetricPayload(
|
|
host.CPUUsage,
|
|
host.Memory,
|
|
host.Disks,
|
|
host.NetInRate,
|
|
host.NetOutRate,
|
|
host.DiskReadRate,
|
|
host.DiskWriteRate,
|
|
SourceDocker,
|
|
)
|
|
}
|
|
|
|
func buildHostMetricPayload(
|
|
cpuUsage float64,
|
|
memory models.Memory,
|
|
disks []models.Disk,
|
|
netInRate, netOutRate, diskReadRate, diskWriteRate float64,
|
|
source DataSource,
|
|
) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
cpuPercent := percentFromUsage(cpuUsage)
|
|
metrics.CPU = &MetricValue{Value: cpuPercent, Percent: cpuPercent, Unit: "percent", Source: source}
|
|
if memory.Total > 0 {
|
|
percent := percentFromUsage(memory.Usage)
|
|
metrics.Memory = &MetricValue{Used: &memory.Used, Total: &memory.Total, Percent: percent, Unit: "bytes", Source: source}
|
|
}
|
|
if len(disks) > 0 {
|
|
disk := disks[0]
|
|
if disk.Total > 0 {
|
|
percent := percentFromUsage(disk.Usage)
|
|
metrics.Disk = &MetricValue{Used: &disk.Used, Total: &disk.Total, Percent: percent, Unit: "bytes", Source: source}
|
|
}
|
|
}
|
|
setNetworkAndDiskIOMetricsHost(metrics, netInRate, netOutRate, diskReadRate, diskWriteRate, source)
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromPBSInstance(instance models.PBSInstance) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
cpuPercent := percentFromUsage(instance.CPU)
|
|
metrics.CPU = &MetricValue{Value: cpuPercent, Percent: cpuPercent, Unit: "percent", Source: SourcePBS}
|
|
if instance.MemoryTotal > 0 {
|
|
percent := percentFromUsage(instance.Memory)
|
|
metrics.Memory = &MetricValue{
|
|
Used: &instance.MemoryUsed,
|
|
Total: &instance.MemoryTotal,
|
|
Percent: percent,
|
|
Unit: "bytes",
|
|
Source: SourcePBS,
|
|
}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromPBSDatastore(datastore models.PBSDatastore) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
if datastore.Total <= 0 {
|
|
return metrics
|
|
}
|
|
|
|
used := datastore.Used
|
|
total := datastore.Total
|
|
percent := percentFromUsage(datastore.Usage)
|
|
if percent == 0 && total > 0 {
|
|
percent = clampMetricValue((float64(used)/float64(total))*100, 0, 100)
|
|
}
|
|
metrics.Disk = &MetricValue{
|
|
Used: &used,
|
|
Total: &total,
|
|
Percent: percent,
|
|
Unit: "bytes",
|
|
Source: SourcePBS,
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromPMGInstance(instance models.PMGInstance) *ResourceMetrics {
|
|
// PMG currently exposes aggregate queue/mail counters, not point-in-time
|
|
// throughput rates. Return an empty metrics object to avoid mislabeling
|
|
// aggregate totals as realtime I/O.
|
|
return &ResourceMetrics{}
|
|
}
|
|
|
|
func metricsFromVM(vm models.VM) *ResourceMetrics {
|
|
return buildVMMetricPayload(
|
|
vm.CPU,
|
|
vm.Memory,
|
|
vm.Disk,
|
|
vm.NetworkIn,
|
|
vm.NetworkOut,
|
|
vm.DiskRead,
|
|
vm.DiskWrite,
|
|
SourceProxmox,
|
|
)
|
|
}
|
|
|
|
func metricsFromContainer(ct models.Container) *ResourceMetrics {
|
|
return buildVMMetricPayload(
|
|
ct.CPU,
|
|
ct.Memory,
|
|
ct.Disk,
|
|
ct.NetworkIn,
|
|
ct.NetworkOut,
|
|
ct.DiskRead,
|
|
ct.DiskWrite,
|
|
SourceProxmox,
|
|
)
|
|
}
|
|
|
|
func buildVMMetricPayload(
|
|
cpu float64,
|
|
memory models.Memory,
|
|
disk models.Disk,
|
|
netIn, netOut, diskRead, diskWrite int64,
|
|
source DataSource,
|
|
) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
cpuPercent := percentFromUsage(cpu)
|
|
metrics.CPU = &MetricValue{Value: cpuPercent, Percent: cpuPercent, Unit: "percent", Source: source}
|
|
if memory.Total > 0 {
|
|
percent := percentFromUsage(memory.Usage)
|
|
metrics.Memory = &MetricValue{Used: &memory.Used, Total: &memory.Total, Percent: percent, Unit: "bytes", Source: source}
|
|
}
|
|
if disk.Total > 0 {
|
|
percent := percentFromUsage(disk.Usage)
|
|
metrics.Disk = &MetricValue{Used: &disk.Used, Total: &disk.Total, Percent: percent, Unit: "bytes", Source: source}
|
|
}
|
|
setNetworkAndDiskIOMetricsVM(metrics, netIn, netOut, diskRead, diskWrite, source)
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromStorage(storage models.Storage) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
if storage.Total <= 0 {
|
|
return metrics
|
|
}
|
|
|
|
used := storage.Used
|
|
total := storage.Total
|
|
percent := percentFromUsage(storage.Usage)
|
|
if percent == 0 && total > 0 {
|
|
percent = clampMetricValue((float64(used)/float64(total))*100, 0, 100)
|
|
}
|
|
metrics.Disk = &MetricValue{
|
|
Used: &used,
|
|
Total: &total,
|
|
Percent: percent,
|
|
Unit: "bytes",
|
|
Source: SourceProxmox,
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromCephCluster(cluster models.CephCluster) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
if cluster.TotalBytes > 0 {
|
|
used := cluster.UsedBytes
|
|
total := cluster.TotalBytes
|
|
percent := clampMetricValue(cluster.UsagePercent, 0, 100)
|
|
if percent == 0 && total > 0 {
|
|
percent = clampMetricValue((float64(used)/float64(total))*100, 0, 100)
|
|
}
|
|
metrics.Disk = &MetricValue{
|
|
Used: &used,
|
|
Total: &total,
|
|
Percent: percent,
|
|
Unit: "bytes",
|
|
Source: SourceProxmox,
|
|
}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromPhysicalDisk(disk models.PhysicalDisk) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
if disk.Size > 0 {
|
|
used := int64(0)
|
|
total := disk.Size
|
|
metrics.Disk = &MetricValue{
|
|
Used: &used,
|
|
Total: &total,
|
|
Unit: "bytes",
|
|
Source: SourceProxmox,
|
|
}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromDockerContainer(ct models.DockerContainer) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
cpuPercent := percentFromUsage(ct.CPUPercent)
|
|
metrics.CPU = &MetricValue{Value: cpuPercent, Percent: cpuPercent, Unit: "percent", Source: SourceDocker}
|
|
if ct.MemoryLimit > 0 {
|
|
percent := percentFromUsage(ct.MemoryPercent)
|
|
metrics.Memory = &MetricValue{Used: &ct.MemoryUsage, Total: &ct.MemoryLimit, Percent: percent, Unit: "bytes", Source: SourceDocker}
|
|
}
|
|
if ct.RootFilesystemBytes > 0 {
|
|
used := ct.WritableLayerBytes
|
|
if used < 0 {
|
|
used = 0
|
|
}
|
|
if used > ct.RootFilesystemBytes {
|
|
used = ct.RootFilesystemBytes
|
|
}
|
|
percent := clampMetricValue((float64(used)/float64(ct.RootFilesystemBytes))*100, 0, 100)
|
|
metrics.Disk = &MetricValue{Used: &used, Total: &ct.RootFilesystemBytes, Percent: percent, Unit: "bytes", Source: SourceDocker}
|
|
}
|
|
if ct.NetInRate > 0 {
|
|
metrics.NetIn = &MetricValue{Value: ct.NetInRate, Unit: "bytes/s", Source: SourceDocker}
|
|
}
|
|
if ct.NetOutRate > 0 {
|
|
metrics.NetOut = &MetricValue{Value: ct.NetOutRate, Unit: "bytes/s", Source: SourceDocker}
|
|
}
|
|
if ct.BlockIO != nil {
|
|
if ct.BlockIO.ReadRateBytesPerSecond != nil && *ct.BlockIO.ReadRateBytesPerSecond > 0 {
|
|
metrics.DiskRead = &MetricValue{Value: *ct.BlockIO.ReadRateBytesPerSecond, Unit: "bytes/s", Source: SourceDocker}
|
|
}
|
|
if ct.BlockIO.WriteRateBytesPerSecond != nil && *ct.BlockIO.WriteRateBytesPerSecond > 0 {
|
|
metrics.DiskWrite = &MetricValue{Value: *ct.BlockIO.WriteRateBytesPerSecond, Unit: "bytes/s", Source: SourceDocker}
|
|
}
|
|
}
|
|
if mockmode.IsEnabled() && (metrics.NetIn == nil || metrics.NetOut == nil || metrics.DiskRead == nil || metrics.DiskWrite == nil) {
|
|
synthetic := syntheticDockerContainerIOMetrics(ct)
|
|
if metrics.NetIn == nil {
|
|
metrics.NetIn = &MetricValue{Value: synthetic.NetIn, Unit: "bytes/s", Source: SourceDocker}
|
|
}
|
|
if metrics.NetOut == nil {
|
|
metrics.NetOut = &MetricValue{Value: synthetic.NetOut, Unit: "bytes/s", Source: SourceDocker}
|
|
}
|
|
if metrics.DiskRead == nil {
|
|
metrics.DiskRead = &MetricValue{Value: synthetic.DiskRead, Unit: "bytes/s", Source: SourceDocker}
|
|
}
|
|
if metrics.DiskWrite == nil {
|
|
metrics.DiskWrite = &MetricValue{Value: synthetic.DiskWrite, Unit: "bytes/s", Source: SourceDocker}
|
|
}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromKubernetesCluster(cluster models.KubernetesCluster, linkedHosts []*models.Host) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
|
|
var cpuSum float64
|
|
var cpuCount int
|
|
|
|
var memoryUsed int64
|
|
var memoryTotal int64
|
|
|
|
var diskUsed int64
|
|
var diskTotal int64
|
|
|
|
var netIn float64
|
|
var netOut float64
|
|
var diskRead float64
|
|
var diskWrite float64
|
|
var hasNetIn bool
|
|
var hasNetOut bool
|
|
var hasDiskRead bool
|
|
var hasDiskWrite bool
|
|
|
|
linkedNodeNames := make(map[string]struct{}, len(linkedHosts)*2)
|
|
for _, host := range linkedHosts {
|
|
if host == nil {
|
|
continue
|
|
}
|
|
|
|
cpuSum += percentFromUsage(host.CPUUsage)
|
|
cpuCount++
|
|
|
|
hostName := strings.TrimSpace(host.Hostname)
|
|
if hostName != "" {
|
|
linkedNodeNames[strings.ToLower(hostName)] = struct{}{}
|
|
linkedNodeNames[strings.ToLower(NormalizeHostname(hostName))] = struct{}{}
|
|
}
|
|
|
|
if host.Memory.Total > 0 {
|
|
memoryTotal += host.Memory.Total
|
|
memoryUsed += host.Memory.Used
|
|
}
|
|
|
|
if len(host.Disks) > 0 {
|
|
disk := host.Disks[0]
|
|
if disk.Total > 0 {
|
|
diskTotal += disk.Total
|
|
diskUsed += disk.Used
|
|
}
|
|
}
|
|
|
|
if host.NetInRate > 0 {
|
|
netIn += host.NetInRate
|
|
hasNetIn = true
|
|
}
|
|
if host.NetOutRate > 0 {
|
|
netOut += host.NetOutRate
|
|
hasNetOut = true
|
|
}
|
|
if host.DiskReadRate > 0 {
|
|
diskRead += host.DiskReadRate
|
|
hasDiskRead = true
|
|
}
|
|
if host.DiskWriteRate > 0 {
|
|
diskWrite += host.DiskWriteRate
|
|
hasDiskWrite = true
|
|
}
|
|
}
|
|
|
|
// Fall back to Kubernetes metrics API usage for nodes without a linked agent host.
|
|
for _, node := range cluster.Nodes {
|
|
nodeName := strings.TrimSpace(node.Name)
|
|
normalized := strings.ToLower(NormalizeHostname(nodeName))
|
|
if _, linked := linkedNodeNames[strings.ToLower(nodeName)]; linked {
|
|
continue
|
|
}
|
|
if normalized != "" {
|
|
if _, linked := linkedNodeNames[normalized]; linked {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if node.UsageCPUPercent > 0 {
|
|
cpuSum += clampMetricValue(node.UsageCPUPercent, 0, 100)
|
|
cpuCount++
|
|
}
|
|
if node.UsageMemoryBytes > 0 {
|
|
total := node.AllocMemoryBytes
|
|
if total <= 0 {
|
|
total = node.CapacityMemoryBytes
|
|
}
|
|
if total > 0 {
|
|
memoryUsed += node.UsageMemoryBytes
|
|
memoryTotal += total
|
|
}
|
|
}
|
|
}
|
|
|
|
if cpuCount > 0 {
|
|
cpuPercent := clampMetricValue(cpuSum/float64(cpuCount), 0, 100)
|
|
metrics.CPU = &MetricValue{Value: cpuPercent, Percent: cpuPercent, Unit: "percent", Source: SourceAgent}
|
|
}
|
|
if memoryTotal > 0 {
|
|
percent := clampMetricValue((float64(memoryUsed)/float64(memoryTotal))*100, 0, 100)
|
|
metrics.Memory = &MetricValue{Used: &memoryUsed, Total: &memoryTotal, Percent: percent, Unit: "bytes", Source: SourceAgent}
|
|
}
|
|
if diskTotal > 0 {
|
|
percent := clampMetricValue((float64(diskUsed)/float64(diskTotal))*100, 0, 100)
|
|
metrics.Disk = &MetricValue{Used: &diskUsed, Total: &diskTotal, Percent: percent, Unit: "bytes", Source: SourceAgent}
|
|
}
|
|
if hasNetIn {
|
|
metrics.NetIn = &MetricValue{Value: netIn, Unit: "bytes/s", Source: SourceAgent}
|
|
}
|
|
if hasNetOut {
|
|
metrics.NetOut = &MetricValue{Value: netOut, Unit: "bytes/s", Source: SourceAgent}
|
|
}
|
|
if hasDiskRead {
|
|
metrics.DiskRead = &MetricValue{Value: diskRead, Unit: "bytes/s", Source: SourceAgent}
|
|
}
|
|
if hasDiskWrite {
|
|
metrics.DiskWrite = &MetricValue{Value: diskWrite, Unit: "bytes/s", Source: SourceAgent}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromKubernetesNode(_ models.KubernetesCluster, node models.KubernetesNode, linkedHost *models.Host) *ResourceMetrics {
|
|
// Kubernetes node usage comes from the pulse-agent host module when running on the
|
|
// same infrastructure node (unified agent). If no linked host exists, use
|
|
// Kubernetes metrics API usage fields when available.
|
|
if linkedHost != nil {
|
|
return metricsFromHost(*linkedHost)
|
|
}
|
|
|
|
metrics := &ResourceMetrics{}
|
|
if node.UsageCPUPercent > 0 {
|
|
cpuPercent := clampMetricValue(node.UsageCPUPercent, 0, 100)
|
|
metrics.CPU = &MetricValue{Value: cpuPercent, Percent: cpuPercent, Unit: "percent", Source: SourceK8s}
|
|
}
|
|
if node.UsageMemoryBytes > 0 {
|
|
total := node.AllocMemoryBytes
|
|
if total <= 0 {
|
|
total = node.CapacityMemoryBytes
|
|
}
|
|
if total > 0 {
|
|
used := node.UsageMemoryBytes
|
|
percent := clampMetricValue((float64(used)/float64(total))*100, 0, 100)
|
|
metrics.Memory = &MetricValue{Used: &used, Total: &total, Percent: percent, Unit: "bytes", Source: SourceK8s}
|
|
}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func metricsFromKubernetesPod(cluster models.KubernetesCluster, pod models.KubernetesPod) *ResourceMetrics {
|
|
metrics := &ResourceMetrics{}
|
|
if pod.UsageCPUPercent > 0 {
|
|
cpuPercent := clampMetricValue(pod.UsageCPUPercent, 0, 100)
|
|
metrics.CPU = &MetricValue{Value: cpuPercent, Percent: cpuPercent, Unit: "percent", Source: SourceK8s}
|
|
}
|
|
if pod.UsageMemoryBytes > 0 {
|
|
total := kubernetesPodMemoryTotalBytes(cluster, pod)
|
|
if total > 0 {
|
|
used := pod.UsageMemoryBytes
|
|
percent := clampMetricValue((float64(used)/float64(total))*100, 0, 100)
|
|
metrics.Memory = &MetricValue{Used: &used, Total: &total, Percent: percent, Unit: "bytes", Source: SourceK8s}
|
|
} else if pod.UsageMemoryPercent > 0 {
|
|
memPercent := clampMetricValue(pod.UsageMemoryPercent, 0, 100)
|
|
metrics.Memory = &MetricValue{Value: memPercent, Percent: memPercent, Unit: "percent", Source: SourceK8s}
|
|
}
|
|
}
|
|
if pod.DiskUsagePercent > 0 {
|
|
diskPercent := clampMetricValue(pod.DiskUsagePercent, 0, 100)
|
|
metrics.Disk = &MetricValue{Value: diskPercent, Percent: diskPercent, Unit: "percent", Source: SourceK8s}
|
|
}
|
|
if pod.NetInRate > 0 {
|
|
metrics.NetIn = &MetricValue{Value: pod.NetInRate, Unit: "bytes/s", Source: SourceK8s}
|
|
}
|
|
if pod.NetOutRate > 0 {
|
|
metrics.NetOut = &MetricValue{Value: pod.NetOutRate, Unit: "bytes/s", Source: SourceK8s}
|
|
}
|
|
|
|
if !mockmode.IsEnabled() {
|
|
return metrics
|
|
}
|
|
|
|
values := syntheticKubernetesPodMetrics(cluster, pod)
|
|
if metrics.CPU == nil {
|
|
metrics.CPU = &MetricValue{Value: values.CPU, Percent: values.CPU, Unit: "percent", Source: SourceK8s}
|
|
}
|
|
if metrics.Memory == nil {
|
|
metrics.Memory = &MetricValue{Value: values.Memory, Percent: values.Memory, Unit: "percent", Source: SourceK8s}
|
|
}
|
|
if metrics.Disk == nil {
|
|
metrics.Disk = &MetricValue{Value: values.Disk, Percent: values.Disk, Unit: "percent", Source: SourceK8s}
|
|
}
|
|
if metrics.NetIn == nil {
|
|
metrics.NetIn = &MetricValue{Value: values.NetIn, Unit: "bytes/s", Source: SourceK8s}
|
|
}
|
|
if metrics.NetOut == nil {
|
|
metrics.NetOut = &MetricValue{Value: values.NetOut, Unit: "bytes/s", Source: SourceK8s}
|
|
}
|
|
return metrics
|
|
}
|
|
|
|
func kubernetesPodMemoryTotalBytes(cluster models.KubernetesCluster, pod models.KubernetesPod) int64 {
|
|
nodeName := strings.TrimSpace(pod.NodeName)
|
|
if nodeName == "" {
|
|
return 0
|
|
}
|
|
for _, node := range cluster.Nodes {
|
|
if !strings.EqualFold(strings.TrimSpace(node.Name), nodeName) {
|
|
continue
|
|
}
|
|
if node.AllocMemoryBytes > 0 {
|
|
return node.AllocMemoryBytes
|
|
}
|
|
if node.CapacityMemoryBytes > 0 {
|
|
return node.CapacityMemoryBytes
|
|
}
|
|
return 0
|
|
}
|
|
return 0
|
|
}
|
|
|
|
type kubernetesPodSyntheticMetrics struct {
|
|
CPU float64
|
|
Memory float64
|
|
Disk float64
|
|
NetIn float64
|
|
NetOut float64
|
|
DiskRead float64
|
|
DiskWrite float64
|
|
}
|
|
|
|
type dockerContainerSyntheticIOMetrics struct {
|
|
NetIn float64
|
|
NetOut float64
|
|
DiskRead float64
|
|
DiskWrite float64
|
|
}
|
|
|
|
func syntheticDockerContainerIOMetrics(ct models.DockerContainer) dockerContainerSyntheticIOMetrics {
|
|
seed := hashMetricsSeed(ct.ID, ct.Name, ct.Image, ct.State)
|
|
rng := rand.New(rand.NewSource(int64(seed)))
|
|
|
|
state := strings.ToLower(strings.TrimSpace(ct.State))
|
|
running := state == "running" || state == "restarting"
|
|
activity := clampMetricValue((percentFromUsage(ct.CPUPercent)+percentFromUsage(ct.MemoryPercent))/180.0, 0.12, 1.25)
|
|
if !running {
|
|
activity = clampMetricValue(activity*0.32, 0.05, 0.45)
|
|
}
|
|
|
|
restartMultiplier := 1 + math.Min(float64(ct.RestartCount)*0.08, 0.55)
|
|
netIn := (28 + activity*540 + rng.Float64()*210) * restartMultiplier
|
|
netOut := (22 + activity*470 + rng.Float64()*180) * restartMultiplier
|
|
diskRead := (16 + activity*320 + rng.Float64()*140) * restartMultiplier
|
|
diskWrite := (14 + activity*300 + rng.Float64()*130) * restartMultiplier
|
|
|
|
return dockerContainerSyntheticIOMetrics{
|
|
NetIn: clampMetricValue(netIn, 4, 3200),
|
|
NetOut: clampMetricValue(netOut, 3, 2800),
|
|
DiskRead: clampMetricValue(diskRead, 2, 2400),
|
|
DiskWrite: clampMetricValue(diskWrite, 2, 2200),
|
|
}
|
|
}
|
|
|
|
func syntheticKubernetesPodMetrics(cluster models.KubernetesCluster, pod models.KubernetesPod) kubernetesPodSyntheticMetrics {
|
|
seed := hashMetricsSeed(
|
|
cluster.ID,
|
|
cluster.Name,
|
|
cluster.DisplayName,
|
|
pod.UID,
|
|
pod.Namespace,
|
|
pod.Name,
|
|
)
|
|
rng := rand.New(rand.NewSource(int64(seed)))
|
|
|
|
phase := strings.ToLower(strings.TrimSpace(pod.Phase))
|
|
totalContainers := len(pod.Containers)
|
|
if totalContainers <= 0 {
|
|
totalContainers = 1
|
|
}
|
|
readyContainers := 0
|
|
for _, container := range pod.Containers {
|
|
if container.Ready {
|
|
readyContainers++
|
|
}
|
|
}
|
|
readiness := float64(readyContainers) / float64(totalContainers)
|
|
if readiness <= 0 && phase == "running" {
|
|
readiness = 0.35
|
|
}
|
|
|
|
restarts := float64(pod.Restarts)
|
|
if restarts < 0 {
|
|
restarts = 0
|
|
}
|
|
restartFactor := math.Min(restarts*1.6, 16)
|
|
|
|
cpu := 0.0
|
|
memory := 0.0
|
|
disk := 0.0
|
|
netIn := 0.0
|
|
netOut := 0.0
|
|
diskRead := 0.0
|
|
diskWrite := 0.0
|
|
|
|
switch phase {
|
|
case "running":
|
|
cpu = 7 + readiness*52 + rng.Float64()*14 - restartFactor*0.35
|
|
memory = 26 + readiness*46 + rng.Float64()*14 + restartFactor*0.25
|
|
disk = 17 + readiness*42 + rng.Float64()*16
|
|
netIn = 14 + readiness*220 + rng.Float64()*70 + restarts*2
|
|
netOut = 10 + readiness*180 + rng.Float64()*55 + restarts*1.6
|
|
diskRead = 8 + readiness*75 + rng.Float64()*32 + restarts*1.8
|
|
diskWrite = 6 + readiness*68 + rng.Float64()*28 + restarts*1.6
|
|
case "pending":
|
|
cpu = 2 + rng.Float64()*7
|
|
memory = 14 + rng.Float64()*16
|
|
disk = 8 + rng.Float64()*14
|
|
netIn = 1 + rng.Float64()*10
|
|
netOut = 1 + rng.Float64()*8
|
|
diskRead = 1 + rng.Float64()*8
|
|
diskWrite = 1 + rng.Float64()*8
|
|
case "failed", "unknown":
|
|
cpu = 1 + rng.Float64()*8
|
|
memory = 9 + rng.Float64()*15 + restartFactor*0.5
|
|
disk = 7 + rng.Float64()*16
|
|
netIn = 1 + rng.Float64()*14 + restarts*1.4
|
|
netOut = 1 + rng.Float64()*11 + restarts*1.2
|
|
diskRead = 1 + rng.Float64()*10 + restarts*1.2
|
|
diskWrite = 1 + rng.Float64()*10 + restarts*1.1
|
|
default:
|
|
cpu = 1 + rng.Float64()*6
|
|
memory = 8 + rng.Float64()*12
|
|
disk = 6 + rng.Float64()*12
|
|
netIn = 1 + rng.Float64()*7
|
|
netOut = 1 + rng.Float64()*6
|
|
diskRead = 1 + rng.Float64()*6
|
|
diskWrite = 1 + rng.Float64()*6
|
|
}
|
|
|
|
if totalContainers > 1 {
|
|
multi := 1 + math.Min(float64(totalContainers-1)*0.08, 0.6)
|
|
cpu *= multi
|
|
memory *= 1 + math.Min(float64(totalContainers-1)*0.1, 0.8)
|
|
disk *= 1 + math.Min(float64(totalContainers-1)*0.07, 0.5)
|
|
netIn *= 1 + math.Min(float64(totalContainers-1)*0.09, 0.7)
|
|
netOut *= 1 + math.Min(float64(totalContainers-1)*0.08, 0.65)
|
|
diskRead *= 1 + math.Min(float64(totalContainers-1)*0.11, 0.8)
|
|
diskWrite *= 1 + math.Min(float64(totalContainers-1)*0.1, 0.7)
|
|
}
|
|
|
|
return kubernetesPodSyntheticMetrics{
|
|
CPU: clampMetricValue(cpu, 0, 100),
|
|
Memory: clampMetricValue(memory, 0, 100),
|
|
Disk: clampMetricValue(disk, 0, 100),
|
|
NetIn: clampMetricValue(netIn, 0, math.Max(1800, netIn+50)),
|
|
NetOut: clampMetricValue(netOut, 0, math.Max(1700, netOut+40)),
|
|
DiskRead: clampMetricValue(diskRead, 0, math.Max(1200, diskRead+30)),
|
|
DiskWrite: clampMetricValue(diskWrite, 0, math.Max(1200, diskWrite+30)),
|
|
}
|
|
}
|
|
|
|
func hashMetricsSeed(parts ...string) uint64 {
|
|
h := fnv.New64a()
|
|
for _, p := range parts {
|
|
_, _ = h.Write([]byte(p))
|
|
_, _ = h.Write([]byte{0})
|
|
}
|
|
return h.Sum64()
|
|
}
|
|
|
|
func clampMetricValue(value, min, max float64) float64 {
|
|
if math.IsNaN(value) || math.IsInf(value, 0) {
|
|
return min
|
|
}
|
|
if value < min {
|
|
return min
|
|
}
|
|
if value > max {
|
|
return max
|
|
}
|
|
return value
|
|
}
|
|
|
|
func percentFromUsage(value float64) float64 {
|
|
if value <= 1.0 {
|
|
return value * 100
|
|
}
|
|
return value
|
|
}
|
|
|
|
func setNetworkAndDiskIOMetricsHost(metrics *ResourceMetrics, netIn, netOut, diskRead, diskWrite float64, source DataSource) {
|
|
if netIn > 0 {
|
|
metrics.NetIn = &MetricValue{Value: netIn, Unit: "bytes/s", Source: source}
|
|
}
|
|
if netOut > 0 {
|
|
metrics.NetOut = &MetricValue{Value: netOut, Unit: "bytes/s", Source: source}
|
|
}
|
|
if diskRead > 0 {
|
|
metrics.DiskRead = &MetricValue{Value: diskRead, Unit: "bytes/s", Source: source}
|
|
}
|
|
if diskWrite > 0 {
|
|
metrics.DiskWrite = &MetricValue{Value: diskWrite, Unit: "bytes/s", Source: source}
|
|
}
|
|
}
|
|
|
|
func setNetworkAndDiskIOMetricsVM(metrics *ResourceMetrics, netIn, netOut, diskRead, diskWrite int64, source DataSource) {
|
|
if netIn != 0 {
|
|
metrics.NetIn = &MetricValue{Value: float64(netIn), Unit: "bytes/s", Source: source}
|
|
}
|
|
if netOut != 0 {
|
|
metrics.NetOut = &MetricValue{Value: float64(netOut), Unit: "bytes/s", Source: source}
|
|
}
|
|
if diskRead != 0 {
|
|
metrics.DiskRead = &MetricValue{Value: float64(diskRead), Unit: "bytes/s", Source: source}
|
|
}
|
|
if diskWrite != 0 {
|
|
metrics.DiskWrite = &MetricValue{Value: float64(diskWrite), Unit: "bytes/s", Source: source}
|
|
}
|
|
}
|