1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Extract cgroups utilities to own submodule.

Use mountinfo rather than for cgroups parsing.
Make helper method private and change name.
Makes method naming more explicit rather than GetThisCgroup.
Use upstream term subsystem rather than cgroupType.
This commit is contained in:
Paul Nasrat 2013-12-21 11:02:06 -05:00
parent 907d9ce13c
commit c561212b83
8 changed files with 175 additions and 95 deletions

1
cgroups/MAINTAINERS Normal file
View file

@ -0,0 +1 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)

101
cgroups/cgroups.go Normal file
View file

@ -0,0 +1,101 @@
package cgroups
import (
"bufio"
"fmt"
"github.com/dotcloud/docker/mount"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
)
// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
func FindCgroupMountpoint(subsystem string) (string, error) {
mounts, err := mount.GetMounts()
if err != nil {
return "", err
}
for _, mount := range mounts {
if mount.Fstype == "cgroup" {
for _, opt := range strings.Split(mount.VfsOpts, ",") {
if opt == subsystem {
return mount.Mountpoint, nil
}
}
}
}
return "", fmt.Errorf("cgroup mountpoint not found for %s", subsystem)
}
// Returns the relative path to the cgroup docker is running in.
func getThisCgroupDir(subsystem string) (string, error) {
f, err := os.Open("/proc/self/cgroup")
if err != nil {
return "", err
}
defer f.Close()
return parseCgroupFile(subsystem, f)
}
func parseCgroupFile(subsystem string, r io.Reader) (string, error) {
s := bufio.NewScanner(r)
for s.Scan() {
if err := s.Err(); err != nil {
return "", err
}
text := s.Text()
parts := strings.Split(text, ":")
if parts[1] == subsystem {
return parts[2], nil
}
}
return "", fmt.Errorf("cgroup '%s' not found in /proc/self/cgroup", subsystem)
}
// Returns a list of pids for the given container.
func GetPidsForContainer(id string) ([]int, error) {
pids := []int{}
// memory is chosen randomly, any cgroup used by docker works
subsystem := "memory"
cgroupRoot, err := FindCgroupMountpoint(subsystem)
if err != nil {
return pids, err
}
cgroupDir, err := getThisCgroupDir(subsystem)
if err != nil {
return pids, err
}
filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
if _, err := os.Stat(filename); os.IsNotExist(err) {
// With more recent lxc versions use, cgroup will be in lxc/
filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks")
}
output, err := ioutil.ReadFile(filename)
if err != nil {
return pids, err
}
for _, p := range strings.Split(string(output), "\n") {
if len(p) == 0 {
continue
}
pid, err := strconv.Atoi(p)
if err != nil {
return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
}
pids = append(pids, pid)
}
return pids, nil
}

27
cgroups/cgroups_test.go Normal file
View file

@ -0,0 +1,27 @@
package cgroups
import (
"bytes"
"testing"
)
const (
cgroupsContents = `11:hugetlb:/
10:perf_event:/
9:blkio:/
8:net_cls:/
7:freezer:/
6:devices:/
5:memory:/
4:cpuacct,cpu:/
3:cpuset:/
2:name=systemd:/user.slice/user-1000.slice/session-16.scope`
)
func TestParseCgroups(t *testing.T) {
r := bytes.NewBuffer([]byte(cgroupsContents))
_, err := parseCgroupFile("blkio", r)
if err != nil {
t.Fatal(err)
}
}

View file

