Adding a disk usage progress bar.
This commit is contained in:
parent
b9e70f0240
commit
c21ff1f166
@ -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
|
||||
}
|
||||
|
@ -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"]
|
||||
|
39
fs/fs.go
39
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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -147,6 +147,32 @@ const containersHtmlTemplate = `
|
||||
</div>
|
||||
</div>
|
||||
{{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}}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
|
Loading…
Reference in New Issue
Block a user