Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve AIX Support #1651

Merged
merged 23 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6b630a8
Add support for per CPU
Dylan-M May 15, 2024
8cc3269
Add support for additional CPU info fields
Dylan-M May 15, 2024
af3f4d2
Correct arguments for AIX percpu
Dylan-M May 15, 2024
d816d96
Initial pass at AIX test integration
Dylan-M May 15, 2024
843d93a
Add AIX test data
Dylan-M May 15, 2024
0a1f220
Remove tests, since my Go knowledge isn't yet deep enough for them
Dylan-M May 15, 2024
5d3cf2e
Update AIX percpu minimum needed lines
Dylan-M May 15, 2024
52d61b8
Add host support for AIX
Dylan-M May 16, 2024
d551997
Fix typos and code misplacement in AIX CPU handling
Dylan-M May 16, 2024
bd42769
Add a working implementation of host.Users() for AIX
Dylan-M May 16, 2024
9be5f7d
Remove extraneous -1 from users function for AIX
Dylan-M May 16, 2024
ea8f72e
Add Usage for AIX nocgo
Dylan-M May 16, 2024
3bf34ce
Add AIX disk serial numbers for nocgo
Dylan-M May 17, 2024
a0bbcca
Only handle virtual memory when specifically requested
Dylan-M May 17, 2024
8df2696
Account for other possible formats output by AIX uptime command
Dylan-M May 18, 2024
0917790
Remove inappropriate package addition
Dylan-M May 18, 2024
b4d95a4
Raise minimum go version to 1.18 (required by changes) and run go mod…
Dylan-M May 18, 2024
b133d60
Ignore host_aix_ppc64 for now
Dylan-M May 18, 2024
9bf502f
Fix logic errors, syntax errors, and typos
Dylan-M May 18, 2024
1d7b4a3
Revert accidental change of go version in go.mod (wasn't supposed to …
Dylan-M May 18, 2024
df9c9bf
Update min version in the readme to match new required min version.
Dylan-M May 21, 2024
ff4ae36
Remove extraneous development note comments
Dylan-M May 22, 2024
125da53
Update the README charts with the AIX information
Dylan-M May 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ See https://pkg.go.dev/github.com/shirou/gopsutil/v3 or https://godocs.io/github

## Requirements

- go1.16 or above is required.
- go1.18 or above is required.

## More Info

Expand Down
79 changes: 72 additions & 7 deletions cpu/cpu_aix_nocgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,57 @@ import (
)

func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
var ret []TimesStat
if percpu {
return []TimesStat{}, common.ErrNotImplementedError
per_out, err := invoke.CommandWithContext(ctx, "sar", "-u", "-P", "ALL", "10", "1")
if err != nil {
return nil, err
}
lines := strings.Split(string(per_out), "\n")
if len(lines) < 6 {
return []TimesStat{}, common.ErrNotImplementedError
}

hp := strings.Fields(lines[5]) // headers
for l := 6; l < len(lines)-1; l++ {
ct := &TimesStat{}
v := strings.Fields(lines[l]) // values
for i, header := range hp {
// We're done in any of these use cases
if i >= len(v) || v[0] == "-" {
break
}

// Position variable for v
pos := i
// There is a missing field at the beginning of all but the first line
// so adjust the position
if l > 6 {
pos = i - 1
}
// We don't want invalid positions
if pos < 0 {
continue
}

if t, err := strconv.ParseFloat(v[pos], 64); err == nil {
switch header {
case `cpu`:
ct.CPU = strconv.FormatFloat(t, 'f', -1, 64)
case `%usr`:
ct.User = t
case `%sys`:
ct.System = t
case `%wio`:
ct.Iowait = t
case `%idle`:
ct.Idle = t
}
}
}
// Valid CPU data, so append it
ret = append(ret, *ct)
}
} else {
out, err := invoke.CommandWithContext(ctx, "sar", "-u", "10", "1")
if err != nil {
Expand All @@ -24,26 +73,28 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
return []TimesStat{}, common.ErrNotImplementedError
}

ret := TimesStat{CPU: "cpu-total"}
ct := &TimesStat{CPU: "cpu-total"}
h := strings.Fields(lines[len(lines)-3]) // headers
v := strings.Fields(lines[len(lines)-2]) // values
for i, header := range h {
if t, err := strconv.ParseFloat(v[i], 64); err == nil {
switch header {
case `%usr`:
ret.User = t
ct.User = t
case `%sys`:
ret.System = t
ct.System = t
case `%wio`:
ret.Iowait = t
ct.Iowait = t
case `%idle`:
ret.Idle = t
ct.Idle = t
}
}
}

return []TimesStat{ret}, nil
ret = append(ret, *ct)
}

return ret, nil
}

