mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Additional stats for podman info
In support of podman machine and its counterpart desktop, we have added new stats to podman info. For storage, we have added GraphRootAllocated and GraphRootUsed in bytes. For CPUs, we have added user, system, and idle percents based on /proc/stat. Fixes: #13876 Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
@ -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"
|
||||
|
@ -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,6 +111,10 @@ type StoreInfo struct {
|
||||
GraphDriverName string `json:"graphDriverName"`
|
||||
GraphOptions map[string]interface{} `json:"graphOptions"`
|
||||
GraphRoot string `json:"graphRoot"`
|
||||
// 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"`
|
||||
@ -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"`
|
||||
}
|
||||
|
@ -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)}
|
||||
|
||||
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
59
libpod/info_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user