diff --git a/collector/cpu_aix.go b/collector/cpu_aix.go index 9d896e2ded..017e1a0c22 100644 --- a/collector/cpu_aix.go +++ b/collector/cpu_aix.go @@ -30,22 +30,50 @@ import ( "github.com/prometheus/client_golang/prometheus" ) +var ( + nodeCPUPhysicalSecondsDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "physical_seconds_total"), + "Seconds the physical CPUs spent in each mode.", + []string{"cpu", "mode"}, nil, + ) + nodeCPUSRunQueueDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "runqueue"), + "Length of the run queue.", []string{"cpu"}, nil, + ) + nodeCPUFlagsDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "flags"), + "CPU flags.", + []string{"cpu", "flag"}, nil, + ) + nodeCPUContextSwitchDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "context_switches_total"), + "Number of context switches.", + []string{"cpu"}, nil, + ) +) + type cpuCollector struct { - cpu typedDesc - logger *slog.Logger - tickPerSecond int64 + cpu typedDesc + cpuPhysical typedDesc + cpuRunQueue typedDesc + cpuFlags typedDesc + cpuContextSwitch typedDesc + + logger *slog.Logger + tickPerSecond float64 + purrTicksPerSecond float64 } func init() { registerCollector("cpu", defaultEnabled, NewCpuCollector) } -func tickPerSecond() (int64, error) { +func tickPerSecond() (float64, error) { ticks, err := C.sysconf(C._SC_CLK_TCK) if ticks == -1 || err != nil { return 0, fmt.Errorf("failed to get clock ticks per second: %v", err) } - return int64(ticks), nil + return float64(ticks), nil } func NewCpuCollector(logger *slog.Logger) (Collector, error) { @@ -53,10 +81,22 @@ func NewCpuCollector(logger *slog.Logger) (Collector, error) { if err != nil { return nil, err } + + pconfig, err := perfstat.PartitionStat() + + if err != nil { + return nil, err + } + return &cpuCollector{ - cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, - logger: logger, - tickPerSecond: ticks, + cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, + cpuPhysical: typedDesc{nodeCPUPhysicalSecondsDesc, prometheus.CounterValue}, + cpuRunQueue: typedDesc{nodeCPUSRunQueueDesc, prometheus.GaugeValue}, + cpuFlags: typedDesc{nodeCPUFlagsDesc, prometheus.GaugeValue}, + cpuContextSwitch: typedDesc{nodeCPUContextSwitchDesc, prometheus.CounterValue}, + logger: logger, + tickPerSecond: ticks, + purrTicksPerSecond: float64(pconfig.ProcessorMhz * 1e6), }, nil } @@ -67,10 +107,26 @@ func (c *cpuCollector) Update(ch chan<- prometheus.Metric) error { } for n, stat := range stats { - ch <- c.cpu.mustNewConstMetric(float64(stat.User/c.tickPerSecond), strconv.Itoa(n), "user") - ch <- c.cpu.mustNewConstMetric(float64(stat.Sys/c.tickPerSecond), strconv.Itoa(n), "system") - ch <- c.cpu.mustNewConstMetric(float64(stat.Idle/c.tickPerSecond), strconv.Itoa(n), "idle") - ch <- c.cpu.mustNewConstMetric(float64(stat.Wait/c.tickPerSecond), strconv.Itoa(n), "wait") + // LPAR metrics + ch <- c.cpu.mustNewConstMetric(float64(stat.User)/c.tickPerSecond, strconv.Itoa(n), "user") + ch <- c.cpu.mustNewConstMetric(float64(stat.Sys)/c.tickPerSecond, strconv.Itoa(n), "system") + ch <- c.cpu.mustNewConstMetric(float64(stat.Idle)/c.tickPerSecond, strconv.Itoa(n), "idle") + ch <- c.cpu.mustNewConstMetric(float64(stat.Wait)/c.tickPerSecond, strconv.Itoa(n), "wait") + + // Physical CPU metrics + ch <- c.cpuPhysical.mustNewConstMetric(float64(stat.PIdle)/c.purrTicksPerSecond, strconv.Itoa(n), "pidle") + ch <- c.cpuPhysical.mustNewConstMetric(float64(stat.PUser)/c.purrTicksPerSecond, strconv.Itoa(n), "puser") + ch <- c.cpuPhysical.mustNewConstMetric(float64(stat.PSys)/c.purrTicksPerSecond, strconv.Itoa(n), "psys") + ch <- c.cpuPhysical.mustNewConstMetric(float64(stat.PWait)/c.purrTicksPerSecond, strconv.Itoa(n), "pwait") + + // Run queue length + ch <- c.cpuRunQueue.mustNewConstMetric(float64(stat.RunQueue), strconv.Itoa(n)) + + // Flags + ch <- c.cpuFlags.mustNewConstMetric(float64(stat.SpurrFlag), strconv.Itoa(n), "spurr") + + // Context switches + ch <- c.cpuContextSwitch.mustNewConstMetric(float64(stat.CSwitches), strconv.Itoa(n)) } return nil } diff --git a/collector/diskstats_aix.go b/collector/diskstats_aix.go index c6619fd9bd..4ad39ff38d 100644 --- a/collector/diskstats_aix.go +++ b/collector/diskstats_aix.go @@ -30,11 +30,19 @@ type diskstatsCollector struct { rbytes typedDesc wbytes typedDesc time typedDesc + bsize typedDesc + qdepth typedDesc + + rserv typedDesc + wserv typedDesc + + xfers typedDesc + xrate typedDesc deviceFilter deviceFilter logger *slog.Logger - tickPerSecond int64 + tickPerSecond float64 } func init() { @@ -57,6 +65,54 @@ func NewDiskstatsCollector(logger *slog.Logger) (Collector, error) { wbytes: typedDesc{writtenBytesDesc, prometheus.CounterValue}, time: typedDesc{ioTimeSecondsDesc, prometheus.CounterValue}, + bsize: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "block_size_bytes"), + "Size of the block device in bytes.", + diskLabelNames, nil, + ), + prometheus.GaugeValue, + }, + qdepth: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "queue_depth"), + "Number of requests in the queue.", + diskLabelNames, nil, + ), + prometheus.GaugeValue, + }, + rserv: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "read_time_seconds_total"), + "The total time spent servicing read requests.", + diskLabelNames, nil, + ), + prometheus.CounterValue, + }, + wserv: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "write_time_seconds_total"), + "The total time spent servicing write requests.", + diskLabelNames, nil, + ), + prometheus.CounterValue, + }, + xfers: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "transfers_total"), + "The total number of transfers to/from disk.", + diskLabelNames, nil, + ), + prometheus.CounterValue, + }, + xrate: typedDesc{ + prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "transfers_to_disk_total"), + "The total number of transfers from disk.", + diskLabelNames, nil, + ), + prometheus.CounterValue, + }, deviceFilter: deviceFilter, logger: logger, @@ -76,7 +132,14 @@ func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { } ch <- c.rbytes.mustNewConstMetric(float64(stat.Rblks*512), stat.Name) ch <- c.wbytes.mustNewConstMetric(float64(stat.Wblks*512), stat.Name) - ch <- c.time.mustNewConstMetric(float64(stat.Time/c.tickPerSecond), stat.Name) + ch <- c.time.mustNewConstMetric(float64(stat.Time)/float64(c.tickPerSecond), stat.Name) + + ch <- c.bsize.mustNewConstMetric(float64(stat.BSize), stat.Name) + ch <- c.qdepth.mustNewConstMetric(float64(stat.QDepth), stat.Name) + ch <- c.rserv.mustNewConstMetric(float64(stat.Rserv)/1e9, stat.Name) + ch <- c.wserv.mustNewConstMetric(float64(stat.Wserv)/1e9, stat.Name) + ch <- c.xfers.mustNewConstMetric(float64(stat.Xfers), stat.Name) + ch <- c.xrate.mustNewConstMetric(float64(stat.XRate), stat.Name) } return nil } diff --git a/collector/filesystem_aix.go b/collector/filesystem_aix.go index a1314a0f01..84bb89131d 100644 --- a/collector/filesystem_aix.go +++ b/collector/filesystem_aix.go @@ -53,9 +53,9 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { mountPoint: stat.MountPoint, fsType: fstype, }, - size: float64(stat.TotalBlocks / 512.0), - free: float64(stat.FreeBlocks / 512.0), - avail: float64(stat.FreeBlocks / 512.0), // AIX doesn't distinguish between free and available blocks. + size: float64(stat.TotalBlocks * 512.0), + free: float64(stat.FreeBlocks * 512.0), + avail: float64(stat.FreeBlocks * 512.0), // AIX doesn't distinguish between free and available blocks. files: float64(stat.TotalInodes), filesFree: float64(stat.FreeInodes), ro: ro, diff --git a/collector/meminfo_aix.go b/collector/meminfo_aix.go index ff59105b04..f90b1fd827 100644 --- a/collector/meminfo_aix.go +++ b/collector/meminfo_aix.go @@ -40,8 +40,12 @@ func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { } return map[string]float64{ - "total_bytes": float64(stats.RealTotal * 4096), - "free_bytes": float64(stats.RealFree * 4096), - "available_bytes": float64(stats.RealAvailable * 4096), + "total_bytes": float64(stats.RealTotal * 4096), + "free_bytes": float64(stats.RealFree * 4096), + "available_bytes": float64(stats.RealAvailable * 4096), + "process_bytes": float64(stats.RealProcess * 4096), + "paging_space_total_bytes": float64(stats.PgSpTotal * 4096), + "paging_space_free_bytes": float64(stats.PgSpFree * 4096), + "page_scans_total": float64(stats.Scans), }, nil } diff --git a/collector/netdev_aix.go b/collector/netdev_aix.go index ae316443cf..e29371b100 100644 --- a/collector/netdev_aix.go +++ b/collector/netdev_aix.go @@ -32,16 +32,20 @@ func getNetDevStats(filter *deviceFilter, logger *slog.Logger) (netDevStats, err for _, stat := range stats { netDev[stat.Name] = map[string]uint64{ - "receive_packets": uint64(stat.RxPackets), - "transmit_packets": uint64(stat.TxPackets), - "receive_bytes": uint64(stat.RxBytes), - "transmit_bytes": uint64(stat.TxBytes), - "receive_errors": uint64(stat.RxErrors), - "transmit_errors": uint64(stat.TxErrors), - "receive_dropped": uint64(stat.RxPacketsDropped), - "transmit_dropped": uint64(stat.TxPacketsDropped), - "receive_multicast": uint64(stat.RxMulticastPackets), - "transmit_multicast": uint64(stat.TxMulticastPackets), + "receive_bytes": uint64(stat.RxBytes), + "receive_dropped": uint64(stat.RxPacketsDropped), + "receive_errors": uint64(stat.RxErrors), + "receive_multicast": uint64(stat.RxMulticastPackets), + "receive_packets": uint64(stat.RxPackets), + "receive_collision_errors": uint64(stat.RxCollisionErrors), + "transmit_bytes": uint64(stat.TxBytes), + "transmit_dropped": uint64(stat.TxPacketsDropped), + "transmit_errors": uint64(stat.TxErrors), + "transmit_multicast": uint64(stat.TxMulticastPackets), + "transmit_packets": uint64(stat.TxPackets), + "transmit_queue_overflow": uint64(stat.TxQueueOverflow), + "transmit_collision_single_errors": uint64(stat.TxSingleCollisionCount), + "transmit_collision_multiple_errors": uint64(stat.TxMultipleCollisionCount), } } diff --git a/collector/netinterface_aix.go b/collector/netinterface_aix.go new file mode 100644 index 0000000000..9a5b766828 --- /dev/null +++ b/collector/netinterface_aix.go @@ -0,0 +1,86 @@ +// Copyright 2025 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !nonetinterface +// +build !nonetinterface + +package collector + +import ( + "log/slog" + + "github.com/power-devops/perfstat" + "github.com/prometheus/client_golang/prometheus" +) + +type netinterfaceCollector struct { + logger *slog.Logger + collisions *prometheus.Desc + ibytes *prometheus.Desc + ipackets *prometheus.Desc + obytes *prometheus.Desc + opackets *prometheus.Desc +} + +const ( + netinterfaceSubsystem = "netinterface" +) + +func init() { + registerCollector("netinterface", defaultEnabled, NewNetinterfaceCollector) +} + +func NewNetinterfaceCollector(logger *slog.Logger) (Collector, error) { + labels := []string{"interface"} + return &netinterfaceCollector{ + logger: logger, + collisions: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "collisions_total"), + "Total number of CSMA collisions on the interface.", labels, nil, + ), + ibytes: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "receive_bytes_total"), + "Total number of bytes received on the interface.", labels, nil, + ), + ipackets: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "receive_packets_total"), + "Total number of packets received on the interface.", labels, nil, + ), + obytes: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "transmit_bytes_total"), + "Total number of bytes transmitted on the interface.", labels, nil, + ), + opackets: prometheus.NewDesc( + prometheus.BuildFQName(namespace, netinterfaceSubsystem, "transmit_packets_total"), + "Total number of packets transmitted on the interface.", labels, nil, + ), + }, nil +} + +func (c *netinterfaceCollector) Update(ch chan<- prometheus.Metric) error { + stats, err := perfstat.NetIfaceStat() + if err != nil { + return err + } + + for _, stat := range stats { + iface := stat.Name + + ch <- prometheus.MustNewConstMetric(c.collisions, prometheus.CounterValue, float64(stat.Collisions), iface) + ch <- prometheus.MustNewConstMetric(c.ibytes, prometheus.CounterValue, float64(stat.IBytes), iface) + ch <- prometheus.MustNewConstMetric(c.ipackets, prometheus.CounterValue, float64(stat.IPackets), iface) + ch <- prometheus.MustNewConstMetric(c.obytes, prometheus.CounterValue, float64(stat.OBytes), iface) + ch <- prometheus.MustNewConstMetric(c.opackets, prometheus.CounterValue, float64(stat.OPackets), iface) + } + return nil +} diff --git a/collector/partition_aix.go b/collector/partition_aix.go new file mode 100644 index 0000000000..3c54e0f41e --- /dev/null +++ b/collector/partition_aix.go @@ -0,0 +1,118 @@ +// Copyright 2025 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !nopartition +// +build !nopartition + +package collector + +import ( + "log/slog" + + "github.com/power-devops/perfstat" + "github.com/prometheus/client_golang/prometheus" +) + +type partitionCollector struct { + logger *slog.Logger + entitledCapacity *prometheus.Desc + memoryMax *prometheus.Desc + memoryOnline *prometheus.Desc + cpuOnline *prometheus.Desc + cpuSys *prometheus.Desc + cpuPool *prometheus.Desc + powerSaveMode *prometheus.Desc + smtThreads *prometheus.Desc +} + +const ( + partitionCollectorSubsystem = "partition" +) + +func init() { + registerCollector("partition", defaultEnabled, NewPartitionCollector) +} + +func NewPartitionCollector(logger *slog.Logger) (Collector, error) { + return &partitionCollector{ + logger: logger, + entitledCapacity: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "entitled_capacity"), + "Entitled processor capacity of the partition in CPU units (e.g. 1.0 = one core).", + nil, nil, + ), + memoryMax: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "memory_max"), + "Maximum memory of the partition in bytes.", + nil, nil, + ), + memoryOnline: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "memory_online"), + "Online memory of the partition in bytes.", + nil, nil, + ), + cpuOnline: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "cpus_online"), + "Number of online CPUs in the partition.", + nil, nil, + ), + cpuSys: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "cpus_sys"), + "Number of physical CPUs in the system.", + nil, nil, + ), + cpuPool: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "cpus_pool"), + "Number of physical CPUs in the pool.", + nil, nil, + ), + powerSaveMode: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "power_save_mode"), + "Power save mode of the partition (1 for enabled, 0 for disabled).", + nil, nil, + ), + smtThreads: prometheus.NewDesc( + prometheus.BuildFQName(namespace, partitionCollectorSubsystem, "smt_threads"), + "Number of SMT threads per core.", + nil, nil, + ), + }, nil +} + +func (c *partitionCollector) Update(ch chan<- prometheus.Metric) error { + stats, err := perfstat.PartitionStat() + if err != nil { + return err + } + + powerSaveMode := 0.0 + if stats.Conf.PowerSave { + powerSaveMode = 1.0 + } + + ch <- prometheus.MustNewConstMetric(c.entitledCapacity, prometheus.GaugeValue, float64(stats.EntCapacity)/100.0) + + ch <- prometheus.MustNewConstMetric(c.memoryMax, prometheus.GaugeValue, float64(stats.Mem.Max)*1024*1024) + ch <- prometheus.MustNewConstMetric(c.memoryOnline, prometheus.GaugeValue, float64(stats.Mem.Online)*1024*1024) + + ch <- prometheus.MustNewConstMetric(c.cpuOnline, prometheus.GaugeValue, float64(stats.VCpus.Online)) + + ch <- prometheus.MustNewConstMetric(c.cpuSys, prometheus.GaugeValue, float64(stats.NumProcessors.Online)) + + ch <- prometheus.MustNewConstMetric(c.cpuPool, prometheus.GaugeValue, float64(stats.ActiveCpusInPool)) + + ch <- prometheus.MustNewConstMetric(c.powerSaveMode, prometheus.GaugeValue, powerSaveMode) + ch <- prometheus.MustNewConstMetric(c.smtThreads, prometheus.GaugeValue, float64(stats.SmtThreads)) + + return nil +}