func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
Expand Down Expand Up @@ -78,6 +129,20 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
}
}
break
} else if strings.HasPrefix(line, "System Model:") {
p := strings.Split(string(line), ":")
if p != nil {
ret.VendorID = strings.TrimSpace(p[1])
}
} else if strings.HasPrefix(line, "Processor Type:") {
p := strings.Split(string(line), ":")
if p != nil {
c := strings.Split(string(p[1]), "_")
if c != nil {
ret.Family = strings.TrimSpace(c[0])
ret.Model = strings.TrimSpace(c[1])
}
}
}
}
return []InfoStat{ret}, nil
Expand Down
68 changes: 68 additions & 0 deletions cpu/testdata/aix/prtconf
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
System Model: IBM pSeries (emulated by qemu)
Machine Serial Number: Not Available
Processor Type: PowerPC_POWER8
Processor Implementation Mode: POWER 8
Processor Version: PV_8_Compat
Number Of Processors: 4
Processor Clock Speed: 1000 MHz
CPU Type: 64-bit
Kernel Type: 64-bit
LPAR Info: 0 aix_7200-04-02-2027
Memory Size: 4096 MB
Good Memory Size: 4096 MB
Platform Firmware level: Not Available
Firmware Version: SLOF,HEAD
Console Login: enable
Auto Restart: true
Full Core: false
NX Crypto Acceleration: Not Capable
In-Core Crypto Acceleration: Capable, but not Enabled

en0
Network Information
Host Name: aix72-dylan
IP Address: 192.168.124.53
Sub Netmask:
Gateway: 192.168.124.1
Name Server:
Domain Name:

Paging Space Information
Total Paging Space: 512MB
Percent Used: 1%

Volume Groups Information
==============================================================================
Active VGs
==============================================================================
rootvg:
PV_NAME PV STATE TOTAL PPs FREE PPs FREE DISTRIBUTION
hdisk0 active 999 809 199..193..17..200..200
==============================================================================

INSTALLED RESOURCE LIST

The following resources are installed on the machine.
+/- = Added or deleted from Resource List.
* = Diagnostic support not available.

Model Architecture: chrp
Model Implementation: Uni-Processor, PCI bus

+ sys0 System Object
+ sysplanar0 System Planar
* vio0 Virtual I/O Bus
* ent0 Virtual I/O Ethernet Adapter (l-lan)
* vscsi0 Virtual SCSI Client Adapter
* cd0 Virtual SCSI Optical Served by VIO Server
* vsa0 LPAR Virtual Serial Adapter
* vty0 Asynchronous Terminal
* pci0 PCI Bus
* scsi0 qemu_virtio-scsi-pci:0000:00:02.0 Virtio SCSI Client Adapter (f41a0800)
* hdisk0 qemu_virtio-scsi-pci:0000:00:02.0-LW_0 MPIO Other Virtio SCSI Disk Drive
+ L2cache0 L2 Cache
+ mem0 Memory
+ proc0 Processor
+ proc1 Processor
+ proc2 Processor
+ proc3 Processor
11 changes: 11 additions & 0 deletions cpu/testdata/aix/sar-u-PALL101
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

AIX aix72-dylan 2 7 000000000000 05/15/24

System configuration: lcpu=4 ent=4.00 mode=Capped

11:19:03 cpu %usr %sys %wio %idle physc %entc
11:19:13 0 1 11 0 88 1.00 25.0
1 0 0 0 100 1.00 25.0
2 0 0 0 100 1.00 25.0
3 0 0 0 100 1.00 25.0
- 0 3 0 97 4.00 100.0
7 changes: 7 additions & 0 deletions cpu/testdata/aix/sar-u101
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

AIX aix72-dylan 2 7 000000000000 05/15/24

System configuration: lcpu=4 ent=4.00 mode=Capped

11:19:44 %usr %sys %wio %idle physc %entc
11:19:54 0 3 0 96 4.00 100.0
4 changes: 0 additions & 4 deletions disk/disk_aix.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOC
return nil, common.ErrNotImplementedError
}

func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}

func LabelWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
5 changes: 5 additions & 0 deletions disk/disk_aix_cgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"

"github.com/power-devops/perfstat"
"github.com/shirou/gopsutil/v3/internal/common"
)

var FSType map[int]string
Expand Down Expand Up @@ -74,3 +75,7 @@ func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
}
return nil, fmt.Errorf("mountpoint %s not found", path)
}

