shirou_gopsutil/docker/docker_linux.go

255 lines
6.1 KiB
Go
Raw Normal View History

2014-06-10 13:38:01 +08:00
// +build linux
2014-12-30 21:09:05 +08:00
package docker
2014-06-10 13:38:01 +08:00
import (
"encoding/json"
"fmt"
"os"
2014-06-10 13:38:01 +08:00
"os/exec"
"path"
"strconv"
"strings"
2014-11-27 09:25:14 +08:00
2014-11-27 09:32:35 +08:00
cpu "github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/internal/common"
2014-06-10 13:38:01 +08:00
)
// GetDockerStat returns a list of Docker basic stats.
// This requires certain permission.
func GetDockerStat() ([]CgroupDockerStat, error) {
path, err := exec.LookPath("docker")
if err != nil {
return nil, ErrDockerNotAvailable
}
out, err := invoke.Command(path, "ps", "-a", "--no-trunc", "--format", "{{.ID}}|{{.Image}}|{{.Names}}|{{.Status}}")
if err != nil {
return []CgroupDockerStat{}, err
}
lines := strings.Split(string(out), "\n")
ret := make([]CgroupDockerStat, 0, len(lines))
for _, l := range lines {
if l == "" {
continue
}
cols := strings.Split(l, "|")
if len(cols) != 4 {
continue
}
names := strings.Split(cols[2], ",")
stat := CgroupDockerStat{
ContainerID: cols[0],
Name: names[0],
Image: cols[1],
Status: cols[3],
Running: strings.Contains(cols[3], "Up"),
}
ret = append(ret, stat)
}
return ret, nil
}
func (c CgroupDockerStat) String() string {
s, _ := json.Marshal(c)
return string(s)
}
2014-06-10 13:38:01 +08:00
// GetDockerIDList returnes a list of DockerID.
// This requires certain permission.
func GetDockerIDList() ([]string, error) {
2015-07-23 10:24:45 +08:00
path, err := exec.LookPath("docker")
if err != nil {
return nil, ErrDockerNotAvailable
}
out, err := invoke.Command(path, "ps", "-q", "--no-trunc")
2014-06-10 13:38:01 +08:00
if err != nil {
return []string{}, err
}
lines := strings.Split(string(out), "\n")
ret := make([]string, 0, len(lines))
for _, l := range lines {
2015-07-23 10:24:45 +08:00
if l == "" {
continue
}
2014-06-10 13:38:01 +08:00
ret = append(ret, l)
}
return ret, nil
}
// CgroupCPU returnes specified cgroup id CPU status.
2016-04-01 20:34:39 +08:00
// containerID is same as docker id if you use docker.
2014-06-10 13:38:01 +08:00
// If you use container via systemd.slice, you could use
2016-04-01 20:34:39 +08:00
// containerID = docker-<container id>.scope and base=/sys/fs/cgroup/cpuacct/system.slice/
func CgroupCPU(containerID string, base string) (*cpu.TimesStat, error) {
statfile := getCgroupFilePath(containerID, base, "cpuacct", "cpuacct.stat")
2015-09-22 04:29:17 +08:00
lines, err := common.ReadLines(statfile)
if err != nil {
return nil, err
}
2016-04-01 20:34:39 +08:00
// empty containerID means all cgroup
if len(containerID) == 0 {
containerID = "all"
2014-06-10 13:38:01 +08:00
}
2016-04-01 20:34:39 +08:00
ret := &cpu.TimesStat{CPU: containerID}
2014-06-10 13:38:01 +08:00
for _, line := range lines {
fields := strings.Split(line, " ")
if fields[0] == "user" {
user, err := strconv.ParseFloat(fields[1], 64)
2014-06-10 13:38:01 +08:00
if err == nil {
ret.User = float64(user)
2014-06-10 13:38:01 +08:00
}
}
if fields[0] == "system" {
system, err := strconv.ParseFloat(fields[1], 64)
2014-06-10 13:38:01 +08:00
if err == nil {
ret.System = float64(system)
2014-06-10 13:38:01 +08:00
}
}
}
return ret, nil
}
func CgroupCPUDocker(containerid string) (*cpu.TimesStat, error) {
return CgroupCPU(containerid, common.HostSys("fs/cgroup/cpuacct/docker"))
2014-06-10 13:38:01 +08:00
}
2016-04-01 20:34:39 +08:00
func CgroupMem(containerID string, base string) (*CgroupMemStat, error) {
statfile := getCgroupFilePath(containerID, base, "memory", "memory.stat")
2015-09-22 04:29:17 +08:00
2016-04-01 20:34:39 +08:00
// empty containerID means all cgroup
if len(containerID) == 0 {
containerID = "all"
2014-06-10 13:38:01 +08:00
}
2015-09-22 04:29:17 +08:00
lines, err := common.ReadLines(statfile)
if err != nil {
return nil, err
}
2016-04-01 20:34:39 +08:00
ret := &CgroupMemStat{ContainerID: containerID}
2014-06-10 13:38:01 +08:00
for _, line := range lines {
fields := strings.Split(line, " ")
v, err := strconv.ParseUint(fields[1], 10, 64)
if err != nil {
continue
}
switch fields[0] {
case "cache":
ret.Cache = v
case "rss":
ret.RSS = v
case "rssHuge":
2014-08-16 02:05:26 +08:00
ret.RSSHuge = v
case "mappedFile":
2014-08-16 02:05:26 +08:00
ret.MappedFile = v
2014-06-10 13:38:01 +08:00
case "pgpgin":
ret.Pgpgin = v
case "pgpgout":
ret.Pgpgout = v
case "pgfault":
ret.Pgfault = v
case "pgmajfault":
ret.Pgmajfault = v
case "inactiveAnon":
2014-08-16 02:05:26 +08:00
ret.InactiveAnon = v
case "activeAnon":
2014-08-16 02:05:26 +08:00
ret.ActiveAnon = v
case "inactiveFile":
2015-07-23 10:24:45 +08:00
ret.InactiveFile = v
case "activeFile":
2014-08-16 02:05:26 +08:00
ret.ActiveFile = v
2014-06-10 13:38:01 +08:00
case "unevictable":
ret.Unevictable = v
case "hierarchicalMemoryLimit":
2014-08-16 02:05:26 +08:00
ret.HierarchicalMemoryLimit = v
case "totalCache":
2014-08-16 02:05:26 +08:00
ret.TotalCache = v
case "totalRss":
2014-08-16 02:05:26 +08:00
ret.TotalRSS = v
case "totalRssHuge":
2014-08-16 02:05:26 +08:00
ret.TotalRSSHuge = v
case "totalMappedFile":
2014-08-16 02:05:26 +08:00
ret.TotalMappedFile = v
case "totalPgpgin":
2014-08-16 02:05:26 +08:00
ret.TotalPgpgIn = v
case "totalPgpgout":
2014-08-16 02:05:26 +08:00
ret.TotalPgpgOut = v
case "totalPgfault":
2014-08-16 02:05:26 +08:00
ret.TotalPgFault = v
case "totalPgmajfault":
2014-08-16 02:05:26 +08:00
ret.TotalPgMajFault = v
case "totalInactiveAnon":
2014-08-16 02:05:26 +08:00
ret.TotalInactiveAnon = v
case "totalActiveAnon":
2014-08-16 02:05:26 +08:00
ret.TotalActiveAnon = v
case "totalInactiveFile":
2014-08-16 02:05:26 +08:00
ret.TotalInactiveFile = v
case "totalActiveFile":
2014-08-16 02:05:26 +08:00
ret.TotalActiveFile = v
case "totalUnevictable":
2014-08-16 02:05:26 +08:00
ret.TotalUnevictable = v
2014-06-10 13:38:01 +08:00
}
}
2016-04-01 20:34:39 +08:00
r, err := getCgroupMemFile(containerID, base, "memory.usage_in_bytes")
if err == nil {
ret.MemUsageInBytes = r
}
2016-04-01 20:34:39 +08:00
r, err = getCgroupMemFile(containerID, base, "memory.max_usage_in_bytes")
if err == nil {
ret.MemMaxUsageInBytes = r
}
2016-04-01 20:34:39 +08:00
r, err = getCgroupMemFile(containerID, base, "memoryLimitInBbytes")
if err == nil {
ret.MemLimitInBytes = r
}
2016-04-01 20:34:39 +08:00
r, err = getCgroupMemFile(containerID, base, "memoryFailcnt")
if err == nil {
ret.MemFailCnt = r
}
2014-06-10 13:38:01 +08:00
return ret, nil
}
2016-04-01 20:34:39 +08:00
func CgroupMemDocker(containerID string) (*CgroupMemStat, error) {
return CgroupMem(containerID, common.HostSys("fs/cgroup/memory/docker"))
2014-06-10 13:38:01 +08:00
}
func (m CgroupMemStat) String() string {
s, _ := json.Marshal(m)
return string(s)
}
// getCgroupFilePath constructs file path to get targetted stats file.
2016-04-01 20:34:39 +08:00
func getCgroupFilePath(containerID, base, target, file string) string {
if len(base) == 0 {
base = common.HostSys(fmt.Sprintf("fs/cgroup/%s/docker", target))
}
2016-04-01 20:34:39 +08:00
statfile := path.Join(base, containerID, file)
if _, err := os.Stat(statfile); os.IsNotExist(err) {
statfile = path.Join(
2016-04-01 20:34:39 +08:00
common.HostSys(fmt.Sprintf("fs/cgroup/%s/system.slice", target)), "docker-"+containerID+".scope", file)
}
return statfile
}
// getCgroupMemFile reads a cgroup file and return the contents as uint64.
2016-04-01 20:34:39 +08:00
func getCgroupMemFile(containerID, base, file string) (uint64, error) {
statfile := getCgroupFilePath(containerID, base, "memory", file)
lines, err := common.ReadLines(statfile)
if err != nil {
return 0, err
}
if len(lines) != 1 {
return 0, fmt.Errorf("wrong format file: %s", statfile)
}
return strconv.ParseUint(lines[0], 10, 64)
}