Mounted partitions space usage metrics per container

This computes the space usage for mounted partitions. It takes in a list of mounted partitions from containerHints
and computes the device's disk usage(so each mount must be a separate partition). This is useful for users who
mount partitions on containers and store most of the container's persistent data on those partitions.
This commit is contained in:
Abin Shahab 2014-10-14 23:10:22 +00:00
parent 08d0aa41d6
commit b8ed0bd0e3
5 changed files with 87 additions and 24 deletions

View File

@ -34,6 +34,12 @@ type containerHints struct {
type containerHint struct { type containerHint struct {
FullName string `json:"full_path,omitempty"` FullName string `json:"full_path,omitempty"`
NetworkInterface *networkInterface `json:"network_interface,omitempty"` NetworkInterface *networkInterface `json:"network_interface,omitempty"`
Mounts []mount `json:"mounts,omitempty"`
}
type mount struct {
HostDir string `json:"host-dir,omitempty"`
ContainerDir string `json:"container-dir,omitempty"`
} }
type networkInterface struct { type networkInterface struct {

View File

@ -15,6 +15,25 @@ func TestGetContainerHintsFromFile(t *testing.T) {
cHints.AllHosts[0].NetworkInterface.VethChild != "eth1" { cHints.AllHosts[0].NetworkInterface.VethChild != "eth1" {
t.Errorf("Cannot find network interface in %s", cHints) t.Errorf("Cannot find network interface in %s", cHints)
} }
var mountDirs [5]string
for i, mountDir := range cHints.AllHosts[0].Mounts {
mountDirs[i] = mountDir.HostDir
}
correctMountDirs := [...]string{
"/var/run/nm-sdc1",
"/var/run/nm-sdb3",
"/var/run/nm-sda3",
"/var/run/netns/root",
"/var/run/openvswitch/db.sock",
}
for i, mountDir := range cHints.AllHosts[0].Mounts {
if correctMountDirs[i] != mountDir.HostDir {
t.Errorf("Cannot find mount %s in %s", mountDir.HostDir, cHints)
}
}
} }
func TestFileNotExist(t *testing.T) { func TestFileNotExist(t *testing.T) {

View File

@ -45,6 +45,7 @@ type rawContainerHandler struct {
watches map[string]struct{} watches map[string]struct{}
fsInfo fs.FsInfo fsInfo fs.FsInfo
networkInterface *networkInterface networkInterface *networkInterface
mounts []mount
} }
func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) {
@ -57,9 +58,11 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac
return nil, err return nil, err
} }
var networkInterface *networkInterface var networkInterface *networkInterface
var mounts []mount
for _, container := range cHints.AllHosts { for _, container := range cHints.AllHosts {
if name == container.FullName { if name == container.FullName {
networkInterface = container.NetworkInterface networkInterface = container.NetworkInterface
mounts = container.Mounts
break break
} }
} }
@ -75,6 +78,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac
watches: make(map[string]struct{}), watches: make(map[string]struct{}),
fsInfo: fsInfo, fsInfo: fsInfo,
networkInterface: networkInterface, networkInterface: networkInterface,
mounts: mounts,
}, nil }, nil
} }
@ -164,7 +168,7 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
} }
// Fs. // Fs.
if self.name == "/" { if self.name == "/" || self.mounts != nil {
spec.HasFilesystem = true spec.HasFilesystem = true
} }
@ -175,6 +179,34 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
return spec, nil return spec, nil
} }
func (self *rawContainerHandler) getFsStats(stats *info.ContainerStats) error {
// Get Filesystem information only for the root cgroup.
if self.name == "/" {
filesystems, err := self.fsInfo.GetGlobalFsInfo()
if err != nil {
return err
}
for _, fs := range filesystems {
stats.Filesystem = append(stats.Filesystem, info.FsStats{fs.Device, fs.Capacity, fs.Capacity - fs.Free})
}
} else if self.mounts != nil {
var mountSet map[string]bool
mountSet = make(map[string]bool)
for _, mount := range self.mounts {
mountSet[mount.HostDir] = true
}
filesystems, err := self.fsInfo.GetFsInfoForPath(mountSet)
if err != nil {
return err
}
for _, fs := range filesystems {
stats.Filesystem = append(stats.Filesystem, info.FsStats{fs.Device, fs.Capacity, fs.Capacity - fs.Free})
}
}
return nil
}
func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) {
state := dockerlibcontainer.State{} state := dockerlibcontainer.State{}
if self.networkInterface != nil { if self.networkInterface != nil {
@ -191,16 +223,11 @@ func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Get Filesystem information only for the root cgroup.
if self.name == "/" { err = self.getFsStats(stats)
filesystems, err := self.fsInfo.GetGlobalFsInfo()
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, fs := range filesystems {
stats.Filesystem = append(stats.Filesystem, info.FsStats{fs.Device, fs.Capacity, fs.Capacity - fs.Free})
}
}
return stats, nil return stats, nil
} }

View File

@ -50,13 +50,16 @@ func NewFsInfo() (FsInfo, error) {
return &RealFsInfo{partitions}, nil return &RealFsInfo{partitions}, nil
} }
func (self *RealFsInfo) GetGlobalFsInfo() ([]Fs, error) { func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]bool) ([]Fs, error) {
filesystems := make([]Fs, 0) filesystems := make([]Fs, 0)
deviceSet := make(map[string]bool)
for device, partition := range self.partitions { for device, partition := range self.partitions {
if mountSet == nil || mountSet[partition.mountpoint] == true {
total, free, err := getVfsStats(partition.mountpoint) total, free, err := getVfsStats(partition.mountpoint)
if err != nil { if err != nil {
glog.Errorf("Statvfs failed. Error: %v", err) glog.Errorf("Statvfs failed. Error: %v", err)
} else { } else if deviceSet[device] == false {
deviceSet[device] = true
deviceInfo := DeviceInfo{ deviceInfo := DeviceInfo{
Device: device, Device: device,
Major: uint(partition.major), Major: uint(partition.major),
@ -66,9 +69,14 @@ func (self *RealFsInfo) GetGlobalFsInfo() ([]Fs, error) {
filesystems = append(filesystems, fs) filesystems = append(filesystems, fs)
} }
} }
}
return filesystems, nil return filesystems, nil
} }
func (self *RealFsInfo) GetGlobalFsInfo() ([]Fs, error) {
return self.GetFsInfoForPath(nil)
}
func major(devNumber uint64) uint { func major(devNumber uint64) uint {
return uint((devNumber >> 8) & 0xfff) return uint((devNumber >> 8) & 0xfff)
} }

View File

@ -16,6 +16,9 @@ type FsInfo interface {
// Returns capacity and free space, in bytes, of all the ext2, ext3, ext4 filesystems on the host. // Returns capacity and free space, in bytes, of all the ext2, ext3, ext4 filesystems on the host.
GetGlobalFsInfo() ([]Fs, error) GetGlobalFsInfo() ([]Fs, error)
// Returns capacity and free space, in bytes, of the set of mounts passed.
GetFsInfoForPath(mountSet map[string]bool) ([]Fs, error)
// Returns number of bytes occupied by 'dir'. // Returns number of bytes occupied by 'dir'.
GetDirUsage(dir string) (uint64, error) GetDirUsage(dir string) (uint64, error)