Merge pull request #14083 from baude/machinestats

Additional stats for podman info
This commit is contained in:
OpenShift Merge Robot 2022-05-03 10:30:14 -04:00 committed by GitHub
commit e5a8b0ca5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 150 additions and 17 deletions

View File

@ -39,6 +39,10 @@ host:
package: conmon-2.0.29-2.fc34.x86_64
path: /usr/bin/conmon
version: 'conmon version 2.0.29, commit: '
cpu_utilization:
idle_percent: 96.84
system_percent: 0.71
user_percent: 2.45
cpus: 8
distribution:
distribution: fedora
@ -124,6 +128,8 @@ store:
graphDriverName: overlay
graphOptions: {}
graphRoot: /home/dwalsh/.local/share/containers/storage
graphRootAllocated: 510389125120
graphRootUsed: 129170714624
graphStatus:
Backing Filesystem: extfs
Native Overlay Diff: "true"

View File

@ -1,6 +1,8 @@
package define
import "github.com/containers/storage/pkg/idtools"
import (
"github.com/containers/storage/pkg/idtools"
)
// Info is the overall struct that describes the host system
// running libpod/podman
@ -31,6 +33,7 @@ type HostInfo struct {
CgroupControllers []string `json:"cgroupControllers"`
Conmon *ConmonInfo `json:"conmon"`
CPUs int `json:"cpus"`
CPUUtilization *CPUUsage `json:"cpuUtilization"`
Distribution DistributionInfo `json:"distribution"`
EventLogger string `json:"eventLogger"`
Hostname string `json:"hostname"`
@ -108,11 +111,15 @@ type StoreInfo struct {
GraphDriverName string `json:"graphDriverName"`
GraphOptions map[string]interface{} `json:"graphOptions"`
GraphRoot string `json:"graphRoot"`
GraphStatus map[string]string `json:"graphStatus"`
ImageCopyTmpDir string `json:"imageCopyTmpDir"`
ImageStore ImageStore `json:"imageStore"`
RunRoot string `json:"runRoot"`
VolumePath string `json:"volumePath"`
// GraphRootAllocated is how much space the graphroot has in bytes
GraphRootAllocated uint64 `json:"graphRootAllocated"`
// GraphRootUsed is how much of graphroot is used in bytes
GraphRootUsed uint64 `json:"graphRootUsed"`
GraphStatus map[string]string `json:"graphStatus"`
ImageCopyTmpDir string `json:"imageCopyTmpDir"`
ImageStore ImageStore `json:"imageStore"`
RunRoot string `json:"runRoot"`
VolumePath string `json:"volumePath"`
}
// ImageStore describes the image store. Right now only the number
@ -137,3 +144,9 @@ type Plugins struct {
// FIXME what should we do with Authorization, docker seems to return nothing by default
// Authorization []string `json:"authorization"`
}
type CPUUsage struct {
UserPercent float64 `json:"userPercent"`
SystemPercent float64 `json:"systemPercent"`
IdlePercent float64 `json:"idlePercent"`
}

View File

@ -5,11 +5,13 @@ import (
"bytes"
"fmt"
"io/ioutil"
"math"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"syscall"
"time"
"github.com/containers/buildah"
@ -115,7 +117,10 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) {
if err != nil {
return nil, errors.Wrapf(err, "error getting available cgroup controllers")
}
cpuUtil, err := getCPUUtilization()
if err != nil {
return nil, err
}
info := define.HostInfo{
Arch: runtime.GOARCH,
BuildahVersion: buildah.Version,
@ -123,6 +128,7 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) {
CgroupControllers: availableControllers,
Linkmode: linkmode.Linkmode(),
CPUs: runtime.NumCPU(),
CPUUtilization: cpuUtil,
Distribution: hostDistributionInfo,
LogDriver: r.config.Containers.LogDriver,
EventLogger: r.eventer.String(),
@ -285,17 +291,25 @@ func (r *Runtime) storeInfo() (*define.StoreInfo, error) {
}
imageInfo := define.ImageStore{Number: len(images)}
info := define.StoreInfo{
ImageStore: imageInfo,
ImageCopyTmpDir: os.Getenv("TMPDIR"),
ContainerStore: conInfo,
GraphRoot: r.store.GraphRoot(),
RunRoot: r.store.RunRoot(),
GraphDriverName: r.store.GraphDriverName(),
GraphOptions: nil,
VolumePath: r.config.Engine.VolumePath,
ConfigFile: configFile,
var grStats syscall.Statfs_t
if err := syscall.Statfs(r.store.GraphRoot(), &grStats); err != nil {
return nil, errors.Wrapf(err, "unable to collect graph root usasge for %q", r.store.GraphRoot())
}
allocated := uint64(grStats.Bsize) * grStats.Blocks
info := define.StoreInfo{
ImageStore: imageInfo,
ImageCopyTmpDir: os.Getenv("TMPDIR"),
ContainerStore: conInfo,
GraphRoot: r.store.GraphRoot(),
GraphRootAllocated: allocated,
GraphRootUsed: allocated - (uint64(grStats.Bsize) * grStats.Bfree),
RunRoot: r.store.RunRoot(),
GraphDriverName: r.store.GraphDriverName(),
GraphOptions: nil,
VolumePath: r.config.Engine.VolumePath,
ConfigFile: configFile,
}
graphOptions := map[string]interface{}{}
for _, o := range r.store.GraphOptions() {
split := strings.SplitN(o, "=", 2)
@ -382,3 +396,44 @@ func (r *Runtime) GetHostDistributionInfo() define.DistributionInfo {
}
return dist
}
// getCPUUtilization Returns a CPUUsage object that summarizes CPU
// usage for userspace, system, and idle time.
func getCPUUtilization() (*define.CPUUsage, error) {
f, err := os.Open("/proc/stat")
if err != nil {
return nil, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
// Read firt line of /proc/stat
for scanner.Scan() {
break
}
// column 1 is user, column 3 is system, column 4 is idle
stats := strings.Split(scanner.Text(), " ")
return statToPercent(stats)
}
func statToPercent(stats []string) (*define.CPUUsage, error) {
// There is always an extra space between cpu and the first metric
userTotal, err := strconv.ParseFloat(stats[2], 64)
if err != nil {
return nil, errors.Wrapf(err, "unable to parse user value %q", stats[1])
}
systemTotal, err := strconv.ParseFloat(stats[4], 64)
if err != nil {
return nil, errors.Wrapf(err, "unable to parse system value %q", stats[3])
}
idleTotal, err := strconv.ParseFloat(stats[5], 64)
if err != nil {
return nil, errors.Wrapf(err, "unable to parse idle value %q", stats[4])
}
total := userTotal + systemTotal + idleTotal
s := define.CPUUsage{
UserPercent: math.Round((userTotal/total*100)*100) / 100,
SystemPercent: math.Round((systemTotal/total*100)*100) / 100,
IdlePercent: math.Round((idleTotal/total*100)*100) / 100,
}
return &s, nil
}

59
libpod/info_test.go Normal file
View File

@ -0,0 +1,59 @@
package libpod
import (
"fmt"
"testing"
"github.com/containers/podman/v4/libpod/define"
"github.com/stretchr/testify/assert"
)
func Test_statToPercent(t *testing.T) {
type args struct {
in0 []string
}
tests := []struct {
name string
args args
want *define.CPUUsage
wantErr assert.ErrorAssertionFunc
}{
{
name: "GoodParse",
args: args{in0: []string{"cpu", " ", "33628064", "27537", "9696996", "1314806705", "588142", "4775073", "2789228", "0", "598711", "0"}},
want: &define.CPUUsage{
UserPercent: 2.48,
SystemPercent: 0.71,
IdlePercent: 96.81,
},
wantErr: assert.NoError,
},
{
name: "BadUserValue",
args: args{in0: []string{"cpu", " ", "k", "27537", "9696996", "1314806705", "588142", "4775073", "2789228", "0", "598711", "0"}},
want: nil,
wantErr: assert.Error,
},
{
name: "BadSystemValue",
args: args{in0: []string{"cpu", " ", "33628064", "27537", "k", "1314806705", "588142", "4775073", "2789228", "0", "598711", "0"}},
want: nil,
wantErr: assert.Error,
},
{
name: "BadIdleValue",
args: args{in0: []string{"cpu", " ", "33628064", "27537", "9696996", "k", "588142", "4775073", "2789228", "0", "598711", "0"}},
want: nil,
wantErr: assert.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := statToPercent(tt.args.in0)
if !tt.wantErr(t, err, fmt.Sprintf("statToPercent(%v)", tt.args.in0)) {
return
}
assert.Equalf(t, tt.want, got, "statToPercent(%v)", tt.args.in0)
})
}
}