Add systemd stats, reusing fs stats functionality
Docker-DCO-1.1-Signed-off-by: Jimmi Dyson <jimmidyson@gmail.com> (github: jimmidyson)
This commit is contained in:
parent
b3f798f9a7
commit
cbd37fba86
|
@ -12,21 +12,21 @@ import (
|
|||
|
||||
var (
|
||||
subsystems = map[string]subsystem{
|
||||
"devices": &devicesGroup{},
|
||||
"memory": &memoryGroup{},
|
||||
"cpu": &cpuGroup{},
|
||||
"cpuset": &cpusetGroup{},
|
||||
"cpuacct": &cpuacctGroup{},
|
||||
"blkio": &blkioGroup{},
|
||||
"perf_event": &perfEventGroup{},
|
||||
"freezer": &freezerGroup{},
|
||||
"devices": &DevicesGroup{},
|
||||
"memory": &MemoryGroup{},
|
||||
"cpu": &CpuGroup{},
|
||||
"cpuset": &CpusetGroup{},
|
||||
"cpuacct": &CpuacctGroup{},
|
||||
"blkio": &BlkioGroup{},
|
||||
"perf_event": &PerfEventGroup{},
|
||||
"freezer": &FreezerGroup{},
|
||||
}
|
||||
)
|
||||
|
||||
type subsystem interface {
|
||||
Set(*data) error
|
||||
Remove(*data) error
|
||||
GetStats(*data, *cgroups.Stats) error
|
||||
GetStats(string, *cgroups.Stats) error
|
||||
}
|
||||
|
||||
type data struct {
|
||||
|
@ -68,10 +68,18 @@ func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
|
|||
return nil, fmt.Errorf("getting CgroupData %s", err)
|
||||
}
|
||||
|
||||
for sysName, sys := range subsystems {
|
||||
// Don't fail if a cgroup hierarchy was not found.
|
||||
if err := sys.GetStats(d, stats); err != nil && err != cgroups.ErrNotFound {
|
||||
return nil, fmt.Errorf("getting stats for system %q %s", sysName, err)
|
||||
for sysname, sys := range subsystems {
|
||||
path, err := d.path(sysname)
|
||||
if err != nil {
|
||||
// Don't fail if a cgroup hierarchy was not found, just skip this subsystem
|
||||
if err == cgroups.ErrNotFound {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := sys.GetStats(path, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,10 @@ import (
|
|||
"github.com/docker/libcontainer/cgroups"
|
||||
)
|
||||
|
||||
type blkioGroup struct {
|
||||
type BlkioGroup struct {
|
||||
}
|
||||
|
||||
func (s *blkioGroup) Set(d *data) error {
|
||||
func (s *BlkioGroup) Set(d *data) error {
|
||||
// we just want to join this group even though we don't set anything
|
||||
if _, err := d.join("blkio"); err != nil && err != cgroups.ErrNotFound {
|
||||
return err
|
||||
|
@ -22,7 +22,7 @@ func (s *blkioGroup) Set(d *data) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *blkioGroup) Remove(d *data) error {
|
||||
func (s *BlkioGroup) Remove(d *data) error {
|
||||
return removePath(d.path("blkio"))
|
||||
}
|
||||
|
||||
|
@ -113,13 +113,9 @@ func getBlkioStat(path string) ([]cgroups.BlkioStatEntry, error) {
|
|||
return blkioStats, nil
|
||||
}
|
||||
|
||||
func (s *blkioGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||
func (s *BlkioGroup) GetStats(path string, 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
|
||||
|
|
|
@ -44,8 +44,8 @@ func TestBlkioStats(t *testing.T) {
|
|||
"blkio.sectors_recursive": sectorsRecursiveContents,
|
||||
})
|
||||
|
||||
blkio := &blkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||
blkio := &BlkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -84,8 +84,8 @@ func TestBlkioStatsNoSectorsFile(t *testing.T) {
|
|||
"blkio.io_queued_recursive": queuedRecursiveContents,
|
||||
})
|
||||
|
||||
blkio := &blkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||
blkio := &BlkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed unexpectedly: %s", err)
|
||||
}
|
||||
|
@ -100,8 +100,8 @@ func TestBlkioStatsNoServiceBytesFile(t *testing.T) {
|
|||
"blkio.sectors_recursive": sectorsRecursiveContents,
|
||||
})
|
||||
|
||||
blkio := &blkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||
blkio := &BlkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed unexpectedly: %s", err)
|
||||
}
|
||||
|
@ -116,8 +116,8 @@ func TestBlkioStatsNoServicedFile(t *testing.T) {
|
|||
"blkio.sectors_recursive": sectorsRecursiveContents,
|
||||
})
|
||||
|
||||
blkio := &blkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||
blkio := &BlkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed unexpectedly: %s", err)
|
||||
}
|
||||
|
@ -132,8 +132,8 @@ func TestBlkioStatsNoQueuedFile(t *testing.T) {
|
|||
"blkio.sectors_recursive": sectorsRecursiveContents,
|
||||
})
|
||||
|
||||
blkio := &blkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||
blkio := &BlkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed unexpectedly: %s", err)
|
||||
}
|
||||
|
@ -149,8 +149,8 @@ func TestBlkioStatsUnexpectedNumberOfFields(t *testing.T) {
|
|||
"blkio.sectors_recursive": sectorsRecursiveContents,
|
||||
})
|
||||
|
||||
blkio := &blkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||
blkio := &BlkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err == nil {
|
||||
t.Fatal("Expected to fail, but did not")
|
||||
}
|
||||
|
@ -166,8 +166,8 @@ func TestBlkioStatsUnexpectedFieldType(t *testing.T) {
|
|||
"blkio.sectors_recursive": sectorsRecursiveContents,
|
||||
})
|
||||
|
||||
blkio := &blkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupData, &actualStats)
|
||||
blkio := &BlkioGroup{}
|
||||
err := blkio.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err == nil {
|
||||
t.Fatal("Expected to fail, but did not")
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
"github.com/docker/libcontainer/cgroups"
|
||||
)
|
||||
|
||||
type cpuGroup struct {
|
||||
type CpuGroup struct {
|
||||
}
|
||||
|
||||
func (s *cpuGroup) Set(d *data) error {
|
||||
func (s *CpuGroup) Set(d *data) error {
|
||||
// We always want to join the cpu group, to allow fair cpu scheduling
|
||||
// on a container basis
|
||||
dir, err := d.join("cpu")
|
||||
|
@ -37,16 +37,11 @@ func (s *cpuGroup) Set(d *data) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *cpuGroup) Remove(d *data) error {
|
||||
func (s *CpuGroup) Remove(d *data) error {
|
||||
return removePath(d.path("cpu"))
|
||||
}
|
||||
|
||||
func (s *cpuGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||
path, err := d.path("cpu")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
f, err := os.Open(filepath.Join(path, "cpu.stat"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
|
|
|
@ -23,8 +23,8 @@ func TestCpuStats(t *testing.T) {
|
|||
"cpu.stat": cpuStatContent,
|
||||
})
|
||||
|
||||
cpu := &cpuGroup{}
|
||||
err := cpu.GetStats(helper.CgroupData, &actualStats)
|
||||
cpu := &CpuGroup{}
|
||||
err := cpu.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -41,8 +41,8 @@ func TestNoCpuStatFile(t *testing.T) {
|
|||
helper := NewCgroupTestUtil("cpu", t)
|
||||
defer helper.cleanup()
|
||||
|
||||
cpu := &cpuGroup{}
|
||||
err := cpu.GetStats(helper.CgroupData, &actualStats)
|
||||
cpu := &CpuGroup{}
|
||||
err := cpu.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err != nil {
|
||||
t.Fatal("Expected not to fail, but did")
|
||||
}
|
||||
|
@ -58,8 +58,8 @@ func TestInvalidCpuStat(t *testing.T) {
|
|||
"cpu.stat": cpuStatContent,
|
||||
})
|
||||
|
||||
cpu := &cpuGroup{}
|
||||
err := cpu.GetStats(helper.CgroupData, &actualStats)
|
||||
cpu := &CpuGroup{}
|
||||
err := cpu.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err == nil {
|
||||
t.Fatal("Expected failed stat parsing.")
|
||||
}
|
||||
|
|
|
@ -22,10 +22,10 @@ var (
|
|||
|
||||
const nanosecondsInSecond = 1000000000
|
||||
|
||||
type cpuacctGroup struct {
|
||||
type CpuacctGroup struct {
|
||||
}
|
||||
|
||||
func (s *cpuacctGroup) Set(d *data) error {
|
||||
func (s *CpuacctGroup) Set(d *data) error {
|
||||
// we just want to join this group even though we don't set anything
|
||||
if _, err := d.join("cpuacct"); err != nil && err != cgroups.ErrNotFound {
|
||||
return err
|
||||
|
@ -33,20 +33,20 @@ func (s *cpuacctGroup) Set(d *data) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *cpuacctGroup) Remove(d *data) error {
|
||||
func (s *CpuacctGroup) Remove(d *data) error {
|
||||
return removePath(d.path("cpuacct"))
|
||||
}
|
||||
|
||||
func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||
func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
var (
|
||||
err error
|
||||
startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage, kernelModeUsage, userModeUsage, percentage uint64
|
||||
)
|
||||
path, err := d.path("cpuacct")
|
||||
if kernelModeUsage, userModeUsage, err = s.getCpuUsage(d, path); err != nil {
|
||||
if kernelModeUsage, userModeUsage, err = getCpuUsage(path); err != nil {
|
||||
return err
|
||||
}
|
||||
startCpu = kernelModeUsage + userModeUsage
|
||||
if startSystem, err = s.getSystemCpuUsage(d); err != nil {
|
||||
if startSystem, err = getSystemCpuUsage(); err != nil {
|
||||
return err
|
||||
}
|
||||
startUsageTime := time.Now()
|
||||
|
@ -55,11 +55,11 @@ func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|||
}
|
||||
// sample for 100ms
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if kernelModeUsage, userModeUsage, err = s.getCpuUsage(d, path); err != nil {
|
||||
if kernelModeUsage, userModeUsage, err = getCpuUsage(path); err != nil {
|
||||
return err
|
||||
}
|
||||
lastCpu = kernelModeUsage + userModeUsage
|
||||
if lastSystem, err = s.getSystemCpuUsage(d); err != nil {
|
||||
if lastSystem, err = getSystemCpuUsage(); err != nil {
|
||||
return err
|
||||
}
|
||||
usageSampleDuration := time.Since(startUsageTime)
|
||||
|
@ -80,7 +80,7 @@ func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|||
stats.CpuStats.CpuUsage.PercentUsage = percentage
|
||||
// Delta usage is in nanoseconds of CPU time so get the usage (in cores) over the sample time.
|
||||
stats.CpuStats.CpuUsage.CurrentUsage = deltaUsage / uint64(usageSampleDuration.Nanoseconds())
|
||||
percpuUsage, err := s.getPercpuUsage(path)
|
||||
percpuUsage, err := getPercpuUsage(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
|||
}
|
||||
|
||||
// TODO(vmarmol): Use cgroups stats.
|
||||
func (s *cpuacctGroup) getSystemCpuUsage(d *data) (uint64, error) {
|
||||
func getSystemCpuUsage() (uint64, error) {
|
||||
|
||||
f, err := os.Open("/proc/stat")
|
||||
if err != nil {
|
||||
|
@ -125,7 +125,7 @@ func (s *cpuacctGroup) getSystemCpuUsage(d *data) (uint64, error) {
|
|||
return 0, fmt.Errorf("invalid stat format")
|
||||
}
|
||||
|
||||
func (s *cpuacctGroup) getCpuUsage(d *data, path string) (uint64, uint64, error) {
|
||||
func getCpuUsage(path string) (uint64, uint64, error) {
|
||||
kernelModeUsage := uint64(0)
|
||||
userModeUsage := uint64(0)
|
||||
data, err := ioutil.ReadFile(filepath.Join(path, "cpuacct.stat"))
|
||||
|
@ -146,7 +146,7 @@ func (s *cpuacctGroup) getCpuUsage(d *data, path string) (uint64, uint64, error)
|
|||
return kernelModeUsage, userModeUsage, nil
|
||||
}
|
||||
|
||||
func (s *cpuacctGroup) getPercpuUsage(path string) ([]uint64, error) {
|
||||
func getPercpuUsage(path string) ([]uint64, error) {
|
||||
percpuUsage := []uint64{}
|
||||
data, err := ioutil.ReadFile(filepath.Join(path, "cpuacct.usage_percpu"))
|
||||
if err != nil {
|
||||
|
|
|
@ -10,10 +10,10 @@ import (
|
|||
"github.com/docker/libcontainer/cgroups"
|
||||
)
|
||||
|
||||
type cpusetGroup struct {
|
||||
type CpusetGroup struct {
|
||||
}
|
||||
|
||||
func (s *cpusetGroup) Set(d *data) error {
|
||||
func (s *CpusetGroup) Set(d *data) error {
|
||||
// we don't want to join this cgroup unless it is specified
|
||||
if d.c.CpusetCpus != "" {
|
||||
dir, err := d.path("cpuset")
|
||||
|
@ -36,15 +36,15 @@ func (s *cpusetGroup) Set(d *data) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *cpusetGroup) Remove(d *data) error {
|
||||
func (s *CpusetGroup) Remove(d *data) error {
|
||||
return removePath(d.path("cpuset"))
|
||||
}
|
||||
|
||||
func (s *cpusetGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||
func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
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) {
|
||||
if cpus, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.cpus")); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ func (s *cpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []b
|
|||
// ensureParent ensures that the parent directory of current is created
|
||||
// with the proper cpus and mems files copied from it's parent if the values
|
||||
// are a file with a new line char
|
||||
func (s *cpusetGroup) ensureParent(current string) error {
|
||||
func (s *CpusetGroup) ensureParent(current string) error {
|
||||
parent := filepath.Dir(current)
|
||||
|
||||
if _, err := os.Stat(parent); err != nil {
|
||||
|
@ -78,7 +78,7 @@ func (s *cpusetGroup) ensureParent(current string) error {
|
|||
|
||||
// copyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent
|
||||
// directory to the current directory if the file's contents are 0
|
||||
func (s *cpusetGroup) copyIfNeeded(current, parent string) error {
|
||||
func (s *CpusetGroup) copyIfNeeded(current, parent string) error {
|
||||
var (
|
||||
err error
|
||||
currentCpus, currentMems []byte
|
||||
|
@ -105,6 +105,6 @@ func (s *cpusetGroup) copyIfNeeded(current, parent string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *cpusetGroup) isEmpty(b []byte) bool {
|
||||
func (s *CpusetGroup) isEmpty(b []byte) bool {
|
||||
return len(bytes.Trim(b, "\n")) == 0
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ package fs
|
|||
|
||||
import "github.com/docker/libcontainer/cgroups"
|
||||
|
||||
type devicesGroup struct {
|
||||
type DevicesGroup struct {
|
||||
}
|
||||
|
||||
func (s *devicesGroup) Set(d *data) error {
|
||||
func (s *DevicesGroup) Set(d *data) error {
|
||||
dir, err := d.join("devices")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -25,10 +25,10 @@ func (s *devicesGroup) Set(d *data) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *devicesGroup) Remove(d *data) error {
|
||||
func (s *DevicesGroup) Remove(d *data) error {
|
||||
return removePath(d.path("devices"))
|
||||
}
|
||||
|
||||
func (s *devicesGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||
func (s *DevicesGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
"github.com/docker/libcontainer/cgroups"
|
||||
)
|
||||
|
||||
type freezerGroup struct {
|
||||
type FreezerGroup struct {
|
||||
}
|
||||
|
||||
func (s *freezerGroup) Set(d *data) error {
|
||||
func (s *FreezerGroup) Set(d *data) error {
|
||||
switch d.c.Freezer {
|
||||
case cgroups.Frozen, cgroups.Thawed:
|
||||
dir, err := d.path("freezer")
|
||||
|
@ -43,7 +43,7 @@ func (s *freezerGroup) Set(d *data) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *freezerGroup) Remove(d *data) error {
|
||||
func (s *FreezerGroup) Remove(d *data) error {
|
||||
return removePath(d.path("freezer"))
|
||||
}
|
||||
|
||||
|
@ -52,12 +52,11 @@ func getFreezerFileData(path string) (string, error) {
|
|||
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 err
|
||||
}
|
||||
var data string
|
||||
func (s *FreezerGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
var (
|
||||
data string
|
||||
err error
|
||||
)
|
||||
if data, err = getFreezerFileData(filepath.Join(path, "freezer.parent_freezing")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
"github.com/docker/libcontainer/cgroups"
|
||||
)
|
||||
|
||||
type memoryGroup struct {
|
||||
type MemoryGroup struct {
|
||||
}
|
||||
|
||||
func (s *memoryGroup) Set(d *data) error {
|
||||
func (s *MemoryGroup) Set(d *data) error {
|
||||
dir, err := d.join("memory")
|
||||
// only return an error for memory if it was not specified
|
||||
if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) {
|
||||
|
@ -47,16 +47,11 @@ func (s *memoryGroup) Set(d *data) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *memoryGroup) Remove(d *data) error {
|
||||
func (s *MemoryGroup) Remove(d *data) error {
|
||||
return removePath(d.path("memory"))
|
||||
}
|
||||
|
||||
func (s *memoryGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||
path, err := d.path("memory")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
// Set stats from memory.stat.
|
||||
statsFile, err := os.Open(filepath.Join(path, "memory.stat"))
|
||||
if err != nil {
|
||||
|
|
|
@ -24,8 +24,8 @@ func TestMemoryStats(t *testing.T) {
|
|||
"memory.failcnt": memoryFailcnt,
|
||||
})
|
||||
|
||||
memory := &memoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||
memory := &MemoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -41,8 +41,8 @@ func TestMemoryStatsNoStatFile(t *testing.T) {
|
|||
"memory.max_usage_in_bytes": memoryMaxUsageContents,
|
||||
})
|
||||
|
||||
memory := &memoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||
memory := &MemoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -56,8 +56,8 @@ func TestMemoryStatsNoUsageFile(t *testing.T) {
|
|||
"memory.max_usage_in_bytes": memoryMaxUsageContents,
|
||||
})
|
||||
|
||||
memory := &memoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||
memory := &MemoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err == nil {
|
||||
t.Fatal("Expected failure")
|
||||
}
|
||||
|
@ -71,8 +71,8 @@ func TestMemoryStatsNoMaxUsageFile(t *testing.T) {
|
|||
"memory.usage_in_bytes": memoryUsageContents,
|
||||
})
|
||||
|
||||
memory := &memoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||
memory := &MemoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err == nil {
|
||||
t.Fatal("Expected failure")
|
||||
}
|
||||
|
@ -87,8 +87,8 @@ func TestMemoryStatsBadStatFile(t *testing.T) {
|
|||
"memory.max_usage_in_bytes": memoryMaxUsageContents,
|
||||
})
|
||||
|
||||
memory := &memoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||
memory := &MemoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err == nil {
|
||||
t.Fatal("Expected failure")
|
||||
}
|
||||
|
@ -103,8 +103,8 @@ func TestMemoryStatsBadUsageFile(t *testing.T) {
|
|||
"memory.max_usage_in_bytes": memoryMaxUsageContents,
|
||||
})
|
||||
|
||||
memory := &memoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||
memory := &MemoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err == nil {
|
||||
t.Fatal("Expected failure")
|
||||
}
|
||||
|
@ -119,8 +119,8 @@ func TestMemoryStatsBadMaxUsageFile(t *testing.T) {
|
|||
"memory.max_usage_in_bytes": "bad",
|
||||
})
|
||||
|
||||
memory := &memoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupData, &actualStats)
|
||||
memory := &MemoryGroup{}
|
||||
err := memory.GetStats(helper.CgroupPath, &actualStats)
|
||||
if err == nil {
|
||||
t.Fatal("Expected failure")
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ import (
|
|||
"github.com/docker/libcontainer/cgroups"
|
||||
)
|
||||
|
||||
type perfEventGroup struct {
|
||||
type PerfEventGroup struct {
|
||||
}
|
||||
|
||||
func (s *perfEventGroup) Set(d *data) error {
|
||||
func (s *PerfEventGroup) Set(d *data) error {
|
||||
// we just want to join this group even though we don't set anything
|
||||
if _, err := d.join("perf_event"); err != nil && err != cgroups.ErrNotFound {
|
||||
return err
|
||||
|
@ -15,10 +15,10 @@ func (s *perfEventGroup) Set(d *data) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *perfEventGroup) Remove(d *data) error {
|
||||
func (s *PerfEventGroup) Remove(d *data) error {
|
||||
return removePath(d.path("perf_event"))
|
||||
}
|
||||
|
||||
func (s *perfEventGroup) GetStats(d *data, stats *cgroups.Stats) error {
|
||||
func (s *PerfEventGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -23,3 +23,7 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
|||
func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
|
||||
return fmt.Errorf("Systemd not supported")
|
||||
}
|
||||
|
||||
func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
|
||||
return nil, fmt.Errorf("Systemd not supported")
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
systemd1 "github.com/coreos/go-systemd/dbus"
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/docker/libcontainer/cgroups/fs"
|
||||
"github.com/dotcloud/docker/pkg/systemd"
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
@ -23,10 +24,24 @@ type systemdCgroup struct {
|
|||
cleanupDirs []string
|
||||
}
|
||||
|
||||
type subsystem interface {
|
||||
GetStats(string, *cgroups.Stats) error
|
||||
}
|
||||
|
||||
var (
|
||||
connLock sync.Mutex
|
||||
theConn *systemd1.Conn
|
||||
hasStartTransientUnit bool
|
||||
subsystems = map[string]subsystem{
|
||||
"devices": &fs.DevicesGroup{},
|
||||
"memory": &fs.MemoryGroup{},
|
||||
"cpu": &fs.CpuGroup{},
|
||||
"cpuset": &fs.CpusetGroup{},
|
||||
"cpuacct": &fs.CpuacctGroup{},
|
||||
"blkio": &fs.BlkioGroup{},
|
||||
"perf_event": &fs.PerfEventGroup{},
|
||||
"freezer": &fs.FreezerGroup{},
|
||||
}
|
||||
)
|
||||
|
||||
func UseSystemd() bool {
|
||||
|
@ -316,7 +331,7 @@ func (c *systemdCgroup) Cleanup() error {
|
|||
}
|
||||
|
||||
func joinFreezer(c *cgroups.Cgroup, pid int) (string, error) {
|
||||
path, err := getFreezerPath(c)
|
||||
path, err := getSubsystemPath(c, "freezer")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -332,23 +347,27 @@ func joinFreezer(c *cgroups.Cgroup, pid int) (string, error) {
|
|||
return path, nil
|
||||
}
|
||||
|
||||
func getFreezerPath(c *cgroups.Cgroup) (string, error) {
|
||||
mountpoint, err := cgroups.FindCgroupMountpoint("freezer")
|
||||
func getSubsystemPath(c *cgroups.Cgroup, subsystem string) (string, error) {
|
||||
mountpoint, err := cgroups.FindCgroupMountpoint(subsystem)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
initPath, err := cgroups.GetInitCgroupDir("freezer")
|
||||
initPath, err := cgroups.GetInitCgroupDir(subsystem)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(mountpoint, initPath, fmt.Sprintf("%s-%s", c.Parent, c.Name)), nil
|
||||
slice := "system.slice"
|
||||
if c.Slice != "" {
|
||||
slice = c.Slice
|
||||
}
|
||||
|
||||
return filepath.Join(mountpoint, initPath, slice, getUnitName(c)), nil
|
||||
}
|
||||
|
||||
func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
|
||||
path, err := getFreezerPath(c)
|
||||
path, err := getSubsystemPath(c, "freezer")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -389,3 +408,28 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
|||
func getUnitName(c *cgroups.Cgroup) string {
|
||||
return fmt.Sprintf("%s-%s.scope", c.Parent, c.Name)
|
||||
}
|
||||
|
||||
/*
|
||||
* This would be nicer to get from the systemd API when accounting
|
||||
* is enabled, but sadly there is no way to do that yet.
|
||||
* The lack of this functionality in the API & the approach taken
|
||||
* is guided by
|
||||
* http://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/#readingaccountinginformation.
|
||||
*/
|
||||
func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
|
||||
var subsystemPath string
|
||||
var err error
|
||||
|
||||
stats := cgroups.NewStats()
|
||||
|
||||
for sysname, sys := range subsystems {
|
||||
if subsystemPath, err = getSubsystemPath(c, sysname); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := sys.GetStats(subsystemPath, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue