package metrics import ( "time" "github.com/prometheus/client_golang/prometheus" "lostak.dev/pve-exporter/proxmox" ) // PVE container collector. type PveContainerCollector struct { apiClient *proxmox.PveApiClient // PVE API client instance. registry *TTLRegistry // TTL metrics registry. state *TTLGaugeVec // Container state prometheus gauge. uptime *TTLGaugeVec // Container uptime prometheus gauge. cpu *TTLGaugeVec // Container count of CPUs prometheus gauge. cpuUsage *TTLGaugeVec // Container CPU usage % prometheus gauge. memBytes *TTLGaugeVec // Container memory in bytes prometheus gauge. memBytesUsed *TTLGaugeVec // Container memory usage in bytes prometheus gauge. netReceive *TTLCounterVec // Container received network traffic in bytes prometheus counter. netTransmit *TTLCounterVec // Container transmitted network traffic in bytes prometheus counter. diskWrite *TTLCounterVec // Container disk written in bytes prometheus counter. diskRead *TTLCounterVec // Container disk read in bytes prometheus counter. disk *TTLGaugeVec // Container disk space usage in bytes prometheus gauge. diskMax *TTLGaugeVec // Container disk size in bytes prometheus gauge. swap *TTLGaugeVec // Container swap usage in bytes prometheus gauge. } // Create new instance of PVE container collector. func NewPveContainerCollector(apiClient *proxmox.PveApiClient, registry *TTLRegistry) *PveContainerCollector { c := PveContainerCollector{apiClient: apiClient} c.registry = registry // Container state. c.state = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_state", Help: "Container state.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.state) // Container uptime. c.uptime = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_uptime_seconds", Help: "Container uptime in seconds.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.uptime) // Container CPU count. c.cpu = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_cpus", Help: "Container CPU count.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.cpu) // Container CPU usage. c.cpuUsage = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_cpu_usage_ratio", Help: "Container CPU usage ratio (0-1).", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.cpuUsage) // Container memory total. c.memBytes = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_memory_total_bytes", Help: "Container total memory in bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.memBytes) // Container memory usage. c.memBytesUsed = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_memory_used_bytes", Help: "Container used memory in bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.memBytesUsed) // Container network RX. c.netReceive = NewTTLCounterVec( prometheus.CounterOpts{ Name: "pve_ct_network_receive_bytes_total", Help: "Container received network traffic in bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.netReceive) // Container network TX. c.netTransmit = NewTTLCounterVec( prometheus.CounterOpts{ Name: "pve_ct_network_transmit_bytes_total", Help: "Container transmitted network traffic in bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.netTransmit) // Container disk written. c.diskWrite = NewTTLCounterVec( prometheus.CounterOpts{ Name: "pve_ct_disk_write_bytes_total", Help: "Container written bytes to disk.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.diskWrite) // Container disk read. c.diskRead = NewTTLCounterVec( prometheus.CounterOpts{ Name: "pve_ct_disk_read_bytes_total", Help: "Container read bytes from disk.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.diskRead) // Container disk size. c.disk = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_disk_used_bytes", Help: "Container used disk space in bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.disk) // Container disk size. c.diskMax = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_disk_size_bytes", Help: "Container disk size bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.diskMax) // Container swap usage. c.swap = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_swap_used_bytes", Help: "Container swap usage bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.swap) return &c } // PveMetricsCollector interface implementation. func (c *PveContainerCollector) CollectMetrics() error { cluster, err := c.apiClient.GetClusterStatus() if err != nil { return err } for _, node := range cluster.NodeStatuses { containers, err := c.apiClient.GetNodeContainerList(node.Name) if err != nil { return err } for _, container := range *containers { // Skip templates because they are always offline. if container.Template == 1 { continue } labels := prometheus.Labels{ "cluster": cluster.GetClusterName(), "node": node.Name, "vmid": container.VMID, "name": container.Name, } c.state.With(labels).Set(container.GetStatusNumeric()) c.cpu.With(labels).Set(float64(container.CPUs)) c.memBytes.With(labels).Set(float64(container.MaxMem)) c.diskMax.With(labels).Set(float64(container.MaxDisk)) // Metrics only on running container. if container.IsRunning() { c.uptime.With(labels).Set(float64(container.Uptime)) c.cpuUsage.With(labels).Set(float64(container.CPU)) c.memBytesUsed.With(labels).Set(float64(container.Mem)) c.netReceive.With(labels).Set(float64(container.NetIn)) c.netTransmit.With(labels).Set(float64(container.NetOut)) c.diskRead.With(labels).Set(float64(container.DiskRead)) c.diskWrite.With(labels).Set(float64(container.DiskWrite)) c.disk.With(labels).Set(float64(container.Disk)) c.swap.With(labels).Set(float64(container.Swap)) } } } return nil } // PveMetricsCollector interface implementation. func (c *PveContainerCollector) GetName() string { return "Container" }