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:
parent
08d0aa41d6
commit
b8ed0bd0e3
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
30
fs/fs.go
30
fs/fs.go
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user