diff --git a/container/raw/handler.go b/container/raw/handler.go index d51eaa8a..cc1e7002 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -24,10 +24,11 @@ import ( "code.google.com/p/go.exp/inotify" "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/cgroups/fs" + cgroup_fs "github.com/docker/libcontainer/cgroups/fs" "github.com/golang/glog" "github.com/google/cadvisor/container" "github.com/google/cadvisor/container/libcontainer" + "github.com/google/cadvisor/fs" "github.com/google/cadvisor/info" "github.com/google/cadvisor/utils" ) @@ -40,6 +41,7 @@ type rawContainerHandler struct { watcher *inotify.Watcher stopWatcher chan error watches map[string]struct{} + fsInfo fs.FsInfo } func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { @@ -53,6 +55,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac machineInfoFactory: machineInfoFactory, stopWatcher: make(chan error), watches: make(map[string]struct{}), + fsInfo: fs.NewFsInfo(), }, nil } @@ -144,8 +147,18 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) { return spec, nil } -func (self *rawContainerHandler) GetStats() (stats *info.ContainerStats, err error) { - return libcontainer.GetStatsCgroupOnly(self.cgroup) +func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { + stats, err := libcontainer.GetStatsCgroupOnly(self.cgroup) + if err != nil { + return nil, err + } + // Get Filesystem information + fsStats, err := self.fsInfo.GetFsStats(self.name) + if err != nil { + return nil, err + } + stats.FsStats = fsStats + return stats, nil } // Lists all directories under "path" and outputs the results as children of "parent". @@ -203,7 +216,7 @@ func (self *rawContainerHandler) ListThreads(listType container.ListType) ([]int } func (self *rawContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { - return fs.GetPids(self.cgroup) + return cgroup_fs.GetPids(self.cgroup) } func (self *rawContainerHandler) watchDirectory(dir string, containerName string) error { diff --git a/fs/fs.go b/fs/fs.go new file mode 100644 index 00000000..b99bd75c --- /dev/null +++ b/fs/fs.go @@ -0,0 +1,66 @@ +// +build linux +// +// Provides Filesystem Stats +package fs + +/* + extern int getBytesFree(const char *path, unsigned long long *bytes); + extern int getBytesTotal(const char *path, unsigned long long *bytes); +*/ +import "C" + +import ( + "syscall" + "unsafe" + + "github.com/docker/docker/pkg/mount" + "github.com/golang/glog" +) + +const EXT_SUPER_MAGIC = 0xEF53 + +type FsInfoImpl struct{} + +func NewFsInfo() FsInfo { + return &FsInfoImpl{} +} + +func (*FsInfoImpl) GetFsStats(containerName string) ([]FsStat, error) { + filesystems := make([]FsStat, 0) + if containerName != "/" { + return filesystems, nil + } + mounts, err := mount.GetMounts() + if err != nil { + return nil, err + } + for _, mount := range mounts { + if !strings.HasPrefix("ext", mount.FsType) || mount.Mountpoint != mount.Root { + continue + } + total, free, err := getVfsStats(mount.Mountpoint) + if err != nil { + glog.Errorf("Statvfs failed. Error: %v", err) + } else { + glog.V(1).Infof("%s is an ext partition at %s. Total: %d, Free: %d", mount.Source, mount.Mountpoint, total, free) + filesystems = append(filesystems, FsStat{mount.Source, total, free}) + } + } + return filesystems, nil +} + +func getVfsStats(path string) (total uint64, free uint64, err error) { + _p0, err := syscall.BytePtrFromString(path) + if err != nil { + return 0, 0, err + } + res, err := C.getBytesFree((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&free))) + if res != 0 { + return 0, 0, err + } + res, err = C.getBytesTotal((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&total))) + if res != 0 { + return 0, 0, err + } + return total, free, nil +} diff --git a/fs/statvfs.c b/fs/statvfs.c new file mode 100644 index 00000000..6961df4f --- /dev/null +++ b/fs/statvfs.c @@ -0,0 +1,23 @@ +// +build cgo + +#include + +int getBytesFree(const char *path, unsigned long long *bytes) { + struct statvfs buf; + int res; + if ((res = statvfs(path, &buf)) && res != 0) { + return -1; + } + *bytes = buf.f_frsize * buf.f_bfree; + return 0; +} + +int getBytesTotal(const char *path, unsigned long long *bytes) { + struct statvfs buf; + int res; + if ((res = statvfs(path, &buf)) && res != 0) { + return -1; + } + *bytes = buf.f_frsize * buf.f_blocks; + return 0; +} diff --git a/fs/types.go b/fs/types.go new file mode 100644 index 00000000..e527993e --- /dev/null +++ b/fs/types.go @@ -0,0 +1,12 @@ +package fs + +type FsStat struct { + Name string `json:"name"` + Capacity uint64 `json:"capacity"` + Free uint64 `json:"free"` +} + +type FsInfo interface { + // Returns capacity and free space, in bytes, of all the ext2, ext3, ext4 filesystems used by container 'containerName'. + GetFsStats(containerName string) ([]FsStat, error) +} diff --git a/info/container.go b/info/container.go index 649b069f..dbfb698f 100644 --- a/info/container.go +++ b/info/container.go @@ -17,6 +17,8 @@ package info import ( "reflect" "time" + + "github.com/google/cadvisor/fs" ) type CpuSpec struct { @@ -236,6 +238,8 @@ type ContainerStats struct { DiskIo DiskIoStats `json:"diskio,omitempty"` Memory *MemoryStats `json:"memory,omitempty"` Network *NetworkStats `json:"network,omitempty"` + // Filesystem statistics + FsStats []fs.FsStat `json:"fs_stats,omitempty"` } // Makes a deep copy of the ContainerStats and returns a pointer to the new