135 lines
4.2 KiB
Go
135 lines
4.2 KiB
Go
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"time"
|
||
)
|
||
|
||
func runHistoryCollector(collect func() (dashboardMetrics, error), store *Store) {
|
||
record := func() {
|
||
metrics, err := collect()
|
||
if err != nil {
|
||
log.Printf("Не удалось собрать исторические метрики: %v", err)
|
||
return
|
||
}
|
||
if err := store.AddHistory(historyPointFromMetrics(metrics)); err != nil {
|
||
log.Printf("Не удалось сохранить исторические метрики: %v", err)
|
||
}
|
||
store.AddEntityMetrics(metrics)
|
||
}
|
||
record()
|
||
ticker := time.NewTicker(time.Minute)
|
||
defer ticker.Stop()
|
||
for range ticker.C {
|
||
record()
|
||
}
|
||
}
|
||
|
||
func (s *Store) AddEntityMetrics(metrics dashboardMetrics) {
|
||
ts := time.Now().Unix()
|
||
for _, p := range metrics.ZFS.Pools {
|
||
v := p.CapacityPercent
|
||
s.AddEntityHistory("zfs", p.Name, ts, &v, nil)
|
||
}
|
||
for _, g := range metrics.Guests.Guests {
|
||
if g.Template != 0 {
|
||
continue
|
||
}
|
||
cpu := g.CPU * 100
|
||
var mem float64
|
||
if g.MaxMemory > 0 {
|
||
mem = float64(g.MemoryBytes) / float64(g.MaxMemory) * 100
|
||
}
|
||
s.AddEntityHistory("guest", fmt.Sprintf("%d · %s", g.VMID, g.Name), ts, &cpu, &mem)
|
||
}
|
||
for _, d := range metrics.Disks {
|
||
iops := d.ReadIOPS + d.WriteIOPS
|
||
util := d.Utilization
|
||
s.AddEntityHistory("disk-io", d.Name+" · "+d.Model, ts, &iops, &util)
|
||
latency := d.LatencyMs
|
||
s.AddEntityHistory("disk-latency", d.Name+" · "+d.Model, ts, &latency, nil)
|
||
if d.Temperature == nil {
|
||
continue
|
||
}
|
||
var wear *float64
|
||
if d.WearUsedPercent != nil {
|
||
wear = d.WearUsedPercent
|
||
}
|
||
s.AddEntityHistory("disk", d.Name+" · "+d.Model, ts, d.Temperature, wear)
|
||
}
|
||
for _, task := range metrics.Activity.Tasks {
|
||
if task.Type == "vzdump" && task.Duration > 0 && task.EndTime >= time.Now().Add(-15*time.Minute).Unix() {
|
||
duration := float64(task.Duration)
|
||
s.AddEntityHistory("backup-duration", "VMID "+task.ID, ts, &duration, nil)
|
||
}
|
||
}
|
||
for _, agent := range metrics.Agents {
|
||
if !agent.Online {
|
||
continue
|
||
}
|
||
name := agent.ID + " · " + agent.Name
|
||
cpu := agent.Report.CPUPercent
|
||
memory := float64(0)
|
||
if agent.Report.MemoryTotal > 0 {
|
||
memory = float64(agent.Report.MemoryUsed) / float64(agent.Report.MemoryTotal) * 100
|
||
}
|
||
swap := float64(0)
|
||
if agent.Report.SwapTotal > 0 {
|
||
swap = float64(agent.Report.SwapUsed) / float64(agent.Report.SwapTotal) * 100
|
||
}
|
||
s.AddEntityHistory("agent-resource", name, ts, &cpu, &memory)
|
||
s.AddEntityHistory("agent-swap", name, ts, &swap, nil)
|
||
pressure := agent.Report.Pressure.IO.Avg10
|
||
s.AddEntityHistory("agent-pressure", name, ts, &pressure, nil)
|
||
for _, filesystem := range agent.Report.Filesystems {
|
||
used := filesystem.UsedPercent
|
||
inodes := filesystem.InodesPercent
|
||
s.AddEntityHistory("agent-filesystem", name+" · "+filesystem.Mountpoint, ts, &used, &inodes)
|
||
}
|
||
}
|
||
s.PruneEntityHistory()
|
||
}
|
||
|
||
func historyPointFromMetrics(metrics dashboardMetrics) HistoryPoint {
|
||
point := HistoryPoint{
|
||
Timestamp: time.Now().Unix(), CPUUsage: metrics.CPU.UsagePercent,
|
||
MemoryUsage: metrics.Memory.UsagePercent, SwapUsage: metrics.Memory.SwapPercent,
|
||
RootUsage: metrics.Storage.UsagePercent, NetworkReceive: metrics.Network.ReceivePerSec,
|
||
NetworkTransmit: metrics.Network.TransmitPerSec, UPSCharge: metrics.UPS.ChargePercent,
|
||
UPSLoad: metrics.UPS.LoadPercent,
|
||
}
|
||
if len(metrics.CPU.Temperatures) > 0 {
|
||
for _, temperature := range metrics.CPU.Temperatures {
|
||
point.CPUTemperature += temperature.Celsius
|
||
}
|
||
point.CPUTemperature /= float64(len(metrics.CPU.Temperatures))
|
||
}
|
||
var zfsSize, zfsUsed uint64
|
||
for _, pool := range metrics.ZFS.Pools {
|
||
zfsSize += pool.SizeBytes
|
||
zfsUsed += pool.AllocatedBytes
|
||
}
|
||
if zfsSize > 0 {
|
||
point.ZFSUsage = float64(zfsUsed) / float64(zfsSize) * 100
|
||
}
|
||
var guestCores, guestUsedCPU float64
|
||
var guestMemory, guestMaxMemory uint64
|
||
for _, guest := range metrics.Guests.Guests {
|
||
if guest.Template != 0 || guest.Status != "running" {
|
||
continue
|
||
}
|
||
guestCores += guest.MaxCPU
|
||
guestUsedCPU += guest.CPU * guest.MaxCPU
|
||
guestMemory += guest.MemoryBytes
|
||
guestMaxMemory += guest.MaxMemory
|
||
}
|
||
if guestCores > 0 {
|
||
point.GuestsCPU = guestUsedCPU / guestCores * 100
|
||
}
|
||
if guestMaxMemory > 0 {
|
||
point.GuestsMemory = float64(guestMemory) / float64(guestMaxMemory) * 100
|
||
}
|
||
return point
|
||
}
|