From cbd37fba867318322e61f8c7d138fd323e0b01d5 Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Fri, 20 Jun 2014 14:13:56 +0100 Subject: [PATCH] Add systemd stats, reusing fs stats functionality Docker-DCO-1.1-Signed-off-by: Jimmi Dyson (github: jimmidyson) --- cgroups/fs/apply_raw.go | 34 +++++++++++------- cgroups/fs/blkio.go | 12 +++---- cgroups/fs/blkio_test.go | 28 +++++++-------- cgroups/fs/cpu.go | 13 +++---- cgroups/fs/cpu_test.go | 12 +++---- cgroups/fs/cpuacct.go | 26 +++++++------- cgroups/fs/cpuset.go | 16 ++++----- cgroups/fs/devices.go | 8 ++--- cgroups/fs/freezer.go | 17 +++++---- cgroups/fs/memory.go | 13 +++---- cgroups/fs/memory_test.go | 28 +++++++-------- cgroups/fs/perf_event.go | 8 ++--- cgroups/systemd/apply_nosystemd.go | 4 +++ cgroups/systemd/apply_systemd.go | 56 ++++++++++++++++++++++++++---- 14 files changed, 158 insertions(+), 117 deletions(-) diff --git a/cgroups/fs/apply_raw.go b/cgroups/fs/apply_raw.go index b4ee668c..2d5a1c0e 100644 --- a/cgroups/fs/apply_raw.go +++ b/cgroups/fs/apply_raw.go @@ -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 } } diff --git a/cgroups/fs/blkio.go b/cgroups/fs/blkio.go index 52cf17b4..38b87577 100644 --- a/cgroups/fs/blkio.go +++ b/cgroups/fs/blkio.go @@ -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 diff --git a/cgroups/fs/blkio_test.go b/cgroups/fs/blkio_test.go index 4a66e7a7..db6f8d52 100644 --- a/cgroups/fs/blkio_test.go +++ b/cgroups/fs/blkio_test.go @@ -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") } diff --git a/cgroups/fs/cpu.go b/cgroups/fs/cpu.go index 6ded4656..efac9ed1 100644 --- a/cgroups/fs/cpu.go +++ b/cgroups/fs/cpu.go @@ -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) { diff --git a/cgroups/fs/cpu_test.go b/cgroups/fs/cpu_test.go index ebdb6a57..017a1f4b 100644 --- a/cgroups/fs/cpu_test.go +++ b/cgroups/fs/cpu_test.go @@ -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.") } diff --git a/cgroups/fs/cpuacct.go b/cgroups/fs/cpuacct.go index a3d22c9f..be180520 100644 --- a/cgroups/fs/cpuacct.go +++ b/cgroups/fs/cpuacct.go @@ -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 { diff --git a/cgroups/fs/cpuset.go b/cgroups/fs/cpuset.go index 094e8b38..9570125f 100644 --- a/cgroups/fs/cpuset.go +++ b/cgroups/fs/cpuset.go @@ -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 } diff --git a/cgroups/fs/devices.go b/cgroups/fs/devices.go index 675cef3f..98d5d2d7 100644 --- a/cgroups/fs/devices.go +++ b/cgroups/fs/devices.go @@ -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 } diff --git a/cgroups/fs/freezer.go b/cgroups/fs/freezer.go index f6a1044a..858f1aa7 100644 --- a/cgroups/fs/freezer.go +++ b/cgroups/fs/freezer.go @@ -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 } diff --git a/cgroups/fs/memory.go b/cgroups/fs/memory.go index b0156ebc..c27150d2 100644 --- a/cgroups/fs/memory.go +++ b/cgroups/fs/memory.go @@ -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 { diff --git a/cgroups/fs/memory_test.go b/cgroups/fs/memory_test.go index 7d8950e1..e92f1daf 100644 --- a/cgroups/fs/memory_test.go +++ b/cgroups/fs/memory_test.go @@ -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") } diff --git a/cgroups/fs/perf_event.go b/cgroups/fs/perf_event.go index b834c3ec..5f45678f 100644 --- a/cgroups/fs/perf_event.go +++ b/cgroups/fs/perf_event.go @@ -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 } diff --git a/cgroups/systemd/apply_nosystemd.go b/cgroups/systemd/apply_nosystemd.go index 6dcfdffd..68559109 100644 --- a/cgroups/systemd/apply_nosystemd.go +++ b/cgroups/systemd/apply_nosystemd.go @@ -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") +} diff --git a/cgroups/systemd/apply_systemd.go b/cgroups/systemd/apply_systemd.go index 6a0ce950..92d8328e 100644 --- a/cgroups/systemd/apply_systemd.go +++ b/cgroups/systemd/apply_systemd.go @@ -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 +}