From 9ce676d9c7bc13ae4fdb77c1808c4cda41e849da Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Tue, 10 Jun 2014 14:38:01 +0900 Subject: [PATCH] add cgroup functions on Linux. --- README.rst | 13 +++ docker_linux.go | 184 +++++++++++++++++++++++++++++++++++++++++++ docker_linux_test.go | 44 +++++++++++ 3 files changed, 241 insertions(+) create mode 100644 docker_linux.go create mode 100644 docker_linux_test.go diff --git a/README.rst b/README.rst index bf925d0..25ee3bd 100644 --- a/README.rst +++ b/README.rst @@ -89,6 +89,19 @@ To becomes more useful, add more information. - Load5 - Load15 +- GetDockerIDList() (linux) + + - container id list ([]string) + +- CgroupCPU() (linux) + + - user + - system + +- CgroupMem() (linux) + + - various status + Some codes are ported some functions from Ohai. very thanks. diff --git a/docker_linux.go b/docker_linux.go new file mode 100644 index 0000000..370da6a --- /dev/null +++ b/docker_linux.go @@ -0,0 +1,184 @@ +// +build linux + +package gopsutil + +import ( + "encoding/json" + "os/exec" + "path" + "strconv" + "strings" +) + +type CgroupMemStat struct { + ContainerID string `json:"containerid"` + Cache uint64 `json:"cache"` + RSS uint64 `json:"rss"` + Rss_huge uint64 `json:"rss_huge"` + Mapped_file uint64 `json:"mapped_file"` + Pgpgin uint64 `json:"pgpgin"` + Pgpgout uint64 `json:"pgpgout"` + Pgfault uint64 `json:"pgfault"` + Pgmajfault uint64 `json:"pgmajfault"` + Inactive_anon uint64 `json:"inactive_anon"` + Active_anon uint64 `json:"active_anon"` + Inactive_file uint64 `json:"inactive_file"` + Active_file uint64 `json:"active_file"` + Unevictable uint64 `json:"unevictable"` + Hierarchical_memory_limit uint64 `json:"hierarchical_memory_limit"` + Total_cache uint64 `json:"total_cache"` + Total_rss uint64 `json:"total_rss"` + Total_rss_huge uint64 `json:"total_rss_huge"` + Total_mapped_file uint64 `json:"total_mapped_file"` + Total_pgpgin uint64 `json:"total_pgpgin"` + Total_pgpgout uint64 `json:"total_pgpgout"` + Total_pgfault uint64 `json:"total_pgfault"` + Total_pgmajfault uint64 `json:"total_pgmajfault"` + Total_inactive_anon uint64 `json:"total_inactive_anon"` + Total_active_anon uint64 `json:"total_active_anon"` + Total_inactive_file uint64 `json:"total_inactive_file"` + Total_active_file uint64 `json:"total_active_file"` + Total_unevictable uint64 `json:"total_unevictable"` +} + +// GetDockerIDList returnes a list of DockerID. +// This requires certain permission. +func GetDockerIDList() ([]string, error) { + out, err := exec.Command("docker", "ps", "-q", "--no-trunc").Output() + if err != nil { + return []string{}, err + } + lines := strings.Split(string(out), "\n") + ret := make([]string, 0, len(lines)) + + for _, l := range lines { + ret = append(ret, l) + } + + return ret, nil +} + +// CgroupCPU returnes specified cgroup id CPU status. +// containerid is same as docker id if you use docker. +// If you use container via systemd.slice, you could use +// containerid = docker-.scope and base=/sys/fs/cgroup/cpuacct/system.slice/ +func CgroupCPU(containerid string, base string) (*CPUTimesStat, error) { + if len(base) == 0 { + base = "/sys/fs/cgroup/cpuacct/docker" + } + path := path.Join(base, containerid, "cpuacct.stat") + + lines, _ := readLines(path) + // empty containerid means all cgroup + if len(containerid) == 0 { + containerid = "all" + } + ret := &CPUTimesStat{CPU: containerid} + for _, line := range lines { + fields := strings.Split(line, " ") + if fields[0] == "user" { + user, err := strconv.ParseFloat(fields[1], 32) + if err == nil { + ret.User = float32(user) + } + } + if fields[0] == "system" { + system, err := strconv.ParseFloat(fields[1], 32) + if err == nil { + ret.System = float32(system) + } + } + } + + return ret, nil +} + +func CgroupCPUDocker(containerid string) (*CPUTimesStat, error) { + return CgroupCPU(containerid, "/sys/fs/cgroup/cpuacct/docker") +} + +func CgroupMem(containerid string, base string) (*CgroupMemStat, error) { + if len(base) == 0 { + base = "/sys/fs/cgroup/memory/docker" + } + path := path.Join(base, containerid, "memory.stat") + // empty containerid means all cgroup + if len(containerid) == 0 { + containerid = "all" + } + lines, _ := readLines(path) + ret := &CgroupMemStat{ContainerID: containerid} + 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 "rss_huge": + ret.Rss_huge = v + case "mapped_file": + ret.Mapped_file = v + case "pgpgin": + ret.Pgpgin = v + case "pgpgout": + ret.Pgpgout = v + case "pgfault": + ret.Pgfault = v + case "pgmajfault": + ret.Pgmajfault = v + case "inactive_anon": + ret.Inactive_anon = v + case "active_anon": + ret.Active_anon = v + case "inactive_file": + ret.Inactive_file = v + case "active_file": + ret.Active_file = v + case "unevictable": + ret.Unevictable = v + case "hierarchical_memory_limit": + ret.Hierarchical_memory_limit = v + case "total_cache": + ret.Total_cache = v + case "total_rss": + ret.Total_rss = v + case "total_rss_huge": + ret.Total_rss_huge = v + case "total_mapped_file": + ret.Total_mapped_file = v + case "total_pgpgin": + ret.Total_pgpgin = v + case "total_pgpgout": + ret.Total_pgpgout = v + case "total_pgfault": + ret.Total_pgfault = v + case "total_pgmajfault": + ret.Total_pgmajfault = v + case "total_inactive_anon": + ret.Total_inactive_anon = v + case "total_active_anon": + ret.Total_active_anon = v + case "total_inactive_file": + ret.Total_inactive_file = v + case "total_active_file": + ret.Total_active_file = v + case "total_unevictable": + ret.Total_unevictable = v + } + } + return ret, nil +} + +func CgroupMemDocker(containerid string) (*CgroupMemStat, error) { + return CgroupMem(containerid, "/sys/fs/cgroup/memory/docker") +} + +func (m CgroupMemStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} diff --git a/docker_linux_test.go b/docker_linux_test.go new file mode 100644 index 0000000..45ff865 --- /dev/null +++ b/docker_linux_test.go @@ -0,0 +1,44 @@ +// +build linux + +package gopsutil + +import ( + "testing" +) + +func TestGetDockerIDList(t *testing.T) { + _, err := GetDockerIDList() + if err != nil { + t.Errorf("error %v", err) + } + // If there is not docker environment, this test always fail. + // not tested here +} + +func TestCgroupCPU(t *testing.T) { + v, _ := GetDockerIDList() + for _, id := range v { + v, err := CgroupCPUDocker(id) + if err != nil { + t.Errorf("error %v", err) + } + if v.CPU == "" { + t.Errorf("could not get CgroupCPU %v", v) + } + + } +} + +func TestCgroupMem(t *testing.T) { + v, _ := GetDockerIDList() + for _, id := range v { + v, err := CgroupMemDocker(id) + if err != nil { + t.Errorf("error %v", err) + } + empty := &CgroupMemStat{} + if v == empty { + t.Errorf("Could not CgroupMemStat %v", v) + } + } +} -- GitLab