mirror of https://github.com/docker/docs.git
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:
commit
887ab77922
|
@ -26,7 +26,7 @@ var (
|
||||||
type subsystem interface {
|
type subsystem interface {
|
||||||
Set(*data) error
|
Set(*data) error
|
||||||
Remove(*data) error
|
Remove(*data) error
|
||||||
Stats(*data) (map[string]int64, error)
|
GetStats(*data, *cgroups.Stats) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type data struct {
|
type data struct {
|
||||||
|
@ -74,7 +74,8 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
||||||
return d, nil
|
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")
|
cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -94,13 +95,15 @@ func GetStats(c *cgroups.Cgroup, subsystem string, pid int) (map[string]int64, e
|
||||||
root: cgroupRoot,
|
root: cgroupRoot,
|
||||||
cgroup: cgroup,
|
cgroup: cgroup,
|
||||||
c: c,
|
c: c,
|
||||||
pid: pid,
|
|
||||||
}
|
}
|
||||||
sys, exists := subsystems[subsystem]
|
|
||||||
if !exists {
|
for _, sys := range subsystems {
|
||||||
return nil, fmt.Errorf("subsystem %s does not exist", subsystem)
|
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) {
|
func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package fs
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -57,65 +56,87 @@ examples:
|
||||||
8:0 Total 0
|
8:0 Total 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")
|
func splitBlkioStatLine(r rune) bool {
|
||||||
if err != nil {
|
return r == ' ' || r == ':'
|
||||||
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 (s *blkioGroup) getSectors(path string) (string, int64, error) {
|
func getBlkioStat(path string) ([]cgroups.BlkioStatEntry, error) {
|
||||||
f, err := os.Open(filepath.Join(path, "blkio.sectors_recursive"))
|
var blkioStats []cgroups.BlkioStatEntry
|
||||||
|
f, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(f)
|
sc := bufio.NewScanner(f)
|
||||||
if err != nil {
|
for sc.Scan() {
|
||||||
return "", 0, err
|
// 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,16 @@ package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sectorsRecursiveContents = `8:0 1024`
|
sectorsRecursiveContents = `8:0 1024`
|
||||||
serviceBytesRecursiveContents = `8:0 Read 100
|
serviceBytesRecursiveContents = `8:0 Read 100
|
||||||
8:0 Write 400
|
8:0 Write 200
|
||||||
8:0 Sync 200
|
8:0 Sync 300
|
||||||
8:0 Async 300
|
8:0 Async 500
|
||||||
8:0 Total 500
|
8:0 Total 500
|
||||||
Total 500`
|
Total 500`
|
||||||
servicedRecursiveContents = `8:0 Read 10
|
servicedRecursiveContents = `8:0 Read 10
|
||||||
|
@ -26,6 +28,12 @@ Total 50`
|
||||||
Total 5`
|
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) {
|
func TestBlkioStats(t *testing.T) {
|
||||||
helper := NewCgroupTestUtil("blkio", t)
|
helper := NewCgroupTestUtil("blkio", t)
|
||||||
defer helper.cleanup()
|
defer helper.cleanup()
|
||||||
|
@ -37,37 +45,34 @@ func TestBlkioStats(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
blkio := &blkioGroup{}
|
blkio := &blkioGroup{}
|
||||||
stats, err := blkio.Stats(helper.CgroupData)
|
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify expected stats.
|
// Verify expected stats.
|
||||||
expectedStats := map[string]int64{
|
expectedStats := cgroups.BlkioStats{}
|
||||||
"blkio.sectors_recursive:8:0": 1024,
|
appendBlkioStatEntry(&expectedStats.SectorsRecursive, 8, 0, 1024, "")
|
||||||
|
|
||||||
// Serviced bytes.
|
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 100, "Read")
|
||||||
"io_service_bytes_recursive:8:0:Read": 100,
|
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 200, "Write")
|
||||||
"io_service_bytes_recursive:8:0:Write": 400,
|
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 300, "Sync")
|
||||||
"io_service_bytes_recursive:8:0:Sync": 200,
|
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Async")
|
||||||
"io_service_bytes_recursive:8:0:Async": 300,
|
appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Total")
|
||||||
"io_service_bytes_recursive:8:0:Total": 500,
|
|
||||||
|
|
||||||
// Serviced requests.
|
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 10, "Read")
|
||||||
"io_serviced_recursive:8:0:Read": 10,
|
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 40, "Write")
|
||||||
"io_serviced_recursive:8:0:Write": 40,
|
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 20, "Sync")
|
||||||
"io_serviced_recursive:8:0:Sync": 20,
|
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 30, "Async")
|
||||||
"io_serviced_recursive:8:0:Async": 30,
|
appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 50, "Total")
|
||||||
"io_serviced_recursive:8:0:Total": 50,
|
|
||||||
|
|
||||||
// Queued requests.
|
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 1, "Read")
|
||||||
"io_queued_recursive:8:0:Read": 1,
|
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 4, "Write")
|
||||||
"io_queued_recursive:8:0:Write": 4,
|
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 2, "Sync")
|
||||||
"io_queued_recursive:8:0:Sync": 2,
|
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 3, "Async")
|
||||||
"io_queued_recursive:8:0:Async": 3,
|
appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 5, "Total")
|
||||||
"io_queued_recursive:8:0:Total": 5,
|
|
||||||
}
|
expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats)
|
||||||
expectStats(t, expectedStats, stats)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlkioStatsNoSectorsFile(t *testing.T) {
|
func TestBlkioStatsNoSectorsFile(t *testing.T) {
|
||||||
|
@ -80,7 +85,7 @@ func TestBlkioStatsNoSectorsFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
blkio := &blkioGroup{}
|
blkio := &blkioGroup{}
|
||||||
_, err := blkio.Stats(helper.CgroupData)
|
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected to fail, but did not")
|
t.Fatal("Expected to fail, but did not")
|
||||||
}
|
}
|
||||||
|
@ -96,7 +101,7 @@ func TestBlkioStatsNoServiceBytesFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
blkio := &blkioGroup{}
|
blkio := &blkioGroup{}
|
||||||
_, err := blkio.Stats(helper.CgroupData)
|
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected to fail, but did not")
|
t.Fatal("Expected to fail, but did not")
|
||||||
}
|
}
|
||||||
|
@ -112,7 +117,7 @@ func TestBlkioStatsNoServicedFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
blkio := &blkioGroup{}
|
blkio := &blkioGroup{}
|
||||||
_, err := blkio.Stats(helper.CgroupData)
|
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected to fail, but did not")
|
t.Fatal("Expected to fail, but did not")
|
||||||
}
|
}
|
||||||
|
@ -128,7 +133,7 @@ func TestBlkioStatsNoQueuedFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
blkio := &blkioGroup{}
|
blkio := &blkioGroup{}
|
||||||
_, err := blkio.Stats(helper.CgroupData)
|
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected to fail, but did not")
|
t.Fatal("Expected to fail, but did not")
|
||||||
}
|
}
|
||||||
|
@ -145,7 +150,7 @@ func TestBlkioStatsUnexpectedNumberOfFields(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
blkio := &blkioGroup{}
|
blkio := &blkioGroup{}
|
||||||
_, err := blkio.Stats(helper.CgroupData)
|
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected to fail, but did not")
|
t.Fatal("Expected to fail, but did not")
|
||||||
}
|
}
|
||||||
|
@ -162,7 +167,7 @@ func TestBlkioStatsUnexpectedFieldType(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
blkio := &blkioGroup{}
|
blkio := &blkioGroup{}
|
||||||
_, err := blkio.Stats(helper.CgroupData)
|
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected to fail, but did not")
|
t.Fatal("Expected to fail, but did not")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cpuGroup struct {
|
type cpuGroup struct {
|
||||||
|
@ -39,16 +41,15 @@ func (s *cpuGroup) Remove(d *data) error {
|
||||||
return removePath(d.path("cpu"))
|
return removePath(d.path("cpu"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *cpuGroup) Stats(d *data) (map[string]int64, error) {
|
func (s *cpuGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
paramData := make(map[string]int64)
|
|
||||||
path, err := d.path("cpu")
|
path, err := d.path("cpu")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.Open(filepath.Join(path, "cpu.stat"))
|
f, err := os.Open(filepath.Join(path, "cpu.stat"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
|
@ -56,9 +57,18 @@ func (s *cpuGroup) Stats(d *data) (map[string]int64, error) {
|
||||||
for sc.Scan() {
|
for sc.Scan() {
|
||||||
t, v, err := getCgroupParamKeyValue(sc.Text())
|
t, v, err := getCgroupParamKeyValue(sc.Text())
|
||||||
if err != nil {
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,40 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCpuStats(t *testing.T) {
|
func TestCpuStats(t *testing.T) {
|
||||||
helper := NewCgroupTestUtil("cpu", t)
|
helper := NewCgroupTestUtil("cpu", t)
|
||||||
defer helper.cleanup()
|
defer helper.cleanup()
|
||||||
cpuStatContent := `nr_periods 2000
|
|
||||||
nr_throttled 200
|
const (
|
||||||
throttled_time 42424242424`
|
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{
|
helper.writeFileContents(map[string]string{
|
||||||
"cpu.stat": cpuStatContent,
|
"cpu.stat": cpuStatContent,
|
||||||
})
|
})
|
||||||
|
|
||||||
cpu := &cpuGroup{}
|
cpu := &cpuGroup{}
|
||||||
stats, err := cpu.Stats(helper.CgroupData)
|
err := cpu.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected_stats := map[string]int64{
|
expectedStats := cgroups.ThrottlingData{
|
||||||
"nr_periods": 2000,
|
Periods: kNrPeriods,
|
||||||
"nr_throttled": 200,
|
ThrottledPeriods: kNrThrottled,
|
||||||
"throttled_time": 42424242424,
|
ThrottledTime: kThrottledTime}
|
||||||
}
|
|
||||||
expectStats(t, expected_stats, stats)
|
expectThrottlingDataEquals(t, expectedStats, actualStats.CpuStats.ThrottlingData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNoCpuStatFile(t *testing.T) {
|
func TestNoCpuStatFile(t *testing.T) {
|
||||||
|
@ -33,7 +42,7 @@ func TestNoCpuStatFile(t *testing.T) {
|
||||||
defer helper.cleanup()
|
defer helper.cleanup()
|
||||||
|
|
||||||
cpu := &cpuGroup{}
|
cpu := &cpuGroup{}
|
||||||
_, err := cpu.Stats(helper.CgroupData)
|
err := cpu.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected to fail, but did not.")
|
t.Fatal("Expected to fail, but did not.")
|
||||||
}
|
}
|
||||||
|
@ -50,7 +59,7 @@ func TestInvalidCpuStat(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
cpu := &cpuGroup{}
|
cpu := &cpuGroup{}
|
||||||
_, err := cpu.Stats(helper.CgroupData)
|
err := cpu.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected failed stat parsing.")
|
t.Fatal("Expected failed stat parsing.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cpuCount = int64(runtime.NumCPU())
|
cpuCount = uint64(runtime.NumCPU())
|
||||||
clockTicks = int64(system.GetClockTicks())
|
clockTicks = uint64(system.GetClockTicks())
|
||||||
)
|
)
|
||||||
|
|
||||||
type cpuacctGroup struct {
|
type cpuacctGroup struct {
|
||||||
|
@ -34,34 +34,33 @@ func (s *cpuacctGroup) Remove(d *data) error {
|
||||||
return removePath(d.path("cpuacct"))
|
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 (
|
var (
|
||||||
startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage int64
|
startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage uint64
|
||||||
percentage int64
|
percentage uint64
|
||||||
paramData = make(map[string]int64)
|
|
||||||
)
|
)
|
||||||
path, err := d.path("cpuacct")
|
path, err := d.path("cpuacct")
|
||||||
if startCpu, err = s.getCpuUsage(d, path); err != nil {
|
if startCpu, err = s.getCpuUsage(d, path); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
if startSystem, err = s.getSystemCpuUsage(d); err != nil {
|
if startSystem, err = s.getSystemCpuUsage(d); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
startUsageTime := time.Now()
|
startUsageTime := time.Now()
|
||||||
if startUsage, err = getCgroupParamInt(path, "cpuacct.usage"); err != nil {
|
if startUsage, err = getCgroupParamInt(path, "cpuacct.usage"); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
// sample for 100ms
|
// sample for 100ms
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
if lastCpu, err = s.getCpuUsage(d, path); err != nil {
|
if lastCpu, err = s.getCpuUsage(d, path); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
if lastSystem, err = s.getSystemCpuUsage(d); err != nil {
|
if lastSystem, err = s.getSystemCpuUsage(d); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
usageSampleDuration := time.Since(startUsageTime)
|
usageSampleDuration := time.Since(startUsageTime)
|
||||||
if lastUsage, err = getCgroupParamInt(path, "cpuacct.usage"); err != nil {
|
if lastUsage, err = getCgroupParamInt(path, "cpuacct.usage"); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
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
|
// NOTE: a percentage over 100% is valid for POSIX because that means the
|
||||||
// processes is using multiple cores
|
// 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.
|
// Delta usage is in nanoseconds of CPU time so get the usage (in cores) over the sample time.
|
||||||
paramData["usage"] = deltaUsage / usageSampleDuration.Nanoseconds()
|
stats.CpuStats.CpuUsage.CurrentUsage = deltaUsage / uint64(usageSampleDuration.Nanoseconds())
|
||||||
return paramData, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(vmarmol): Use cgroups stats.
|
// 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")
|
f, err := os.Open("/proc/stat")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -99,9 +97,9 @@ func (s *cpuacctGroup) getSystemCpuUsage(d *data) (int64, error) {
|
||||||
return 0, fmt.Errorf("invalid number of cpu fields")
|
return 0, fmt.Errorf("invalid number of cpu fields")
|
||||||
}
|
}
|
||||||
|
|
||||||
var total int64
|
var total uint64
|
||||||
for _, i := range parts[1:8] {
|
for _, i := range parts[1:8] {
|
||||||
v, err := strconv.ParseInt(i, 10, 64)
|
v, err := strconv.ParseUint(i, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0.0, fmt.Errorf("Unable to convert value %s to int: %s", i, err)
|
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")
|
return 0, fmt.Errorf("invalid stat format")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *cpuacctGroup) getCpuUsage(d *data, path string) (int64, error) {
|
func (s *cpuacctGroup) getCpuUsage(d *data, path string) (uint64, error) {
|
||||||
cpuTotal := int64(0)
|
cpuTotal := uint64(0)
|
||||||
f, err := os.Open(filepath.Join(path, "cpuacct.stat"))
|
f, err := os.Open(filepath.Join(path, "cpuacct.stat"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cpusetGroup struct {
|
type cpusetGroup struct {
|
||||||
|
@ -38,8 +40,8 @@ func (s *cpusetGroup) Remove(d *data) error {
|
||||||
return removePath(d.path("cpuset"))
|
return removePath(d.path("cpuset"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *cpusetGroup) Stats(d *data) (map[string]int64, error) {
|
func (s *cpusetGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
return nil, ErrNotSupportStat
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *cpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
|
func (s *cpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
import "github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
||||||
|
|
||||||
type devicesGroup struct {
|
type devicesGroup struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +57,6 @@ func (s *devicesGroup) Remove(d *data) error {
|
||||||
return removePath(d.path("devices"))
|
return removePath(d.path("devices"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *devicesGroup) Stats(d *data) (map[string]int64, error) {
|
func (s *devicesGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
return nil, ErrNotSupportStat
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
||||||
|
@ -35,39 +32,25 @@ func (s *freezerGroup) Remove(d *data) error {
|
||||||
return removePath(d.path("freezer"))
|
return removePath(d.path("freezer"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *freezerGroup) Stats(d *data) (map[string]int64, error) {
|
func getFreezerFileData(path string) (string, error) {
|
||||||
var (
|
data, err := ioutil.ReadFile(path)
|
||||||
paramData = make(map[string]int64)
|
return strings.TrimSuffix(string(data), "\n"), err
|
||||||
params = []string{
|
}
|
||||||
"parent_freezing",
|
|
||||||
"self_freezing",
|
|
||||||
// comment out right now because this is string "state",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
func (s *freezerGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
path, err := d.path("freezer")
|
path, err := d.path("freezer")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
var data string
|
||||||
// TODO(vmarmol): This currently outputs nothing since the output is a string, fix.
|
if data, err = getFreezerFileData(filepath.Join(path, "freezer.parent_freezing")); err != nil {
|
||||||
for _, param := range params {
|
return err
|
||||||
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
|
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,11 @@ package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
||||||
)
|
)
|
||||||
|
|
||||||
type memoryGroup struct {
|
type memoryGroup struct {
|
||||||
|
@ -50,17 +51,16 @@ func (s *memoryGroup) Remove(d *data) error {
|
||||||
return removePath(d.path("memory"))
|
return removePath(d.path("memory"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *memoryGroup) Stats(d *data) (map[string]int64, error) {
|
func (s *memoryGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
paramData := make(map[string]int64)
|
|
||||||
path, err := d.path("memory")
|
path, err := d.path("memory")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set stats from memory.stat.
|
// Set stats from memory.stat.
|
||||||
statsFile, err := os.Open(filepath.Join(path, "memory.stat"))
|
statsFile, err := os.Open(filepath.Join(path, "memory.stat"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
defer statsFile.Close()
|
defer statsFile.Close()
|
||||||
|
|
||||||
|
@ -68,23 +68,22 @@ func (s *memoryGroup) Stats(d *data) (map[string]int64, error) {
|
||||||
for sc.Scan() {
|
for sc.Scan() {
|
||||||
t, v, err := getCgroupParamKeyValue(sc.Text())
|
t, v, err := getCgroupParamKeyValue(sc.Text())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
paramData[t] = v
|
stats.MemoryStats.Stats[t] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set memory usage and max historical usage.
|
// Set memory usage and max historical usage.
|
||||||
params := []string{
|
value, err := getCgroupParamInt(path, "memory.usage_in_bytes")
|
||||||
"usage_in_bytes",
|
if err != nil {
|
||||||
"max_usage_in_bytes",
|
return err
|
||||||
}
|
}
|
||||||
for _, param := range params {
|
stats.MemoryStats.Usage = value
|
||||||
value, err := getCgroupParamInt(path, fmt.Sprintf("memory.%s", param))
|
value, err = getCgroupParamInt(path, "memory.max_usage_in_bytes")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
|
||||||
paramData[param] = value
|
|
||||||
}
|
}
|
||||||
|
stats.MemoryStats.MaxUsage = value
|
||||||
|
|
||||||
return paramData, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/cgroups"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -21,12 +23,12 @@ func TestMemoryStats(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
memory := &memoryGroup{}
|
memory := &memoryGroup{}
|
||||||
stats, err := memory.Stats(helper.CgroupData)
|
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
expectedStats := map[string]int64{"cache": 512, "rss": 1024, "usage_in_bytes": 2048, "max_usage_in_bytes": 4096}
|
expectedStats := cgroups.MemoryStats{Usage: 2048, MaxUsage: 4096, Stats: map[string]uint64{"cache": 512, "rss": 1024}}
|
||||||
expectStats(t, expectedStats, stats)
|
expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMemoryStatsNoStatFile(t *testing.T) {
|
func TestMemoryStatsNoStatFile(t *testing.T) {
|
||||||
|
@ -38,7 +40,7 @@ func TestMemoryStatsNoStatFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
memory := &memoryGroup{}
|
memory := &memoryGroup{}
|
||||||
_, err := memory.Stats(helper.CgroupData)
|
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected failure")
|
t.Fatal("Expected failure")
|
||||||
}
|
}
|
||||||
|
@ -53,7 +55,7 @@ func TestMemoryStatsNoUsageFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
memory := &memoryGroup{}
|
memory := &memoryGroup{}
|
||||||
_, err := memory.Stats(helper.CgroupData)
|
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected failure")
|
t.Fatal("Expected failure")
|
||||||
}
|
}
|
||||||
|
@ -68,7 +70,7 @@ func TestMemoryStatsNoMaxUsageFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
memory := &memoryGroup{}
|
memory := &memoryGroup{}
|
||||||
_, err := memory.Stats(helper.CgroupData)
|
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected failure")
|
t.Fatal("Expected failure")
|
||||||
}
|
}
|
||||||
|
@ -84,7 +86,7 @@ func TestMemoryStatsBadStatFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
memory := &memoryGroup{}
|
memory := &memoryGroup{}
|
||||||
_, err := memory.Stats(helper.CgroupData)
|
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected failure")
|
t.Fatal("Expected failure")
|
||||||
}
|
}
|
||||||
|
@ -100,7 +102,7 @@ func TestMemoryStatsBadUsageFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
memory := &memoryGroup{}
|
memory := &memoryGroup{}
|
||||||
_, err := memory.Stats(helper.CgroupData)
|
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected failure")
|
t.Fatal("Expected failure")
|
||||||
}
|
}
|
||||||
|
@ -116,7 +118,7 @@ func TestMemoryStatsBadMaxUsageFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
memory := &memoryGroup{}
|
memory := &memoryGroup{}
|
||||||
_, err := memory.Stats(helper.CgroupData)
|
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected failure")
|
t.Fatal("Expected failure")
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,6 @@ func (s *perfEventGroup) Remove(d *data) error {
|
||||||
return removePath(d.path("perf_event"))
|
return removePath(d.path("perf_event"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *perfEventGroup) Stats(d *data) (map[string]int64, error) {
|
func (s *perfEventGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||||
return nil, ErrNotSupportStat
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ package fs
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,11 +16,11 @@ var (
|
||||||
|
|
||||||
// Parses a cgroup param and returns as name, value
|
// Parses a cgroup param and returns as name, value
|
||||||
// i.e. "io_service_bytes 1234" will return as io_service_bytes, 1234
|
// 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)
|
parts := strings.Fields(t)
|
||||||
switch len(parts) {
|
switch len(parts) {
|
||||||
case 2:
|
case 2:
|
||||||
value, err := strconv.ParseInt(parts[1], 10, 64)
|
value, err := strconv.ParseUint(parts[1], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, fmt.Errorf("Unable to convert param value to int: %s", err)
|
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.
|
// 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))
|
contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile))
|
||||||
if err != nil {
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,18 @@ package cgroups
|
||||||
|
|
||||||
type ThrottlingData struct {
|
type ThrottlingData struct {
|
||||||
// Number of periods with throttling active
|
// 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.
|
// 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.
|
// Aggregate time the container was throttled for in nanoseconds.
|
||||||
ThrottledTime int64 `json:"throttled_time,omitempty"`
|
ThrottledTime uint64 `json:"throttled_time,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CpuUsage struct {
|
type CpuUsage struct {
|
||||||
// percentage of available CPUs currently being used.
|
// 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.
|
// 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 {
|
type CpuStats struct {
|
||||||
|
@ -23,26 +23,27 @@ type CpuStats struct {
|
||||||
|
|
||||||
type MemoryStats struct {
|
type MemoryStats struct {
|
||||||
// current res_counter usage for memory
|
// current res_counter usage for memory
|
||||||
Usage int64 `json:"usage,omitempty"`
|
Usage uint64 `json:"usage,omitempty"`
|
||||||
// maximum usage ever recorded.
|
// maximum usage ever recorded.
|
||||||
MaxUsage int64 `json:"max_usage,omitempty"`
|
MaxUsage uint64 `json:"max_usage,omitempty"`
|
||||||
// TODO(vishh): Export these as stronger types.
|
// TODO(vishh): Export these as stronger types.
|
||||||
// all the stats exported via memory.stat.
|
// all the stats exported via memory.stat.
|
||||||
Stats map[string]int64 `json:"stats,omitempty"`
|
Stats map[string]uint64 `json:"stats,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlkioStatEntry struct {
|
type BlkioStatEntry struct {
|
||||||
Major int64 `json:"major,omitempty"`
|
Major uint64 `json:"major,omitempty"`
|
||||||
Minor int64 `json:"minor,omitempty"`
|
Minor uint64 `json:"minor,omitempty"`
|
||||||
Op string `json:"op,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
|
// number of bytes tranferred to and from the block device
|
||||||
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"`
|
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"`
|
||||||
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recusrive,omitempty"`
|
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recusrive,omitempty"`
|
||||||
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,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.
|
// TODO(Vishh): Remove freezer from stats since it does not logically belong in stats.
|
||||||
|
@ -54,6 +55,11 @@ type FreezerStats struct {
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
CpuStats CpuStats `json:"cpu_stats,omitempty"`
|
CpuStats CpuStats `json:"cpu_stats,omitempty"`
|
||||||
MemoryStats MemoryStats `json:"memory_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"`
|
FreezerStats FreezerStats `json:"freezer_stats,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewStats() *Stats {
|
||||||
|
memoryStats := MemoryStats{Stats: make(map[string]uint64)}
|
||||||
|
return &Stats{MemoryStats: memoryStats}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue