diff --git a/cgroups/fs/blkio.go b/cgroups/fs/blkio.go index 48dcf1a..fae8d5a 100644 --- a/cgroups/fs/blkio.go +++ b/cgroups/fs/blkio.go @@ -1,6 +1,11 @@ package fs import ( + "bufio" + "fmt" + "os" + "path/filepath" + "github.com/dotcloud/docker/pkg/cgroups" ) @@ -18,3 +23,34 @@ func (s *blkioGroup) Set(d *data) error { func (s *blkioGroup) Remove(d *data) error { return removePath(d.path("blkio")) } + +func (s *blkioGroup) Stats(d *data) (map[string]float64, error) { + paramData := make(map[string]float64) + path, err := d.path("blkio") + if err != nil { + return paramData, fmt.Errorf("Unable to read %s cgroup param: %s", path, err) + } + params := []string{ + "sectors", + "io_service_bytes", + "io_serviced", + "io_queued", + } + for _, param := range params { + p := fmt.Sprintf("blkio.%s", param) + f, err := os.Open(filepath.Join(path, p)) + if err != nil { + return paramData, err + } + defer f.Close() + sc := bufio.NewScanner(f) + for sc.Scan() { + _, v, err := getCgroupParamKeyValue(sc.Text()) + if err != nil { + return paramData, fmt.Errorf("Error parsing param data: %s", err) + } + paramData[param] = v + } + } + return paramData, nil +} diff --git a/cgroups/fs/cpuacct.go b/cgroups/fs/cpuacct.go index 22dfb41..621592e 100644 --- a/cgroups/fs/cpuacct.go +++ b/cgroups/fs/cpuacct.go @@ -1,6 +1,14 @@ package fs import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "github.com/dotcloud/docker/pkg/cgroups" ) @@ -18,3 +26,64 @@ func (s *cpuacctGroup) Set(d *data) error { func (s *cpuacctGroup) Remove(d *data) error { return removePath(d.path("cpuacct")) } + +func (s *cpuacctGroup) Stats(d *data) (map[string]float64, error) { + paramData := make(map[string]float64) + path, err := d.path("cpuacct") + if err != nil { + return paramData, fmt.Errorf("Unable to read %s cgroup param: %s", path, err) + } + f, err := os.Open(filepath.Join(path, "cpuacct.stat")) + if err != nil { + return paramData, err + } + defer f.Close() + sc := bufio.NewScanner(f) + cpuTotal := 0.0 + for sc.Scan() { + t, v, err := getCgroupParamKeyValue(sc.Text()) + if err != nil { + return paramData, fmt.Errorf("Error parsing param data: %s", err) + } + // set the raw data in map + paramData[t] = v + cpuTotal += v + } + // calculate percentage from jiffies + // get sys uptime + uf, err := os.Open("/proc/uptime") + if err != nil { + return paramData, fmt.Errorf("Unable to open /proc/uptime") + } + defer uf.Close() + uptimeData, err := ioutil.ReadAll(uf) + if err != nil { + return paramData, fmt.Errorf("Error reading /proc/uptime: %s", err) + } + uptimeFields := strings.Fields(string(uptimeData)) + uptime, err := strconv.ParseFloat(uptimeFields[0], 64) + if err != nil { + return paramData, fmt.Errorf("Error parsing cpu stats: %s", err) + } + // find starttime of process + pf, err := os.Open(filepath.Join(path, "cgroup.procs")) + if err != nil { + return paramData, fmt.Errorf("Error parsing cpu stats: %s", err) + } + defer pf.Close() + pr := bufio.NewReader(pf) + l, _, err := pr.ReadLine() + if err != nil { + return paramData, fmt.Errorf("Error reading param file: %s", err) + } + starttime, err := strconv.ParseFloat(string(l), 64) + if err != nil { + return paramData, fmt.Errorf("Unable to parse starttime: %s", err) + } + // get total elapsed seconds since proc start + seconds := uptime - (starttime / 100) + // finally calc percentage + cpuPercentage := 100.0 * ((cpuTotal / 100.0) / float64(seconds)) + paramData["percentage"] = cpuPercentage + return paramData, nil +} diff --git a/cgroups/fs/memory.go b/cgroups/fs/memory.go index a098f21..26281c1 100644 --- a/cgroups/fs/memory.go +++ b/cgroups/fs/memory.go @@ -1,7 +1,10 @@ package fs import ( + "bufio" + "fmt" "os" + "path/filepath" "strconv" ) @@ -43,3 +46,26 @@ func (s *memoryGroup) Set(d *data) error { func (s *memoryGroup) Remove(d *data) error { return removePath(d.path("memory")) } + +func (s *memoryGroup) Stats(d *data) (map[string]float64, error) { + paramData := make(map[string]float64) + path, err := d.path("memory") + if err != nil { + fmt.Errorf("Unable to read %s cgroup param: %s", path, err) + return paramData, err + } + f, err := os.Open(filepath.Join(path, "memory.stat")) + if err != nil { + return paramData, err + } + defer f.Close() + sc := bufio.NewScanner(f) + for sc.Scan() { + t, v, err := getCgroupParamKeyValue(sc.Text()) + if err != nil { + return paramData, fmt.Errorf("Error parsing param data: %s", err) + } + paramData[t] = v + } + return paramData, nil +} diff --git a/cgroups/fs/utils.go b/cgroups/fs/utils.go new file mode 100644 index 0000000..260cf13 --- /dev/null +++ b/cgroups/fs/utils.go @@ -0,0 +1,24 @@ +package fs + +import ( + "fmt" + "strconv" + "strings" +) + +// Parses a cgroup param and returns as name, value +// i.e. "io_service_bytes 1234" will return as io_service_bytes, 1234 +func getCgroupParamKeyValue(t string) (string, float64, error) { + parts := strings.Fields(t) + switch len(parts) { + case 2: + name := parts[0] + value, err := strconv.ParseFloat(parts[1], 64) + if err != nil { + return "", 0.0, fmt.Errorf("Unable to convert param value to float: %s", err) + } + return name, value, nil + default: + return "", 0.0, fmt.Errorf("Unable to parse cgroup param: not enough parts; expected 2") + } +}