@ -4,6 +4,10 @@ import (
"time" "time"
) )
func GetMounts() ([]*MountInfo, error) {
return parseMountTable()
}
// Looks at /proc/self/mountinfo to determine of the specified // Looks at /proc/self/mountinfo to determine of the specified
// mountpoint has been mounted // mountpoint has been mounted
func Mounted(mountpoint string) (bool, error) { func Mounted(mountpoint string) (bool, error) {
@ -14,7 +18,7 @@ func Mounted(mountpoint string) (bool, error) {
// Search the table for the mountpoint // Search the table for the mountpoint
for _, e := range entries { for _, e := range entries {
if e.mountpoint == mountpoint { if e.Mountpoint == mountpoint {
return true, nil return true, nil
} }
} }

View file

@ -5,22 +5,35 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"strings"
) )
const ( const (
// We only parse upto the mountinfo because that is all we /* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
// care about right now (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
mountinfoFormat = "%d %d %d:%d %s %s %s"
(1) mount ID: unique identifier of the mount (may be reused after umount)
(2) parent ID: ID of parent (or of self for the top of the mount tree)
(3) major:minor: value of st_dev for files on filesystem
(4) root: root of the mount within the filesystem
(5) mount point: mount point relative to the process's root
(6) mount options: per mount options
(7) optional fields: zero or more fields of the form "tag[:value]"
(8) separator: marks the end of the optional fields
(9) filesystem type: name of filesystem of the form "type[.subtype]"
(10) mount source: filesystem specific information or "none"
(11) super options: per super block options*/
mountinfoFormat = "%d %d %d:%d %s %s %s "
) )
// Represents one line from /proc/self/mountinfo type MountInfo struct {
type procEntry struct { Id, Parent, Major, Minor int
id, parent, major, minor int Root, Mountpoint, Opts string
source, mountpoint, opts string Fstype, Source, VfsOpts string
} }
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts // Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts
func parseMountTable() ([]*procEntry, error) { func parseMountTable() ([]*MountInfo, error) {
f, err := os.Open("/proc/self/mountinfo") f, err := os.Open("/proc/self/mountinfo")
if err != nil { if err != nil {
return nil, err return nil, err
@ -30,10 +43,10 @@ func parseMountTable() ([]*procEntry, error) {
return parseInfoFile(f) return parseInfoFile(f)
} }
func parseInfoFile(r io.Reader) ([]*procEntry, error) { func parseInfoFile(r io.Reader) ([]*MountInfo, error) {
var ( var (
s = bufio.NewScanner(r) s = bufio.NewScanner(r)
out = []*procEntry{} out = []*MountInfo{}
) )
for s.Scan() { for s.Scan() {
@ -42,14 +55,24 @@ func parseInfoFile(r io.Reader) ([]*procEntry, error) {
} }
var ( var (
p = &procEntry{} p = &MountInfo{}
text = s.Text() text = s.Text()
) )
if _, err := fmt.Sscanf(text, mountinfoFormat, if _, err := fmt.Sscanf(text, mountinfoFormat,
&p.id, &p.parent, &p.major, &p.minor, &p.Id, &p.Parent, &p.Major, &p.Minor,
&p.source, &p.mountpoint, &p.opts); err != nil { &p.Root, &p.Mountpoint, &p.Opts); err != nil {
return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err) return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
} }
// Safe as mountinfo encodes mountpoints with spaces as \040.
index := strings.Index(text, " - ")
postSeparatorFields := strings.Fields(text[index+3:])
if len(postSeparatorFields) != 3 {
return nil, fmt.Errorf("Error did not find 3 fields post '-' in '%s'", text)
}
p.Fstype = postSeparatorFields[0]
p.Source = postSeparatorFields[1]
p.VfsOpts = postSeparatorFields[2]
out = append(out, p) out = append(out, p)
} }
return out, nil return out, nil

View file

@ -4,11 +4,12 @@ import (
"container/list" "container/list"
"fmt" "fmt"
"github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/pkg/graphdb" "github.com/dotcloud/docker/cgroups"
"github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/graphdriver"
"github.com/dotcloud/docker/graphdriver/aufs" "github.com/dotcloud/docker/graphdriver/aufs"
_ "github.com/dotcloud/docker/graphdriver/devmapper" _ "github.com/dotcloud/docker/graphdriver/devmapper"
_ "github.com/dotcloud/docker/graphdriver/vfs" _ "github.com/dotcloud/docker/graphdriver/vfs"
"github.com/dotcloud/docker/pkg/graphdb"
"github.com/dotcloud/docker/utils" "github.com/dotcloud/docker/utils"
"io" "io"
"io/ioutil" "io/ioutil"
@ -332,7 +333,7 @@ func (runtime *Runtime) restore() error {
// FIXME: comment please! // FIXME: comment please!
func (runtime *Runtime) UpdateCapabilities(quiet bool) { func (runtime *Runtime) UpdateCapabilities(quiet bool) {
if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil { if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil {
if !quiet { if !quiet {
log.Printf("WARNING: %s\n", err) log.Printf("WARNING: %s\n", err)
} }

View file

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/auth" "github.com/dotcloud/docker/auth"
"github.com/dotcloud/docker/cgroups"
"github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/pkg/graphdb" "github.com/dotcloud/docker/pkg/graphdb"
"github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/registry"
@ -701,7 +702,7 @@ func (srv *Server) ContainerTop(name, psArgs string) (*APITop, error) {
if !container.State.IsRunning() { if !container.State.IsRunning() {
return nil, fmt.Errorf("Container %s is not running", name) return nil, fmt.Errorf("Container %s is not running", name)
} }
pids, err := utils.GetPidsForContainer(container.ID) pids, err := cgroups.GetPidsForContainer(container.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -583,28 +583,6 @@ func CompareKernelVersion(a, b *KernelVersionInfo) int {
return 0 return 0
} }
func FindCgroupMountpoint(cgroupType string) (string, error) {
output, err := ioutil.ReadFile("/proc/mounts")
if err != nil {
return "", err
}
// /proc/mounts has 6 fields per line, one mount per line, e.g.
// cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0
for _, line := range strings.Split(string(output), "\n") {
parts := strings.Split(line, " ")
if len(parts) == 6 && parts[2] == "cgroup" {
for _, opt := range strings.Split(parts[3], ",") {
if opt == cgroupType {
return parts[1], nil
}
}
}
}
return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
}
func GetKernelVersion() (*KernelVersionInfo, error) { func GetKernelVersion() (*KernelVersionInfo, error) {
var ( var (
err error err error
@ -1157,59 +1135,3 @@ func CopyFile(src, dst string) (int64, error) {
defer df.Close() defer df.Close()
return io.Copy(df, sf) return io.Copy(df, sf)
} }
// Returns the relative path to the cgroup docker is running in.
func GetThisCgroup(cgroupType string) (string, error) {
output, err := ioutil.ReadFile("/proc/self/cgroup")
if err != nil {
return "", err
}
for _, line := range strings.Split(string(output), "\n") {
parts := strings.Split(line, ":")
// any type used by docker should work
if parts[1] == cgroupType {
return parts[2], nil
}
}
return "", fmt.Errorf("cgroup '%s' not found in /proc/self/cgroup", cgroupType)
}
// Returns a list of pids for the given container.
func GetPidsForContainer(id string) ([]int, error) {
pids := []int{}
// memory is chosen randomly, any cgroup used by docker works
cgroupType := "memory"
cgroupRoot, err := FindCgroupMountpoint(cgroupType)
if err != nil {
return pids, err
}
cgroupThis, err := GetThisCgroup(cgroupType)
if err != nil {
return pids, err
}
filename := filepath.Join(cgroupRoot, cgroupThis, id, "tasks")
if _, err := os.Stat(filename); os.IsNotExist(err) {
// With more recent lxc versions use, cgroup will be in lxc/
filename = filepath.Join(cgroupRoot, cgroupThis, "lxc", id, "tasks")
}
output, err := ioutil.ReadFile(filename)
if err != nil {
return pids, err
}
for _, p := range strings.Split(string(output), "\n") {
if len(p) == 0 {
continue
}
pid, err := strconv.Atoi(p)
if err != nil {
return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
}
pids = append(pids, pid)
}
return pids, nil
}