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 *TTLGaugeVec // Container network RX in bytes prometheus gauge. netTransmit *TTLGaugeVec // Container network TX in bytes prometheus gauge. diskWrite *TTLGaugeVec // Container disk written in bytes prometheus gauge. diskRead *TTLGaugeVec // Container disk read in bytes prometheus gauge. 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", Help: "Container uptime.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.uptime) // Container CPU count. c.cpu = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_cpu_count", 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", Help: "Container CPU usage.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.cpuUsage) // Container memory total. c.memBytes = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_mem_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_mem_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 = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_network_in_bytes", Help: "Container network RX bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.netReceive) // Container network TX. c.netTransmit = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_network_out_bytes", Help: "Container network TX bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.netTransmit) // Container disk written. c.diskWrite = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_disk_wr_bytes", Help: "Container disk written bytes.", }, []string{"cluster", "node", "vmid", "name"}, 5*time.Minute, ) c.registry.Register(c.diskWrite) // Container disk read. c.diskRead = NewTTLGaugeVec( prometheus.GaugeOpts{ Name: "pve_ct_disk_rd_bytes", Help: "Container disk read bytes.", }, []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_usage_bytes", Help: "Container disk read 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" }