Merge pull request #6096 from vishh/stats_strongtype

Replaced fs.Stats() interface with fs.GetStats() which returns a cgroups.Stats strong type
This commit is contained in:
Michael Crosby 2014-05-29 15:23:01 -07:00
commit 887ab77922
16 changed files with 329 additions and 231 deletions

View File

@ -26,7 +26,7 @@ var (
type subsystem interface {
Set(*data) error
Remove(*data) error
Stats(*data) (map[string]int64, error)
GetStats(*data, *cgroups.Stats) error
}
type data struct {
@ -74,7 +74,8 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
return d, nil
}
func GetStats(c *cgroups.Cgroup, subsystem string, pid int) (map[string]int64, error) {
func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
stats := cgroups.NewStats()
cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
if err != nil {
return nil, err
@ -94,13 +95,15 @@ func GetStats(c *cgroups.Cgroup, subsystem string, pid int) (map[string]int64, e
root: cgroupRoot,
cgroup: cgroup,
c: c,
pid: pid,
}
sys, exists := subsystems[subsystem]
if !exists {
return nil, fmt.Errorf("subsystem %s does not exist", subsystem)
for _, sys := range subsystems {
if err := sys.GetStats(d, stats); err != nil {
return nil, err
}
}
return sys.Stats(d)
return stats, nil
}
func GetPids(c *cgroups.Cgroup) ([]int, error) {

View File

@ -3,7 +3,6 @@ package fs
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
@ -57,65 +56,87 @@ examples:
8:0 Total 0
Total 0
*/
func (s *blkioGroup) Stats(d *data) (map[string]int64, error) {
var (
paramData = make(map[string]int64)
params = []string{
"io_service_bytes_recursive",
"io_serviced_recursive",
"io_queued_recursive",
}
)
path, err := d.path("blkio")
if err != nil {
return nil, err
}
k, v, err := s.getSectors(path)
if err != nil {
return nil, err
}
paramData[fmt.Sprintf("blkio.sectors_recursive:%s", k)] = v
for _, param := range params {
f, err := os.Open(filepath.Join(path, fmt.Sprintf("blkio.%s", param)))
if err != nil {
return nil, err
}
defer f.Close()
sc := bufio.NewScanner(f)
for sc.Scan() {
// format: dev type amount
fields := strings.Fields(sc.Text())
switch len(fields) {
case 3:
v, err := strconv.ParseInt(fields[2], 10, 64)
if err != nil {
return nil, err
}
paramData[fmt.Sprintf("%s:%s:%s", param, fields[0], fields[1])] = v
case 2:
// this is the total line, skip
default:
return nil, ErrNotValidFormat
}
}
}
return paramData, nil
func splitBlkioStatLine(r rune) bool {
return r == ' ' || r == ':'
}
func (s *blkioGroup) getSectors(path string) (string, int64, error) {
f, err := os.Open(filepath.Join(path, "blkio.sectors_recursive"))
func getBlkioStat(path string) ([]cgroups.BlkioStatEntry, error) {
var blkioStats []cgroups.BlkioStatEntry
f, err := os.Open(path)
if err != nil {
return "", 0, err
return nil, err
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
return "", 0, err
sc := bufio.NewScanner(f)
for sc.Scan() {
// format: dev type amount
fields := strings.FieldsFunc(sc.Text(), splitBlkioStatLine)
if len(fields) < 3 {
if len(fields) == 2 && fields[0] == "Total" {
// skip total line
continue
} else {
return nil, fmt.Errorf("Invalid line found while parsing %s: %s", path, sc.Text())
}
}
v, err := strconv.ParseUint(fields[0], 10, 64)
if err != nil {
return nil, err
}
major := v
v, err = strconv.ParseUint(fields[1], 10, 64)
if err != nil {
return nil, err
}
minor := v
op := ""
valueField := 2
if len(fields) == 4 {
op = fields[2]
valueField = 3
}
v, err = strconv.ParseUint(fields[valueField], 10, 64)
if err != nil {
return nil, err
}
blkioStats = append(blkioStats, cgroups.BlkioStatEntry{Major: major, Minor: minor, Op: op, Value: v})
}
return getCgroupParamKeyValue(string(data))
return blkioStats, nil
}
func (s *blkioGroup) GetStats(d *data, stats *cgroups.Stats) error {
var blkioStats []cgroups.BlkioStatEntry
var err error
path, err := d.path("blkio")
if err != nil {
return err
}
if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.sectors_recursive")); err != nil {
return err
}
stats.BlkioStats.SectorsRecursive = blkioStats
if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_service_bytes_recursive")); err != nil {
return err
}
stats.BlkioStats.IoServiceBytesRecursive = blkioStats
if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_serviced_recursive")); err != nil {
return err
}
stats.BlkioStats.IoServicedRecursive = blkioStats
if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.io_queued_recursive")); err != nil {
return err
}
stats.BlkioStats.IoQueuedRecursive = blkioStats
return nil
}

View File

@ -2,14 +2,16 @@ package fs
import (
"testing"
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
)
const (
sectorsRecursiveContents = `8:0 1024`
serviceBytesRecursiveContents = `8:0 Read 100
8:0 Write 400
8:0 Sync 200
8:0 Async 300
8:0 Write 200
8:0 Sync 300
8:0 Async 500
8:0 Total 500
Total 500`
servicedRecursiveContents = `8:0 Read 10
@ -26,6 +28,12 @@ Total 50`
Total 5`
)
var actualStats = *cgroups.NewStats()
func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, minor, value uint64, op string) {
*blkioStatEntries = append(*blkioStatEntries, cgroups.BlkioStatEntry{Major: major, Minor: minor, Value: value, Op: op})
}
func TestBlkioStats(t *testing.T) {
helper := NewCgroupTestUtil("blkio", t)
defer helper.cleanup()
@ -37,37 +45,34 @@ func TestBlkioStats(t *testing.T) {
})
blkio := &blkioGroup{}
stats, err := blkio.Stats(helper.CgroupData)
err := blkio.GetStats(helper.CgroupData, &actualStats)
if err != nil {
t.Fatal(err)
}
// Verify expected stats.
expectedStats := map[string]int64{
"blkio.sectors_recursive:8:0": 1024,
expectedStats := cgroups.BlkioStats{}
appendBlkioStatEntry(&expectedStats.SectorsRecursive, 8, 0, 1024, "")
// Serviced bytes.
"io_service_bytes_recursive:8:0:Read": 100,
"io_service_bytes_recursive:8:0:Write": 400,
"io_service_bytes_recursive:8:0:Sync": 200,
"io_service_bytes_recursive:8:0:Async": 300,
"io_service_bytes_recursive:8:0:Total": 500,
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 100, "Read")
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 200, "Write")
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 300, "Sync")
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Async")
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Total")
// Serviced requests.
"io_serviced_recursive:8:0:Read": 10,
"io_serviced_recursive:8:0:Write": 40,
"io_serviced_recursive:8:0:Sync": 20,
"io_serviced_recursive:8:0:Async": 30,
"io_serviced_recursive:8:0:Total": 50,
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 10, "Read")
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 40, "Write")
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 20, "Sync")
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 30, "Async")
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 50, "Total")
// Queued requests.
"io_queued_recursive:8:0:Read": 1,
"io_queued_recursive:8:0:Write": 4,
"io_queued_recursive:8:0:Sync": 2,
"io_queued_recursive:8:0:Async": 3,
"io_queued_recursive:8:0:Total": 5,
}
expectStats(t, expectedStats, stats)
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 1, "Read")
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 4, "Write")
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 2, "Sync")
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 3, "Async")
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 5, "Total")
expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats)
}
func TestBlkioStatsNoSectorsFile(t *testing.T) {
@ -80,7 +85,7 @@ func TestBlkioStatsNoSectorsFile(t *testing.T) {
})
blkio := &blkioGroup{}
_, err := blkio.Stats(helper.CgroupData)
err := blkio.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected to fail, but did not")
}
@ -96,7 +101,7 @@ func TestBlkioStatsNoServiceBytesFile(t *testing.T) {
})
blkio := &blkioGroup{}
_, err := blkio.Stats(helper.CgroupData)
err := blkio.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected to fail, but did not")
}
@ -112,7 +117,7 @@ func TestBlkioStatsNoServicedFile(t *testing.T) {
})
blkio := &blkioGroup{}
_, err := blkio.Stats(helper.CgroupData)
err := blkio.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected to fail, but did not")
}
@ -128,7 +133,7 @@ func TestBlkioStatsNoQueuedFile(t *testing.T) {
})
blkio := &blkioGroup{}
_, err := blkio.Stats(helper.CgroupData)
err := blkio.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected to fail, but did not")
}
@ -145,7 +150,7 @@ func TestBlkioStatsUnexpectedNumberOfFields(t *testing.T) {
})
blkio := &blkioGroup{}
_, err := blkio.Stats(helper.CgroupData)
err := blkio.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected to fail, but did not")
}
@ -162,7 +167,7 @@ func TestBlkioStatsUnexpectedFieldType(t *testing.T) {
})
blkio := &blkioGroup{}
_, err := blkio.Stats(helper.CgroupData)
err := blkio.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected to fail, but did not")
}

View File

@ -5,6 +5,8 @@ import (
"os"
"path/filepath"
"strconv"
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
)
type cpuGroup struct {
@ -39,16 +41,15 @@ func (s *cpuGroup) Remove(d *data) error {
return removePath(d.path("cpu"))
}
func (s *cpuGroup) Stats(d *data) (map[string]int64, error) {
paramData := make(map[string]int64)
func (s *cpuGroup) GetStats(d *data, stats *cgroups.Stats) error {
path, err := d.path("cpu")
if err != nil {
return nil, err
return err
}
f, err := os.Open(filepath.Join(path, "cpu.stat"))
if err != nil {
return nil, err
return err
}
defer f.Close()
@ -56,9 +57,18 @@ func (s *cpuGroup) Stats(d *data) (map[string]int64, error) {
for sc.Scan() {
t, v, err := getCgroupParamKeyValue(sc.Text())
if err != nil {
return nil, err
return err
}
switch t {
case "nr_periods":
stats.CpuStats.ThrottlingData.Periods = v
case "nr_throttled":
stats.CpuStats.ThrottlingData.ThrottledPeriods = v
case "throttled_time":
stats.CpuStats.ThrottlingData.ThrottledTime = v
}
paramData[t] = v
}
return paramData, nil
return nil
}

View File

@ -1,31 +1,40 @@
package fs
import (
"fmt"
"testing"
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
)
func TestCpuStats(t *testing.T) {
helper := NewCgroupTestUtil("cpu", t)
defer helper.cleanup()
cpuStatContent := `nr_periods 2000
nr_throttled 200
throttled_time 42424242424`
const (
kNrPeriods = 2000
kNrThrottled = 200
kThrottledTime = uint64(18446744073709551615)
)
cpuStatContent := fmt.Sprintf("nr_periods %d\n nr_throttled %d\n throttled_time %d\n",
kNrPeriods, kNrThrottled, kThrottledTime)
helper.writeFileContents(map[string]string{
"cpu.stat": cpuStatContent,
})
cpu := &cpuGroup{}
stats, err := cpu.Stats(helper.CgroupData)
err := cpu.GetStats(helper.CgroupData, &actualStats)
if err != nil {
t.Fatal(err)
}
expected_stats := map[string]int64{
"nr_periods": 2000,
"nr_throttled": 200,
"throttled_time": 42424242424,
}
expectStats(t, expected_stats, stats)
expectedStats := cgroups.ThrottlingData{
Periods: kNrPeriods,
ThrottledPeriods: kNrThrottled,
ThrottledTime: kThrottledTime}
expectThrottlingDataEquals(t, expectedStats, actualStats.CpuStats.ThrottlingData)
}
func TestNoCpuStatFile(t *testing.T) {
@ -33,7 +42,7 @@ func TestNoCpuStatFile(t *testing.T) {
defer helper.cleanup()
cpu := &cpuGroup{}
_, err := cpu.Stats(helper.CgroupData)
err := cpu.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected to fail, but did not.")
}
@ -50,7 +59,7 @@ func TestInvalidCpuStat(t *testing.T) {
})
cpu := &cpuGroup{}
_, err := cpu.Stats(helper.CgroupData)
err := cpu.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected failed stat parsing.")
}

View File

@ -15,8 +15,8 @@ import (
)
var (
cpuCount = int64(runtime.NumCPU())
clockTicks = int64(system.GetClockTicks())
cpuCount = uint64(runtime.NumCPU())
clockTicks = uint64(system.GetClockTicks())
)
type cpuacctGroup struct {
@ -34,34 +34,33 @@ func (s *cpuacctGroup) Remove(d *data) error {
return removePath(d.path("cpuacct"))
}
func (s *cpuacctGroup) Stats(d *data) (map[string]int64, error) {
func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
var (
startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage int64
percentage int64
paramData = make(map[string]int64)
startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage uint64
percentage uint64
)
path, err := d.path("cpuacct")
if startCpu, err = s.getCpuUsage(d, path); err != nil {
return nil, err
return err
}
if startSystem, err = s.getSystemCpuUsage(d); err != nil {
return nil, err
return err
}
startUsageTime := time.Now()
if startUsage, err = getCgroupParamInt(path, "cpuacct.usage"); err != nil {
return nil, err
return err
}
// sample for 100ms
time.Sleep(100 * time.Millisecond)
if lastCpu, err = s.getCpuUsage(d, path); err != nil {
return nil, err
return err
}
if lastSystem, err = s.getSystemCpuUsage(d); err != nil {
return nil, err
return err
}
usageSampleDuration := time.Since(startUsageTime)
if lastUsage, err = getCgroupParamInt(path, "cpuacct.usage"); err != nil {
return nil, err
return err
}
var (
@ -74,15 +73,14 @@ func (s *cpuacctGroup) Stats(d *data) (map[string]int64, error) {
}
// NOTE: a percentage over 100% is valid for POSIX because that means the
// processes is using multiple cores
paramData["percentage"] = percentage
stats.CpuStats.CpuUsage.PercentUsage = percentage
// Delta usage is in nanoseconds of CPU time so get the usage (in cores) over the sample time.
paramData["usage"] = deltaUsage / usageSampleDuration.Nanoseconds()
return paramData, nil
stats.CpuStats.CpuUsage.CurrentUsage = deltaUsage / uint64(usageSampleDuration.Nanoseconds())
return nil
}
// TODO(vmarmol): Use cgroups stats.
func (s *cpuacctGroup) getSystemCpuUsage(d *data) (int64, error) {
func (s *cpuacctGroup) getSystemCpuUsage(d *data) (uint64, error) {
f, err := os.Open("/proc/stat")
if err != nil {
@ -99,9 +97,9 @@ func (s *cpuacctGroup) getSystemCpuUsage(d *data) (int64, error) {
return 0, fmt.Errorf("invalid number of cpu fields")
}
var total int64
var total uint64
for _, i := range parts[1:8] {
v, err := strconv.ParseInt(i, 10, 64)
v, err := strconv.ParseUint(i, 10, 64)
if err != nil {
return 0.0, fmt.Errorf("Unable to convert value %s to int: %s", i, err)
}
@ -115,8 +113,8 @@ func (s *cpuacctGroup) getSystemCpuUsage(d *data) (int64, error) {
return 0, fmt.Errorf("invalid stat format")
}
func (s *cpuacctGroup) getCpuUsage(d *data, path string) (int64, error) {
cpuTotal := int64(0)
func (s *cpuacctGroup) getCpuUsage(d *data, path string) (uint64, error) {
cpuTotal := uint64(0)
f, err := os.Open(filepath.Join(path, "cpuacct.stat"))
if err != nil {
return 0, err

View File

@ -6,6 +6,8 @@ import (
"os"
"path/filepath"
"strconv"
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
)
type cpusetGroup struct {
@ -38,8 +40,8 @@ func (s *cpusetGroup) Remove(d *data) error {
return removePath(d.path("cpuset"))
}
func (s *cpusetGroup) Stats(d *data) (map[string]int64, error) {
return nil, ErrNotSupportStat
func (s *cpusetGroup) GetStats(d *data, stats *cgroups.Stats) error {
return nil
}
func (s *cpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {

View File

@ -1,5 +1,7 @@
package fs
import "github.com/dotcloud/docker/pkg/libcontainer/cgroups"
type devicesGroup struct {
}
@ -55,6 +57,6 @@ func (s *devicesGroup) Remove(d *data) error {
return removePath(d.path("devices"))
}
func (s *devicesGroup) Stats(d *data) (map[string]int64, error) {
return nil, ErrNotSupportStat
func (s *devicesGroup) GetStats(d *data, stats *cgroups.Stats) error {
return nil
}

View File

@ -1,11 +1,8 @@
package fs
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
@ -35,39 +32,25 @@ func (s *freezerGroup) Remove(d *data) error {
return removePath(d.path("freezer"))
}
func (s *freezerGroup) Stats(d *data) (map[string]int64, error) {
var (
paramData = make(map[string]int64)
params = []string{
"parent_freezing",
"self_freezing",
// comment out right now because this is string "state",
}
)
func getFreezerFileData(path string) (string, error) {
data, err := ioutil.ReadFile(path)
return strings.TrimSuffix(string(data), "\n"), err
}
func (s *freezerGroup) GetStats(d *data, stats *cgroups.Stats) error {
path, err := d.path("freezer")
if err != nil {
return nil, err
return err
}
// TODO(vmarmol): This currently outputs nothing since the output is a string, fix.
for _, param := range params {
f, err := os.Open(filepath.Join(path, fmt.Sprintf("freezer.%s", param)))
if err != nil {
return nil, err
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
v, err := strconv.ParseInt(strings.TrimSuffix(string(data), "\n"), 10, 64)
if err != nil {
return nil, err
}
paramData[param] = v
var data string
if data, err = getFreezerFileData(filepath.Join(path, "freezer.parent_freezing")); err != nil {
return err
}
return paramData, nil
stats.FreezerStats.ParentState = data
if data, err = getFreezerFileData(filepath.Join(path, "freezer.self_freezing")); err != nil {
return err
}
stats.FreezerStats.SelfState = data
return nil
}

View File

@ -2,10 +2,11 @@ package fs
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strconv"
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
)
type memoryGroup struct {
@ -50,17 +51,16 @@ func (s *memoryGroup) Remove(d *data) error {
return removePath(d.path("memory"))
}
func (s *memoryGroup) Stats(d *data) (map[string]int64, error) {
paramData := make(map[string]int64)
func (s *memoryGroup) GetStats(d *data, stats *cgroups.Stats) error {
path, err := d.path("memory")
if err != nil {
return nil, err
return err
}
// Set stats from memory.stat.
statsFile, err := os.Open(filepath.Join(path, "memory.stat"))
if err != nil {
return nil, err
return err
}
defer statsFile.Close()
@ -68,23 +68,22 @@ func (s *memoryGroup) Stats(d *data) (map[string]int64, error) {
for sc.Scan() {
t, v, err := getCgroupParamKeyValue(sc.Text())
if err != nil {
return nil, err
return err
}
paramData[t] = v
stats.MemoryStats.Stats[t] = v
}
// Set memory usage and max historical usage.
params := []string{
"usage_in_bytes",
"max_usage_in_bytes",
value, err := getCgroupParamInt(path, "memory.usage_in_bytes")
if err != nil {
return err
}
for _, param := range params {
value, err := getCgroupParamInt(path, fmt.Sprintf("memory.%s", param))
if err != nil {
return nil, err
}
paramData[param] = value
stats.MemoryStats.Usage = value
value, err = getCgroupParamInt(path, "memory.max_usage_in_bytes")
if err != nil {
return err
}
stats.MemoryStats.MaxUsage = value
return paramData, nil
return nil
}

View File

@ -2,6 +2,8 @@ package fs
import (
"testing"
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
)
const (
@ -21,12 +23,12 @@ func TestMemoryStats(t *testing.T) {
})
memory := &memoryGroup{}
stats, err := memory.Stats(helper.CgroupData)
err := memory.GetStats(helper.CgroupData, &actualStats)
if err != nil {
t.Fatal(err)
}
expectedStats := map[string]int64{"cache": 512, "rss": 1024, "usage_in_bytes": 2048, "max_usage_in_bytes": 4096}
expectStats(t, expectedStats, stats)
expectedStats := cgroups.MemoryStats{Usage: 2048, MaxUsage: 4096, Stats: map[string]uint64{"cache": 512, "rss": 1024}}
expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats)
}
func TestMemoryStatsNoStatFile(t *testing.T) {
@ -38,7 +40,7 @@ func TestMemoryStatsNoStatFile(t *testing.T) {
})
memory := &memoryGroup{}
_, err := memory.Stats(helper.CgroupData)
err := memory.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected failure")
}
@ -53,7 +55,7 @@ func TestMemoryStatsNoUsageFile(t *testing.T) {
})
memory := &memoryGroup{}
_, err := memory.Stats(helper.CgroupData)
err := memory.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected failure")
}
@ -68,7 +70,7 @@ func TestMemoryStatsNoMaxUsageFile(t *testing.T) {
})
memory := &memoryGroup{}
_, err := memory.Stats(helper.CgroupData)
err := memory.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected failure")
}
@ -84,7 +86,7 @@ func TestMemoryStatsBadStatFile(t *testing.T) {
})
memory := &memoryGroup{}
_, err := memory.Stats(helper.CgroupData)
err := memory.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected failure")
}
@ -100,7 +102,7 @@ func TestMemoryStatsBadUsageFile(t *testing.T) {
})
memory := &memoryGroup{}
_, err := memory.Stats(helper.CgroupData)
err := memory.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected failure")
}
@ -116,7 +118,7 @@ func TestMemoryStatsBadMaxUsageFile(t *testing.T) {
})
memory := &memoryGroup{}
_, err := memory.Stats(helper.CgroupData)
err := memory.GetStats(helper.CgroupData, &actualStats)
if err == nil {
t.Fatal("Expected failure")
}

View File

@ -19,6 +19,6 @@ func (s *perfEventGroup) Remove(d *data) error {
return removePath(d.path("perf_event"))
}
func (s *perfEventGroup) Stats(d *data) (map[string]int64, error) {
return nil, ErrNotSupportStat
func (s *perfEventGroup) GetStats(d *data, stats *cgroups.Stats) error {
return nil
}

View File

@ -0,0 +1,73 @@
package fs
import (
"fmt"
"log"
"testing"
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
)
func blkioStatEntryEquals(expected, actual []cgroups.BlkioStatEntry) error {
if len(expected) != len(actual) {
return fmt.Errorf("blkioStatEntries length do not match")
}
for i, expValue := range expected {
actValue := actual[i]
if expValue != actValue {
return fmt.Errorf("Expected blkio stat entry %v but found %v", expValue, actValue)
}
}
return nil
}
func expectBlkioStatsEquals(t *testing.T, expected, actual cgroups.BlkioStats) {
if err := blkioStatEntryEquals(expected.IoServiceBytesRecursive, actual.IoServiceBytesRecursive); err != nil {
log.Printf("blkio IoServiceBytesRecursive do not match - %s\n", err)
t.Fail()
}
if err := blkioStatEntryEquals(expected.IoServicedRecursive, actual.IoServicedRecursive); err != nil {
log.Printf("blkio IoServicedRecursive do not match - %s\n", err)
t.Fail()
}
if err := blkioStatEntryEquals(expected.IoQueuedRecursive, actual.IoQueuedRecursive); err != nil {
log.Printf("blkio IoQueuedRecursive do not match - %s\n", err)
t.Fail()
}
if err := blkioStatEntryEquals(expected.SectorsRecursive, actual.SectorsRecursive); err != nil {
log.Printf("blkio SectorsRecursive do not match - %s\n", err)
t.Fail()
}
}
func expectThrottlingDataEquals(t *testing.T, expected, actual cgroups.ThrottlingData) {
if expected != actual {
log.Printf("Expected throttling data %v but found %v\n", expected, actual)
t.Fail()
}
}
func expectMemoryStatEquals(t *testing.T, expected, actual cgroups.MemoryStats) {
if expected.Usage != actual.Usage {
log.Printf("Expected memory usage %d but found %d\n", expected.Usage, actual.Usage)
t.Fail()
}
if expected.MaxUsage != actual.MaxUsage {
log.Printf("Expected memory max usage %d but found %d\n", expected.MaxUsage, actual.MaxUsage)
t.Fail()
}
for key, expValue := range expected.Stats {
actValue, ok := actual.Stats[key]
if !ok {
log.Printf("Expected memory stat key %s not found\n", key)
t.Fail()
}
if expValue != actValue {
log.Printf("Expected memory stat value %d but found %d\n", expValue, actValue)
t.Fail()
}
}
}

View File

@ -8,7 +8,6 @@ package fs
import (
"fmt"
"io/ioutil"
"log"
"os"
"testing"
)
@ -59,17 +58,3 @@ func (c *cgroupTestUtil) writeFileContents(fileContents map[string]string) {
}
}
}
// Expect the specified stats.
func expectStats(t *testing.T, expected, actual map[string]int64) {
for stat, expectedValue := range expected {
actualValue, ok := actual[stat]
if !ok {
log.Printf("Expected stat %s to exist: %s", stat, actual)
t.Fail()
} else if actualValue != expectedValue {
log.Printf("Expected stats %s to have value %f but had %f instead", stat, expectedValue, actualValue)
t.Fail()
}
}
}

View File

@ -16,11 +16,11 @@ var (
// 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, int64, error) {
func getCgroupParamKeyValue(t string) (string, uint64, error) {
parts := strings.Fields(t)
switch len(parts) {
case 2:
value, err := strconv.ParseInt(parts[1], 10, 64)
value, err := strconv.ParseUint(parts[1], 10, 64)
if err != nil {
return "", 0, fmt.Errorf("Unable to convert param value to int: %s", err)
}
@ -31,10 +31,10 @@ func getCgroupParamKeyValue(t string) (string, int64, error) {
}
// Gets a single int64 value from the specified cgroup file.
func getCgroupParamInt(cgroupPath, cgroupFile string) (int64, error) {
func getCgroupParamInt(cgroupPath, cgroupFile string) (uint64, error) {
contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile))
if err != nil {
return -1, err
return 0, err
}
return strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64)
return strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64)
}

View File

@ -2,18 +2,18 @@ package cgroups
type ThrottlingData struct {
// Number of periods with throttling active
Periods int64 `json:"periods,omitempty"`
Periods uint64 `json:"periods,omitempty"`
// Number of periods when the container hit its throttling limit.
ThrottledPeriods int64 `json:"throttled_periods,omitempty"`
ThrottledPeriods uint64 `json:"throttled_periods,omitempty"`
// Aggregate time the container was throttled for in nanoseconds.
ThrottledTime int64 `json:"throttled_time,omitempty"`
ThrottledTime uint64 `json:"throttled_time,omitempty"`
}
type CpuUsage struct {
// percentage of available CPUs currently being used.
PercentUsage int64 `json:"percent_usage,omitempty"`
PercentUsage uint64 `json:"percent_usage,omitempty"`
// nanoseconds of cpu time consumed over the last 100 ms.
CurrentUsage int64 `json:"current_usage,omitempty"`
CurrentUsage uint64 `json:"current_usage,omitempty"`
}
type CpuStats struct {
@ -23,26 +23,27 @@ type CpuStats struct {
type MemoryStats struct {
// current res_counter usage for memory
Usage int64 `json:"usage,omitempty"`
Usage uint64 `json:"usage,omitempty"`
// maximum usage ever recorded.
MaxUsage int64 `json:"max_usage,omitempty"`
MaxUsage uint64 `json:"max_usage,omitempty"`
// TODO(vishh): Export these as stronger types.
// all the stats exported via memory.stat.
Stats map[string]int64 `json:"stats,omitempty"`
Stats map[string]uint64 `json:"stats,omitempty"`
}
type BlkioStatEntry struct {
Major int64 `json:"major,omitempty"`
Minor int64 `json:"minor,omitempty"`
Major uint64 `json:"major,omitempty"`
Minor uint64 `json:"minor,omitempty"`
Op string `json:"op,omitempty"`
Value int64 `json:"value,omitempty"`
Value uint64 `json:"value,omitempty"`
}
type BlockioStats struct {
type BlkioStats struct {
// number of bytes tranferred to and from the block device
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"`
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recusrive,omitempty"`
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"`
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive,omitempty"`
}
// TODO(Vishh): Remove freezer from stats since it does not logically belong in stats.
@ -54,6 +55,11 @@ type FreezerStats struct {
type Stats struct {
CpuStats CpuStats `json:"cpu_stats,omitempty"`
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
BlockioStats BlockioStats `json:"blockio_stats,omitempty"`
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
FreezerStats FreezerStats `json:"freezer_stats,omitempty"`
}
func NewStats() *Stats {
memoryStats := MemoryStats{Stats: make(map[string]uint64)}
return &Stats{MemoryStats: memoryStats}
}