diff --git a/container/raw/handler.go b/container/raw/handler.go index 6d6fd7a9..6441f652 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -45,6 +45,10 @@ type rawContainerHandler struct { } func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { + fsInfo, err := fs.NewFsInfo() + if err != nil { + return nil, err + } return &rawContainerHandler{ name: name, cgroup: &cgroups.Cgroup{ @@ -55,7 +59,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac machineInfoFactory: machineInfoFactory, stopWatcher: make(chan error), watches: make(map[string]struct{}), - fsInfo: fs.NewFsInfo(), + fsInfo: fsInfo, }, nil } @@ -146,7 +150,7 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) { // Fs. if self.name == "/" { - spec.HasFs = true + spec.HasFilesystem = true } return spec, nil } @@ -158,7 +162,7 @@ func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { } // Get Filesystem information only for the root cgroup. if self.name == "/" { - stats.Fs, err = self.fsInfo.GetFsStats() + stats.Filesystem, err = self.fsInfo.GetFsStats() if err != nil { return nil, err } diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 5702ab63..1c1b2d28 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -3,7 +3,6 @@ MAINTAINER dengnan@google.com vmarmol@google.com vishnuk@google.com # Grab cadvisor from the staging directory. ADD cadvisor /usr/bin/cadvisor -RUN mkdir /rootfs EXPOSE 8080 ENTRYPOINT ["/usr/bin/cadvisor"] diff --git a/fs/fs.go b/fs/fs.go index a5bd2331..4309e0db 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -18,41 +18,50 @@ import ( "github.com/golang/glog" ) -type FsInfoImpl struct{} - -func NewFsInfo() FsInfo { - return &FsInfoImpl{} +type partition struct { + mountpoint string + major uint32 + minor uint32 } -func (*FsInfoImpl) GetFsStats() ([]FsStat, error) { - filesystems := make([]FsStat, 0) +type FsInfoImpl struct { + partitions map[string]partition +} + +func NewFsInfo() (FsInfo, error) { mounts, err := mount.GetMounts() if err != nil { return nil, err } - processedPartitions := make(map[string]bool, 0) + partitions := make(map[string]partition, 0) for _, mount := range mounts { if !strings.HasPrefix(mount.Fstype, "ext") { continue } // Avoid bind mounts. - if _, ok := processedPartitions[mount.Source]; ok { + if _, ok := partitions[mount.Source]; ok { continue } - total, free, err := getVfsStats(mount.Mountpoint) + partitions[mount.Source] = partition{mount.Mountpoint, uint32(mount.Major), uint32(mount.Minor)} + } + return &FsInfoImpl{partitions}, nil +} + +func (self *FsInfoImpl) GetFsStats() ([]FsStats, error) { + filesystems := make([]FsStats, 0) + for device, partition := range self.partitions { + total, free, err := getVfsStats(partition.mountpoint) if err != nil { glog.Errorf("Statvfs failed. Error: %v", err) } else { - glog.V(1).Infof("%s is an %s partition at %s. Total: %d, Free: %d", mount.Source, mount.Fstype, mount.Mountpoint, total, free) - fsStat := FsStat{ - Device: mount.Source, - Major: uint(mount.Major), - Minor: uint(mount.Minor), + fsStat := FsStats{ + Device: device, + Major: uint(partition.major), + Minor: uint(partition.minor), Capacity: total, Free: free, } filesystems = append(filesystems, fsStat) - processedPartitions[mount.Source] = true } } return filesystems, nil diff --git a/fs/types.go b/fs/types.go index 78294b85..4fabdbc2 100644 --- a/fs/types.go +++ b/fs/types.go @@ -1,6 +1,6 @@ package fs -type FsStat struct { +type FsStats struct { Device string `json:"device,omitempty"` Major uint `json:"major"` Minor uint `json:"minor"` @@ -10,5 +10,5 @@ type FsStat struct { type FsInfo interface { // Returns capacity and free space, in bytes, of all the ext2, ext3, ext4 filesystems on the host. - GetFsStats() ([]FsStat, error) + GetFsStats() ([]FsStats, error) } diff --git a/info/container.go b/info/container.go index 83197b39..22c31e78 100644 --- a/info/container.go +++ b/info/container.go @@ -50,7 +50,7 @@ type ContainerSpec struct { HasNetwork bool `json:"has_network"` - HasFs bool `json:"has_fs"` + HasFilesystem bool `json:"has_filesystem"` } // Container reference contains enough information to uniquely identify a container @@ -241,7 +241,7 @@ type ContainerStats struct { Memory *MemoryStats `json:"memory,omitempty"` Network *NetworkStats `json:"network,omitempty"` // Filesystem statistics - Fs []fs.FsStat `json:"fs,omitempty"` + Filesystem []fs.FsStats `json:"filesystem,omitempty"` } // Makes a deep copy of the ContainerStats and returns a pointer to the new diff --git a/pages/containers.go b/pages/containers.go index eca961aa..18b86ccd 100644 --- a/pages/containers.go +++ b/pages/containers.go @@ -27,6 +27,7 @@ import ( "time" "github.com/golang/glog" + "github.com/google/cadvisor/fs" "github.com/google/cadvisor/info" "github.com/google/cadvisor/manager" ) @@ -98,6 +99,8 @@ var funcMap = template.FuncMap{ "getMemoryUsagePercent": getMemoryUsagePercent, "getHotMemoryPercent": getHotMemoryPercent, "getColdMemoryPercent": getColdMemoryPercent, + "getFsStats": getFsStats, + "getFsUsagePercent": getFsUsagePercent, } // TODO(vmarmol): Consider housekeeping Spec too so we can show changes through time. We probably don't need it ever second though. @@ -115,6 +118,7 @@ type pageData struct { CpuAvailable bool MemoryAvailable bool NetworkAvailable bool + FsAvailable bool } func init() { @@ -252,6 +256,17 @@ func getColdMemoryPercent(spec *info.ContainerSpec, stats []*info.ContainerStats return toMemoryPercent((latestStats.Usage)-(latestStats.WorkingSet), spec, machine) } +func getFsStats(stats []*info.ContainerStats) []fs.FsStats { + if len(stats) == 0 { + return []fs.FsStats{} + } + return stats[len(stats)-1].Filesystem +} + +func getFsUsagePercent(capacity, free uint64) uint64 { + return uint64((float64(capacity-free) / float64(capacity)) * 100) +} + func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error { start := time.Now() @@ -312,6 +327,7 @@ func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) CpuAvailable: cont.Spec.HasCpu, MemoryAvailable: cont.Spec.HasMemory, NetworkAvailable: cont.Spec.HasNetwork, + FsAvailable: cont.Spec.HasFilesystem, } err = pageTemplate.Execute(w, data) if err != nil { diff --git a/pages/containers_html.go b/pages/containers_html.go index c883790e..71c87bf2 100644 --- a/pages/containers_html.go +++ b/pages/containers_html.go @@ -147,6 +147,32 @@ const containersHtmlTemplate = ` {{end}} + {{if .FsAvailable}} +
+
+

Filesystem

+
+
+ {{with getFsStats .Stats}} + {{range .}} +
+

Partition: {{.Device}}

+
+
+
+
+
+
+
+
+
+ {{printSize .Capacity}} {{printUnit .Capacity}} ({{getFsUsagePercent .Capacity .Free}}%) +
+ {{end}} + {{end}} +
+
+ {{end}} {{if .NetworkAvailable}}