summaryrefslogtreecommitdiff
path: root/libpod/stats.go
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2017-12-04 15:06:06 -0600
committerAtomic Bot <atomic-devel@projectatomic.io>2017-12-13 21:15:09 +0000
commit9cc0c8ae143070c4d4ee691cab6a0fd0ebcaa538 (patch)
tree9736c47af7246c93e2e2b6d6d246653ca57cc316 /libpod/stats.go
parent61f606e192ad67b4819a909a6f1b18c41e33db2d (diff)
downloadpodman-9cc0c8ae143070c4d4ee691cab6a0fd0ebcaa538.tar.gz
podman-9cc0c8ae143070c4d4ee691cab6a0fd0ebcaa538.tar.bz2
podman-9cc0c8ae143070c4d4ee691cab6a0fd0ebcaa538.zip
kpod stats
Move kpod stats to the libpod backend. Signed-off-by: baude <bbaude@redhat.com> Closes: #113 Approved by: baude
Diffstat (limited to 'libpod/stats.go')
-rw-r--r--libpod/stats.go120
1 files changed, 120 insertions, 0 deletions
diff --git a/libpod/stats.go b/libpod/stats.go
new file mode 100644
index 000000000..86da0679e
--- /dev/null
+++ b/libpod/stats.go
@@ -0,0 +1,120 @@
+package libpod
+
+import (
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/containerd/cgroups"
+ "github.com/opencontainers/runc/libcontainer"
+ "github.com/pkg/errors"
+)
+
+// ContainerStats contains the statistics information for a running container
+type ContainerStats struct {
+ ContainerID string
+ CPU float64
+ CPUNano uint64
+ SystemNano uint64
+ MemUsage uint64
+ MemLimit uint64
+ MemPerc float64
+ NetInput uint64
+ NetOutput uint64
+ BlockInput uint64
+ BlockOutput uint64
+ PIDs uint64
+}
+
+// GetContainerStats gets the running stats for a given container
+func (c *Container) GetContainerStats(previousStats *ContainerStats) (*ContainerStats, error) {
+ stats := new(ContainerStats)
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ if err := c.syncContainer(); err != nil {
+ return stats, errors.Wrapf(err, "error updating container %s state", c.ID())
+ }
+ cgroup, err := cgroups.Load(cgroups.V1, c.CGroupPath())
+ if err != nil {
+ return stats, errors.Wrapf(err, "unable to load cgroup at %+v", c.CGroupPath())
+ }
+
+ cgroupStats, err := cgroup.Stat()
+ if err != nil {
+ return stats, errors.Wrapf(err, "unable to obtain cgroup stats")
+ }
+ conState := c.state.State
+ if err != nil {
+ return stats, errors.Wrapf(err, "unable to determine container state")
+ }
+
+ previousCPU := previousStats.CPUNano
+ previousSystem := previousStats.SystemNano
+ stats.ContainerID = c.ID()
+ stats.CPU = calculateCPUPercent(cgroupStats, previousCPU, previousSystem)
+ stats.MemUsage = cgroupStats.Memory.Usage.Usage
+ stats.MemLimit = getMemLimit(cgroupStats.Memory.Usage.Limit)
+ stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100
+ stats.PIDs = 0
+ if conState == ContainerStateRunning {
+ stats.PIDs = cgroupStats.Pids.Current - 1
+ }
+ stats.BlockInput, stats.BlockOutput = calculateBlockIO(cgroupStats)
+ stats.CPUNano = cgroupStats.Cpu.Usage.Total
+ stats.SystemNano = cgroupStats.Cpu.Usage.Kernel
+ // TODO Figure out where to get the Netout stuff.
+ //stats.NetInput, stats.NetOutput = getContainerNetIO(cgroupStats)
+ return stats, nil
+}
+
+// getMemory limit returns the memory limit for a given cgroup
+// If the configured memory limit is larger than the total memory on the sys, the
+// physical system memory size is returned
+func getMemLimit(cgroupLimit uint64) uint64 {
+ si := &syscall.Sysinfo_t{}
+ err := syscall.Sysinfo(si)
+ if err != nil {
+ return cgroupLimit
+ }
+
+ physicalLimit := uint64(si.Totalram)
+ if cgroupLimit > physicalLimit {
+ return physicalLimit
+ }
+ return cgroupLimit
+}
+
+// Returns the total number of bytes transmitted and received for the given container stats
+func getContainerNetIO(stats *libcontainer.Stats) (received uint64, transmitted uint64) { //nolint
+ for _, iface := range stats.Interfaces {
+ received += iface.RxBytes
+ transmitted += iface.TxBytes
+ }
+ return
+}
+
+func calculateCPUPercent(stats *cgroups.Stats, previousCPU, previousSystem uint64) float64 {
+ var (
+ cpuPercent = 0.0
+ cpuDelta = float64(stats.Cpu.Usage.Total - previousCPU)
+ systemDelta = float64(uint64(time.Now().UnixNano()) - previousSystem)
+ )
+ if systemDelta > 0.0 && cpuDelta > 0.0 {
+ // gets a ratio of container cpu usage total, multiplies it by the number of cores (4 cores running
+ // at 100% utilization should be 400% utilization), and multiplies that by 100 to get a percentage
+ cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.Cpu.Usage.PerCpu)) * 100
+ }
+ return cpuPercent
+}
+
+func calculateBlockIO(stats *cgroups.Stats) (read uint64, write uint64) {
+ for _, blkIOEntry := range stats.Blkio.IoServiceBytesRecursive {
+ switch strings.ToLower(blkIOEntry.Op) {
+ case "read":
+ read += blkIOEntry.Value
+ case "write":
+ write += blkIOEntry.Value
+ }
+ }
+ return
+}