Skip to content
80 changes: 68 additions & 12 deletions collector/cpu_aix.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,73 @@ 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) {
ticks, err := tickPerSecond()
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
}

Expand All @@ -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
}
67 changes: 65 additions & 2 deletions collector/diskstats_aix.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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,

Expand All @@ -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
}
6 changes: 3 additions & 3 deletions collector/filesystem_aix.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 7 additions & 3 deletions collector/meminfo_aix.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
24 changes: 14 additions & 10 deletions collector/netdev_aix.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}

Expand Down
86 changes: 86 additions & 0 deletions collector/netinterface_aix.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading