Adding a disk usage progress bar.

This commit is contained in:
Vishnu Kannan 2014-09-30 23:38:34 +00:00
parent b9e70f0240
commit c21ff1f166
7 changed files with 77 additions and 23 deletions

View File

@ -45,6 +45,10 @@ type rawContainerHandler struct {
} }
func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) {
fsInfo, err := fs.NewFsInfo()
if err != nil {
return nil, err
}
return &rawContainerHandler{ return &rawContainerHandler{
name: name, name: name,
cgroup: &cgroups.Cgroup{ cgroup: &cgroups.Cgroup{
@ -55,7 +59,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac
machineInfoFactory: machineInfoFactory, machineInfoFactory: machineInfoFactory,
stopWatcher: make(chan error), stopWatcher: make(chan error),
watches: make(map[string]struct{}), watches: make(map[string]struct{}),
fsInfo: fs.NewFsInfo(), fsInfo: fsInfo,
}, nil }, nil
} }
@ -146,7 +150,7 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
// Fs. // Fs.
if self.name == "/" { if self.name == "/" {
spec.HasFs = true spec.HasFilesystem = true
} }
return spec, nil return spec, nil
} }
@ -158,7 +162,7 @@ func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) {
} }
// Get Filesystem information only for the root cgroup. // Get Filesystem information only for the root cgroup.
if self.name == "/" { if self.name == "/" {
stats.Fs, err = self.fsInfo.GetFsStats() stats.Filesystem, err = self.fsInfo.GetFsStats()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3,7 +3,6 @@ MAINTAINER dengnan@google.com vmarmol@google.com vishnuk@google.com
# Grab cadvisor from the staging directory. # Grab cadvisor from the staging directory.
ADD cadvisor /usr/bin/cadvisor ADD cadvisor /usr/bin/cadvisor
RUN mkdir /rootfs
EXPOSE 8080 EXPOSE 8080
ENTRYPOINT ["/usr/bin/cadvisor"] ENTRYPOINT ["/usr/bin/cadvisor"]

View File

@ -18,41 +18,50 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
) )
type FsInfoImpl struct{} type partition struct {
mountpoint string
func NewFsInfo() FsInfo { major uint32
return &FsInfoImpl{} minor uint32
} }
func (*FsInfoImpl) GetFsStats() ([]FsStat, error) { type FsInfoImpl struct {
filesystems := make([]FsStat, 0) partitions map[string]partition
}
func NewFsInfo() (FsInfo, error) {
mounts, err := mount.GetMounts() mounts, err := mount.GetMounts()
if err != nil { if err != nil {
return nil, err return nil, err
} }
processedPartitions := make(map[string]bool, 0) partitions := make(map[string]partition, 0)
for _, mount := range mounts { for _, mount := range mounts {
if !strings.HasPrefix(mount.Fstype, "ext") { if !strings.HasPrefix(mount.Fstype, "ext") {
continue continue
} }
// Avoid bind mounts. // Avoid bind mounts.
if _, ok := processedPartitions[mount.Source]; ok { if _, ok := partitions[mount.Source]; ok {
continue 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 { if err != nil {
glog.Errorf("Statvfs failed. Error: %v", err) glog.Errorf("Statvfs failed. Error: %v", err)
} else { } 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 := FsStats{
fsStat := FsStat{ Device: device,
Device: mount.Source, Major: uint(partition.major),
Major: uint(mount.Major), Minor: uint(partition.minor),
Minor: uint(mount.Minor),
Capacity: total, Capacity: total,
Free: free, Free: free,
} }
filesystems = append(filesystems, fsStat) filesystems = append(filesystems, fsStat)
processedPartitions[mount.Source] = true
} }
} }
return filesystems, nil return filesystems, nil

View File

@ -1,6 +1,6 @@
package fs package fs
type FsStat struct { type FsStats struct {
Device string `json:"device,omitempty"` Device string `json:"device,omitempty"`
Major uint `json:"major"` Major uint `json:"major"`
Minor uint `json:"minor"` Minor uint `json:"minor"`
@ -10,5 +10,5 @@ type FsStat struct {
type FsInfo interface { 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.
GetFsStats() ([]FsStat, error) GetFsStats() ([]FsStats, error)
} }

View File

@ -50,7 +50,7 @@ type ContainerSpec struct {
HasNetwork bool `json:"has_network"` 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 // Container reference contains enough information to uniquely identify a container
@ -241,7 +241,7 @@ type ContainerStats struct {
Memory *MemoryStats `json:"memory,omitempty"` Memory *MemoryStats `json:"memory,omitempty"`
Network *NetworkStats `json:"network,omitempty"` Network *NetworkStats `json:"network,omitempty"`
// Filesystem statistics // 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 // Makes a deep copy of the ContainerStats and returns a pointer to the new

View File

@ -27,6 +27,7 @@ import (
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/google/cadvisor/fs"
"github.com/google/cadvisor/info" "github.com/google/cadvisor/info"
"github.com/google/cadvisor/manager" "github.com/google/cadvisor/manager"
) )
@ -98,6 +99,8 @@ var funcMap = template.FuncMap{
"getMemoryUsagePercent": getMemoryUsagePercent, "getMemoryUsagePercent": getMemoryUsagePercent,
"getHotMemoryPercent": getHotMemoryPercent, "getHotMemoryPercent": getHotMemoryPercent,
"getColdMemoryPercent": getColdMemoryPercent, "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. // 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 CpuAvailable bool
MemoryAvailable bool MemoryAvailable bool
NetworkAvailable bool NetworkAvailable bool
FsAvailable bool
} }
func init() { func init() {
@ -252,6 +256,17 @@ func getColdMemoryPercent(spec *info.ContainerSpec, stats []*info.ContainerStats
return toMemoryPercent((latestStats.Usage)-(latestStats.WorkingSet), spec, machine) 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 { func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
start := time.Now() start := time.Now()
@ -312,6 +327,7 @@ func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL)
CpuAvailable: cont.Spec.HasCpu, CpuAvailable: cont.Spec.HasCpu,
MemoryAvailable: cont.Spec.HasMemory, MemoryAvailable: cont.Spec.HasMemory,
NetworkAvailable: cont.Spec.HasNetwork, NetworkAvailable: cont.Spec.HasNetwork,
FsAvailable: cont.Spec.HasFilesystem,
} }
err = pageTemplate.Execute(w, data) err = pageTemplate.Execute(w, data)
if err != nil { if err != nil {

View File

@ -147,6 +147,32 @@ const containersHtmlTemplate = `
</div> </div>
</div> </div>
{{end}} {{end}}
{{if .FsAvailable}}
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Filesystem</h3>
</div>
<div class="panel-body">
{{with getFsStats .Stats}}
{{range .}}
<div class="row col-sm-12">
<h4>Partition: {{.Device}}</h4>
</div>
<div class="col-sm-9">
<div class="progress">
<div id="memory-usage-chart"></div>
<div class="progress-bar progress-bar-danger" style="width: {{getFsUsagePercent .Capacity .Free}}%">
</div>
</div>
</div>
<div class="col-sm-3">
{{printSize .Capacity}} {{printUnit .Capacity}} ({{getFsUsagePercent .Capacity .Free}}%)
</div>
{{end}}
{{end}}
</div>
</div>
{{end}}
{{if .NetworkAvailable}} {{if .NetworkAvailable}}
<div class="panel panel-primary"> <div class="panel panel-primary">
<div class="panel-heading"> <div class="panel-heading">