func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
}
144 changes: 144 additions & 0 deletions disk/disk_aix_nocgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ package disk

import (
"context"
"errors"
"regexp"
"strconv"
"strings"

"github.com/shirou/gopsutil/v3/internal/common"
Expand Down Expand Up @@ -79,3 +81,145 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
func getFsType(stat unix.Statfs_t) string {
return FSType[int(stat.Vfstype)]
}

func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
Dylan-M marked this conversation as resolved.
Show resolved Hide resolved
out, err := invoke.CommandWithContext(ctx, "df", "-v")
if err != nil {
return nil, err
}

ret := &UsageStat{}

blocksize := uint64(512)
lines := strings.Split(string(out), "\n")
if len(lines) < 2 {
return &UsageStat{}, common.ErrNotImplementedError
}

hf := strings.Fields(strings.Replace(lines[0], "Mounted on", "Path", -1)) // headers
for line := 1; line < len(lines); line++ {
fs := strings.Fields(lines[line]) // values
for i, header := range hf {
// We're done in any of these use cases
if i >= len(fs) {
break
}

switch header {
case `Filesystem`:
// This is not a valid fs for us to parse
if fs[i] == "/proc" || fs[i] == "/ahafs" || fs[i] != path {
break
}

ret.Fstype, err = GetMountFSTypeWithContext(ctx, fs[i])
if err != nil {
return nil, err
}
case `Path`:
ret.Path = fs[i]
case `512-blocks`:
total, err := strconv.ParseUint(fs[i], 10, 64)
ret.Total = total * blocksize
if err != nil {
return nil, err
}
case `Used`:
ret.Used, err = strconv.ParseUint(fs[i], 10, 64)
if err != nil {
return nil, err
}
case `Free`:
ret.Free, err = strconv.ParseUint(fs[i], 10, 64)
if err != nil {
return nil, err
}
case `%Used`:
val, err := strconv.Atoi(strings.Replace(fs[i], "%", "", -1))
if err != nil {
return nil, err
}
ret.UsedPercent = float64(val) / float64(100)
case `Ifree`:
ret.InodesFree, err = strconv.ParseUint(fs[i], 10, 64)
if err != nil {
return nil, err
}
case `Iused`:
ret.InodesUsed, err = strconv.ParseUint(fs[i], 10, 64)
if err != nil {
return nil, err
}
case `%Iused`:
val, err := strconv.Atoi(strings.Replace(fs[i], "%", "", -1))
if err != nil {
return nil, err
}
ret.InodesUsedPercent = float64(val) / float64(100)
}
}

// Calculated value, since it isn't returned by the command
ret.InodesTotal = ret.InodesUsed + ret.InodesFree

// Valid Usage data, so append it
return ret, nil
}

return ret, nil
}

func GetMountFSTypeWithContext(ctx context.Context, mp string) (string, error) {
out, err := invoke.CommandWithContext(ctx, "mount")
if err != nil {
return "", err
}

// Kind of inefficient, but it works
lines := strings.Split(string(out[:]), "\n")
for line := 1; line < len(lines); line++ {
fields := strings.Fields(lines[line])
if strings.TrimSpace(fields[0]) == mp {
return fields[2], nil
}
}

return "", nil
}

// Using lscfg and a device name, we can get the device information
// # lscfg -vl hdisk2
// hdisk2 U8284.22A.21D72DW-V19-C22-T1-W500507680304A7D2-L2000000000000 MPIO FC 2145

// Manufacturer................IBM
// Machine Type and Model......2145
// ROS Level and ID............0000
// Device Specific.(Z0)........0000063268181002
// Device Specific.(Z1)........00c0204
// Serial Number...............600507630081029F5000000000000015
Dylan-M marked this conversation as resolved.
Show resolved Hide resolved
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
// This isn't linux, these aren't actual disk devices
if strings.HasPrefix(name, "/dev/") {
return "", errors.New("devices on /dev are not physical disks on aix")
}
out, err := invoke.CommandWithContext(ctx, "lscfg", "-vl", name)
if err != nil {
return "", err
}

ret := ""
// Kind of inefficient, but it works
lines := strings.Split(string(out[:]), "\n")
for line := 1; line < len(lines); line++ {
v := strings.TrimSpace(lines[line])
if strings.HasPrefix(v, "Serial Number...............") {
ret = strings.TrimPrefix(v, "Serial Number...............")
if ret == "" {
return "", errors.New("empty serial for disk")
}
return ret, nil
}
}

return ret, errors.New("serial entry not found for disk")
}
Loading
Loading