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

@ -32,8 +32,14 @@ type containerHints struct {
}
type containerHint struct {
FullName string `json:"full_path,omitempty"`
NetworkInterface *networkInterface `json:"network_interface,omitempty"`
FullName string `json:"full_path,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 {

View File

@ -15,6 +15,25 @@ func TestGetContainerHintsFromFile(t *testing.T) {
cHints.AllHosts[0].NetworkInterface.VethChild != "eth1" {
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) {

View File

@ -45,6 +45,7 @@ type rawContainerHandler struct {
watches map[string]struct{}
fsInfo fs.FsInfo
networkInterface *networkInterface
mounts []mount
}
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
}
var networkInterface *networkInterface
var mounts []mount
for _, container := range cHints.AllHosts {
if name == container.FullName {
networkInterface = container.NetworkInterface
mounts = container.Mounts
break
}
}
@ -74,7 +77,8 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac
stopWatcher: make(chan error),
watches: make(map[string]struct{}),
fsInfo: fsInfo,
networkInterface: networkInterface,
networkInterface: networkInterface,
mounts: mounts,
}, nil
}
@ -164,7 +168,7 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
}
// Fs.
if self.name == "/" {
if self.name == "/" || self.mounts != nil {
spec.HasFilesystem = true
}
@ -175,6 +179,34 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
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) {
state := dockerlibcontainer.State{}
if self.networkInterface != nil {
@ -191,15 +223,10 @@ func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) {
if err != nil {
return nil, err
}
// Get Filesystem information only for the root cgroup.
if self.name == "/" {
filesystems, err := self.fsInfo.GetGlobalFsInfo()
if err != nil {
return nil, err
}
for _, fs := range filesystems {
stats.Filesystem = append(stats.Filesystem, info.FsStats{fs.Device, fs.Capacity, fs.Capacity - fs.Free})
}
err = self.getFsStats(stats)
if err != nil {
return nil, err
}
return stats, nil

View File

@ -50,25 +50,33 @@ func NewFsInfo() (FsInfo, error) {
return &RealFsInfo{partitions}, nil
}
func (self *RealFsInfo) GetGlobalFsInfo() ([]Fs, error) {
func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]bool) ([]Fs, error) {
filesystems := make([]Fs, 0)
deviceSet := make(map[string]bool)
for device, partition := range self.partitions {
total, free, err := getVfsStats(partition.mountpoint)
if err != nil {
glog.Errorf("Statvfs failed. Error: %v", err)
} else {
deviceInfo := DeviceInfo{
Device: device,
Major: uint(partition.major),
Minor: uint(partition.minor),
if mountSet == nil || mountSet[partition.mountpoint] == true {
total, free, err := getVfsStats(partition.mountpoint)
if err != nil {
glog.Errorf("Statvfs failed. Error: %v", err)
} else if deviceSet[device] == false {
deviceSet[device] = true
deviceInfo := DeviceInfo{
Device: device,
Major: uint(partition.major),
Minor: uint(partition.minor),
}
fs := Fs{deviceInfo, total, free}
filesystems = append(filesystems, fs)
}
fs := Fs{deviceInfo, total, free}
filesystems = append(filesystems, fs)
}
}
return filesystems, nil
}
func (self *RealFsInfo) GetGlobalFsInfo() ([]Fs, error) {
return self.GetFsInfoForPath(nil)
}
func major(devNumber uint64) uint {
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.
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'.
GetDirUsage(dir string) (uint64, error)