Commit 4664b395 authored by Victor Marmol's avatar Victor Marmol

Update cAdvisor godep.

- Fixes working set reporting. - Adds disk Available size. - Adds support for ps inside containers via cAdvisor.
parent fab98059
...@@ -206,88 +206,88 @@ ...@@ -206,88 +206,88 @@
}, },
{ {
"ImportPath": "github.com/google/cadvisor/api", "ImportPath": "github.com/google/cadvisor/api",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/collector", "ImportPath": "github.com/google/cadvisor/collector",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/container", "ImportPath": "github.com/google/cadvisor/container",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/events", "ImportPath": "github.com/google/cadvisor/events",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/fs", "ImportPath": "github.com/google/cadvisor/fs",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/healthz", "ImportPath": "github.com/google/cadvisor/healthz",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/http", "ImportPath": "github.com/google/cadvisor/http",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/info/v1", "ImportPath": "github.com/google/cadvisor/info/v1",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/info/v2", "ImportPath": "github.com/google/cadvisor/info/v2",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/manager", "ImportPath": "github.com/google/cadvisor/manager",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/metrics", "ImportPath": "github.com/google/cadvisor/metrics",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/pages", "ImportPath": "github.com/google/cadvisor/pages",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/storage", "ImportPath": "github.com/google/cadvisor/storage",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/summary", "ImportPath": "github.com/google/cadvisor/summary",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/utils", "ImportPath": "github.com/google/cadvisor/utils",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/validate", "ImportPath": "github.com/google/cadvisor/validate",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/version", "ImportPath": "github.com/google/cadvisor/version",
"Comment": "0.13.0-32-g832c6e9", "Comment": "0.13.0-48-g9535dd2",
"Rev": "832c6e94c325ecdf05da884880c503be45594818" "Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
}, },
{ {
"ImportPath": "github.com/google/go-github/github", "ImportPath": "github.com/google/go-github/github",
......
...@@ -38,6 +38,7 @@ const ( ...@@ -38,6 +38,7 @@ const (
storageApi = "storage" storageApi = "storage"
attributesApi = "attributes" attributesApi = "attributes"
versionApi = "version" versionApi = "version"
psApi = "ps"
) )
// Interface for a cAdvisor API version // Interface for a cAdvisor API version
...@@ -304,7 +305,7 @@ func (self *version2_0) Version() string { ...@@ -304,7 +305,7 @@ func (self *version2_0) Version() string {
} }
func (self *version2_0) SupportedRequestTypes() []string { func (self *version2_0) SupportedRequestTypes() []string {
return []string{versionApi, attributesApi, eventsApi, machineApi, summaryApi, statsApi, specApi, storageApi} return []string{versionApi, attributesApi, eventsApi, machineApi, summaryApi, statsApi, specApi, storageApi, psApi}
} }
func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error { func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
...@@ -391,6 +392,17 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma ...@@ -391,6 +392,17 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
return writeResult(fi, w) return writeResult(fi, w)
case eventsApi: case eventsApi:
return handleEventRequest(request, m, w, r) return handleEventRequest(request, m, w, r)
case psApi:
// reuse container type from request.
// ignore recursive.
// TODO(rjnagal): consider count to limit ps output.
name := getContainerName(request)
glog.V(4).Infof("Api - Spec for container %q, options %+v", name, opt)
ps, err := m.GetProcessList(name, opt)
if err != nil {
return fmt.Errorf("process listing failed: %v", err)
}
return writeResult(ps, w)
default: default:
return fmt.Errorf("unknown request type %q", requestType) return fmt.Errorf("unknown request type %q", requestType)
} }
......
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package base
import (
"time"
"github.com/google/cadvisor/collector"
"github.com/google/cadvisor/info/v2"
)
type Collector struct {
}
var _ collector.Collector = &Collector{}
func (c *Collector) Collect() (time.Time, []v2.Metric, error) {
return time.Now(), []v2.Metrics{}, nil
}
func (c *Collector) Name() string {
return "default"
}
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package base
import ()
...@@ -18,6 +18,8 @@ import ( ...@@ -18,6 +18,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"time" "time"
"github.com/google/cadvisor/info/v2"
) )
type collectorManager struct { type collectorManager struct {
...@@ -46,17 +48,19 @@ func (cm *collectorManager) RegisterCollector(collector Collector) error { ...@@ -46,17 +48,19 @@ func (cm *collectorManager) RegisterCollector(collector Collector) error {
return nil return nil
} }
func (cm *collectorManager) Collect() (time.Time, error) { func (cm *collectorManager) Collect() (time.Time, []v2.Metric, error) {
var errors []error var errors []error
// Collect from all collectors that are ready. // Collect from all collectors that are ready.
var next time.Time var next time.Time
var metrics []v2.Metric
for _, c := range cm.collectors { for _, c := range cm.collectors {
if c.nextCollectionTime.Before(time.Now()) { if c.nextCollectionTime.Before(time.Now()) {
nextCollection, err := c.collector.Collect() nextCollection, newMetrics, err := c.collector.Collect()
if err != nil { if err != nil {
errors = append(errors, err) errors = append(errors, err)
} }
metrics = append(metrics, newMetrics...)
c.nextCollectionTime = nextCollection c.nextCollectionTime = nextCollection
} }
...@@ -66,7 +70,7 @@ func (cm *collectorManager) Collect() (time.Time, error) { ...@@ -66,7 +70,7 @@ func (cm *collectorManager) Collect() (time.Time, error) {
} }
} }
return next, compileErrors(errors) return next, metrics, compileErrors(errors)
} }
// Make an error slice into a single error. // Make an error slice into a single error.
......
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/google/cadvisor/info/v2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
...@@ -27,9 +28,9 @@ type fakeCollector struct { ...@@ -27,9 +28,9 @@ type fakeCollector struct {
collectedFrom int collectedFrom int
} }
func (fc *fakeCollector) Collect() (time.Time, error) { func (fc *fakeCollector) Collect() (time.Time, []v2.Metric, error) {
fc.collectedFrom++ fc.collectedFrom++
return fc.nextCollectionTime, fc.err return fc.nextCollectionTime, []v2.Metric{}, fc.err
} }
func (fc *fakeCollector) Name() string { func (fc *fakeCollector) Name() string {
...@@ -53,7 +54,7 @@ func TestCollect(t *testing.T) { ...@@ -53,7 +54,7 @@ func TestCollect(t *testing.T) {
assert.NoError(cm.RegisterCollector(f2)) assert.NoError(cm.RegisterCollector(f2))
// First collection, everyone gets collected from. // First collection, everyone gets collected from.
nextTime, err := cm.Collect() nextTime, _, err := cm.Collect()
assert.Equal(firstTime, nextTime) assert.Equal(firstTime, nextTime)
assert.NoError(err) assert.NoError(err)
assert.Equal(1, f1.collectedFrom) assert.Equal(1, f1.collectedFrom)
...@@ -62,7 +63,7 @@ func TestCollect(t *testing.T) { ...@@ -62,7 +63,7 @@ func TestCollect(t *testing.T) {
f1.nextCollectionTime = time.Now().Add(2 * time.Hour) f1.nextCollectionTime = time.Now().Add(2 * time.Hour)
// Second collection, only the one that is ready gets collected from. // Second collection, only the one that is ready gets collected from.
nextTime, err = cm.Collect() nextTime, _, err = cm.Collect()
assert.Equal(secondTime, nextTime) assert.Equal(secondTime, nextTime)
assert.NoError(err) assert.NoError(err)
assert.Equal(2, f1.collectedFrom) assert.Equal(2, f1.collectedFrom)
......
...@@ -16,6 +16,8 @@ package collector ...@@ -16,6 +16,8 @@ package collector
import ( import (
"time" "time"
"github.com/google/cadvisor/info/v2"
) )
type FakeCollectorManager struct { type FakeCollectorManager struct {
...@@ -25,7 +27,7 @@ func (fkm *FakeCollectorManager) RegisterCollector(collector Collector) error { ...@@ -25,7 +27,7 @@ func (fkm *FakeCollectorManager) RegisterCollector(collector Collector) error {
return nil return nil
} }
func (fkm *FakeCollectorManager) Collect() (time.Time, error) { func (fkm *FakeCollectorManager) Collect() (time.Time, []v2.Metric, error) {
var zero time.Time var zero time.Time
return zero, nil return zero, []v2.Metric{}, nil
} }
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
package collector package collector
import ( import (
"github.com/google/cadvisor/info/v2"
"time" "time"
) )
...@@ -26,7 +27,7 @@ type Collector interface { ...@@ -26,7 +27,7 @@ type Collector interface {
// Returns the next time this collector should be collected from. // Returns the next time this collector should be collected from.
// Next collection time is always returned, even when an error occurs. // Next collection time is always returned, even when an error occurs.
// A collection time of zero means no more collection. // A collection time of zero means no more collection.
Collect() (time.Time, error) Collect() (time.Time, []v2.Metric, error)
// Name of this collector. // Name of this collector.
Name() string Name() string
...@@ -41,5 +42,5 @@ type CollectorManager interface { ...@@ -41,5 +42,5 @@ type CollectorManager interface {
// at which a collector will be ready to collect from. // at which a collector will be ready to collect from.
// Next collection time is always returned, even when an error occurs. // Next collection time is always returned, even when an error occurs.
// A collection time of zero means no more collection. // A collection time of zero means no more collection.
Collect() (time.Time, error) Collect() (time.Time, []v2.Metric, error)
} }
...@@ -325,8 +325,7 @@ func (self *dockerContainerHandler) ListThreads(listType container.ListType) ([] ...@@ -325,8 +325,7 @@ func (self *dockerContainerHandler) ListThreads(listType container.ListType) ([]
} }
func (self *dockerContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { func (self *dockerContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
// TODO(vmarmol): Implement. return containerLibcontainer.GetProcesses(self.cgroupManager)
return nil, nil
} }
func (self *dockerContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error { func (self *dockerContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error {
......
...@@ -96,6 +96,14 @@ func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info. ...@@ -96,6 +96,14 @@ func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info.
return stats, nil return stats, nil
} }
func GetProcesses(cgroupManager cgroups.Manager) ([]int, error) {
pids, err := cgroupManager.GetPids()
if err != nil {
return nil, err
}
return pids, nil
}
func DockerStateDir(dockerRoot string) string { func DockerStateDir(dockerRoot string) string {
return path.Join(dockerRoot, "containers") return path.Join(dockerRoot, "containers")
} }
...@@ -186,10 +194,21 @@ func toContainerStats2(s *cgroups.Stats, ret *info.ContainerStats) { ...@@ -186,10 +194,21 @@ func toContainerStats2(s *cgroups.Stats, ret *info.ContainerStats) {
ret.Memory.HierarchicalData.Pgmajfault = v ret.Memory.HierarchicalData.Pgmajfault = v
} }
if v, ok := s.MemoryStats.Stats["total_inactive_anon"]; ok { if v, ok := s.MemoryStats.Stats["total_inactive_anon"]; ok {
ret.Memory.WorkingSet = ret.Memory.Usage - v workingSet := ret.Memory.Usage
if v, ok := s.MemoryStats.Stats["total_active_file"]; ok { if workingSet < v {
ret.Memory.WorkingSet -= v workingSet = 0
} else {
workingSet -= v
}
if v, ok := s.MemoryStats.Stats["total_inactive_file"]; ok {
if workingSet < v {
workingSet = 0
} else {
workingSet -= v
}
} }
ret.Memory.WorkingSet = workingSet
} }
} }
......
...@@ -264,6 +264,7 @@ func (self *rawContainerHandler) getFsStats(stats *info.ContainerStats) error { ...@@ -264,6 +264,7 @@ func (self *rawContainerHandler) getFsStats(stats *info.ContainerStats) error {
Device: fs.Device, Device: fs.Device,
Limit: fs.Capacity, Limit: fs.Capacity,
Usage: fs.Capacity - fs.Free, Usage: fs.Capacity - fs.Free,
Available: fs.Available,
ReadsCompleted: fs.DiskStats.ReadsCompleted, ReadsCompleted: fs.DiskStats.ReadsCompleted,
ReadsMerged: fs.DiskStats.ReadsMerged, ReadsMerged: fs.DiskStats.ReadsMerged,
SectorsRead: fs.DiskStats.SectorsRead, SectorsRead: fs.DiskStats.SectorsRead,
...@@ -398,8 +399,7 @@ func (self *rawContainerHandler) ListThreads(listType container.ListType) ([]int ...@@ -398,8 +399,7 @@ func (self *rawContainerHandler) ListThreads(listType container.ListType) ([]int
} }
func (self *rawContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { func (self *rawContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
// TODO(vmarmol): Implement return libcontainer.GetProcesses(self.cgroupManager)
return nil, nil
} }
func (self *rawContainerHandler) watchDirectory(dir string, containerName string) error { func (self *rawContainerHandler) watchDirectory(dir string, containerName string) error {
......
...@@ -20,6 +20,7 @@ package fs ...@@ -20,6 +20,7 @@ package fs
/* /*
extern int getBytesFree(const char *path, unsigned long long *bytes); extern int getBytesFree(const char *path, unsigned long long *bytes);
extern int getBytesTotal(const char *path, unsigned long long *bytes); extern int getBytesTotal(const char *path, unsigned long long *bytes);
extern int getBytesAvail(const char *path, unsigned long long *bytes);
*/ */
import "C" import "C"
...@@ -176,7 +177,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er ...@@ -176,7 +177,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
_, hasMount := mountSet[partition.mountpoint] _, hasMount := mountSet[partition.mountpoint]
_, hasDevice := deviceSet[device] _, hasDevice := deviceSet[device]
if mountSet == nil || (hasMount && !hasDevice) { if mountSet == nil || (hasMount && !hasDevice) {
total, free, err := getVfsStats(partition.mountpoint) total, free, avail, 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 {
...@@ -186,7 +187,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er ...@@ -186,7 +187,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
Major: uint(partition.major), Major: uint(partition.major),
Minor: uint(partition.minor), Minor: uint(partition.minor),
} }
fs := Fs{deviceInfo, total, free, diskStatsMap[device]} fs := Fs{deviceInfo, total, free, avail, diskStatsMap[device]}
filesystems = append(filesystems, fs) filesystems = append(filesystems, fs)
} }
} }
...@@ -287,18 +288,22 @@ func (self *RealFsInfo) GetDirUsage(dir string) (uint64, error) { ...@@ -287,18 +288,22 @@ func (self *RealFsInfo) GetDirUsage(dir string) (uint64, error) {
return usageInKb * 1024, nil return usageInKb * 1024, nil
} }
func getVfsStats(path string) (total uint64, free uint64, err error) { func getVfsStats(path string) (total uint64, free uint64, avail uint64, err error) {
_p0, err := syscall.BytePtrFromString(path) _p0, err := syscall.BytePtrFromString(path)
if err != nil { if err != nil {
return 0, 0, err return 0, 0, 0, err
} }
res, err := C.getBytesFree((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&free))) res, err := C.getBytesFree((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&free)))
if res != 0 { if res != 0 {
return 0, 0, err return 0, 0, 0, err
} }
res, err = C.getBytesTotal((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&total))) res, err = C.getBytesTotal((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&total)))
if res != 0 { if res != 0 {
return 0, 0, err return 0, 0, 0, err
} }
return total, free, nil res, err = C.getBytesAvail((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&avail)))
if res != 0 {
return 0, 0, 0, err
}
return total, free, avail, nil
} }
...@@ -21,3 +21,14 @@ int getBytesTotal(const char *path, unsigned long long *bytes) { ...@@ -21,3 +21,14 @@ int getBytesTotal(const char *path, unsigned long long *bytes) {
*bytes = buf.f_frsize * buf.f_blocks; *bytes = buf.f_frsize * buf.f_blocks;
return 0; return 0;
} }
// Bytes available to non-root.
int getBytesAvail(const char *path, unsigned long long *bytes) {
struct statvfs buf;
int res;
if ((res = statvfs(path, &buf)) && res != 0) {
return -1;
}
*bytes = buf.f_frsize * buf.f_bavail;
return 0;
}
...@@ -24,6 +24,7 @@ type Fs struct { ...@@ -24,6 +24,7 @@ type Fs struct {
DeviceInfo DeviceInfo
Capacity uint64 Capacity uint64
Free uint64 Free uint64
Available uint64
DiskStats DiskStats DiskStats DiskStats
} }
......
...@@ -341,6 +341,9 @@ type FsStats struct { ...@@ -341,6 +341,9 @@ type FsStats struct {
// Number of bytes that is consumed by the container on this filesystem. // Number of bytes that is consumed by the container on this filesystem.
Usage uint64 `json:"usage"` Usage uint64 `json:"usage"`
// Number of bytes available for non-root user.
Available uint64 `json:"available"`
// Number of reads completed // Number of reads completed
// This is the total number of reads completed successfully. // This is the total number of reads completed successfully.
ReadsCompleted uint64 `json:"reads_completed"` ReadsCompleted uint64 `json:"reads_completed"`
......
...@@ -151,6 +151,9 @@ type FsInfo struct { ...@@ -151,6 +151,9 @@ type FsInfo struct {
// Filesystem usage in bytes. // Filesystem usage in bytes.
Capacity uint64 `json:"capacity"` Capacity uint64 `json:"capacity"`
// Bytes available for non-root use.
Available uint64 `json:"available"`
// Number of bytes used on this filesystem. // Number of bytes used on this filesystem.
Usage uint64 `json:"usage"` Usage uint64 `json:"usage"`
...@@ -166,3 +169,16 @@ type RequestOptions struct { ...@@ -166,3 +169,16 @@ type RequestOptions struct {
// Whether to include stats for child subcontainers. // Whether to include stats for child subcontainers.
Recursive bool `json:"recursive"` Recursive bool `json:"recursive"`
} }
type ProcessInfo struct {
User string `json:"user"`
Pid int `json:"pid"`
Ppid int `json:"parent_pid"`
StartTime string `json:"start_time"`
PercentCpu string `json:"percent_cpu"`
RSS string `json:"rss"`
VirtualSize string `json:"virtual_size"`
Status string `json:"status"`
RunningTime string `json:"running_time"`
Cmd string `json:"cmd"`
}
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v2
import (
"time"
)
// Type of metric being exported.
type MetricType string
const (
// Instantaneous value. May increase or decrease.
MetricGauge MetricType = "gauge"
// A counter-like value that is only expected to increase.
MetricCumulative = "cumulative"
// Rate over a time period.
MetricDelta = "delta"
)
// An exported metric.
type Metric struct {
// The name of the metric.
Name string `json:"name"`
// Type of the metric.
Type MetricType `json:"type"`
// Metadata associated with this metric.
Labels map[string]string
// Value of the metric. Only one of these values will be
// available according to the output type of the metric.
// If no values are available, there are no data points.
IntPoints []IntPoint `json:"int_points,omitempty"`
FloatPoints []FloatPoint `json:"float_points,omitempty"`
}
// An integer metric data point.
type IntPoint struct {
// Time at which the metric was queried
Timestamp time.Time `json:"timestamp"`
// The value of the metric at this point.
Value int64 `json:"value"`
}
// A float metric data point.
type FloatPoint struct {
// Time at which the metric was queried
Timestamp time.Time `json:"timestamp"`
// The value of the metric at this point.
Value float64 `json:"value"`
}
...@@ -18,7 +18,10 @@ import ( ...@@ -18,7 +18,10 @@ import (
"flag" "flag"
"fmt" "fmt"
"math" "math"
"os/exec"
"sort" "sort"
"strconv"
"strings"
"sync" "sync"
"time" "time"
...@@ -113,6 +116,63 @@ func (c *containerData) DerivedStats() (v2.DerivedStats, error) { ...@@ -113,6 +116,63 @@ func (c *containerData) DerivedStats() (v2.DerivedStats, error) {
return c.summaryReader.DerivedStats() return c.summaryReader.DerivedStats()
} }
func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
// report all processes for root.
isRoot := c.info.Name == "/"
pidMap := map[int]bool{}
if !isRoot {
pids, err := c.handler.ListProcesses(container.ListSelf)
if err != nil {
return nil, err
}
for _, pid := range pids {
pidMap[pid] = true
}
}
// TODO(rjnagal): Take format as an option?
format := "user,pid,ppid,stime,pcpu,rss,vsz,stat,time,comm"
args := []string{"-e", "-o", format}
expectedFields := 10
out, err := exec.Command("ps", args...).Output()
if err != nil {
return nil, fmt.Errorf("failed to execute ps command: %v", err)
}
processes := []v2.ProcessInfo{}
lines := strings.Split(string(out), "\n")
for _, line := range lines[1:] {
if len(line) == 0 {
continue
}
fields := strings.Fields(line)
if len(fields) < expectedFields {
return nil, fmt.Errorf("expected at least %d fields, found %d: output: %q", expectedFields, len(fields), line)
}
pid, err := strconv.Atoi(fields[1])
if err != nil {
return nil, fmt.Errorf("invalid pid %q: %v", fields[1], err)
}
ppid, err := strconv.Atoi(fields[2])
if err != nil {
return nil, fmt.Errorf("invalid ppid %q: %v", fields[2], err)
}
if isRoot || pidMap[pid] == true {
processes = append(processes, v2.ProcessInfo{
User: fields[0],
Pid: pid,
Ppid: ppid,
StartTime: fields[3],
PercentCpu: fields[4],
RSS: fields[5],
VirtualSize: fields[6],
Status: fields[7],
RunningTime: fields[8],
Cmd: strings.Join(fields[9:], " "),
})
}
}
return processes, nil
}
func newContainerData(containerName string, memoryStorage *memory.InMemoryStorage, handler container.ContainerHandler, loadReader cpuload.CpuLoadReader, logUsage bool, collectorManager collector.CollectorManager) (*containerData, error) { func newContainerData(containerName string, memoryStorage *memory.InMemoryStorage, handler container.ContainerHandler, loadReader cpuload.CpuLoadReader, logUsage bool, collectorManager collector.CollectorManager) (*containerData, error) {
if memoryStorage == nil { if memoryStorage == nil {
return nil, fmt.Errorf("nil memory storage") return nil, fmt.Errorf("nil memory storage")
...@@ -232,8 +292,9 @@ func (c *containerData) housekeeping() { ...@@ -232,8 +292,9 @@ func (c *containerData) housekeeping() {
} }
} }
// TODO(vmarmol): Export metrics.
// Run custom collectors. // Run custom collectors.
nextCollectionTime, err := c.collectorManager.Collect() nextCollectionTime, _, err := c.collectorManager.Collect()
if err != nil && c.allowErrorLogging() { if err != nil && c.allowErrorLogging() {
glog.Warningf("[%s] Collection failed: %v", c.info.Name, err) glog.Warningf("[%s] Collection failed: %v", c.info.Name, err)
} }
......
...@@ -90,6 +90,9 @@ type Manager interface { ...@@ -90,6 +90,9 @@ type Manager interface {
// Returns information for all global filesystems if label is empty. // Returns information for all global filesystems if label is empty.
GetFsInfo(label string) ([]v2.FsInfo, error) GetFsInfo(label string) ([]v2.FsInfo, error)
// Get ps output for a container.
GetProcessList(containerName string, options v2.RequestOptions) ([]v2.ProcessInfo, error)
// Get events streamed through passedChannel that fit the request. // Get events streamed through passedChannel that fit the request.
WatchForEvents(request *events.Request) (*events.EventChannel, error) WatchForEvents(request *events.Request) (*events.EventChannel, error)
...@@ -609,6 +612,7 @@ func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) { ...@@ -609,6 +612,7 @@ func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) {
Mountpoint: mountpoint, Mountpoint: mountpoint,
Capacity: fs.Limit, Capacity: fs.Limit,
Usage: fs.Usage, Usage: fs.Usage,
Available: fs.Available,
Labels: labels, Labels: labels,
} }
fsInfo = append(fsInfo, fi) fsInfo = append(fsInfo, fi)
...@@ -640,6 +644,27 @@ func (m *manager) Exists(containerName string) bool { ...@@ -640,6 +644,27 @@ func (m *manager) Exists(containerName string) bool {
return false return false
} }
func (m *manager) GetProcessList(containerName string, options v2.RequestOptions) ([]v2.ProcessInfo, error) {
// override recursive. Only support single container listing.
options.Recursive = false
conts, err := m.getRequestedContainers(containerName, options)
if err != nil {
return nil, err
}
if len(conts) != 1 {
return nil, fmt.Errorf("Expected the request to match only one container")
}
// TODO(rjnagal): handle count? Only if we can do count by type (eg. top 5 cpu users)
ps := []v2.ProcessInfo{}
for _, cont := range conts {
ps, err = cont.GetProcessList()
if err != nil {
return nil, err
}
}
return ps, nil
}
// Create a container. // Create a container.
func (m *manager) createContainer(containerName string) error { func (m *manager) createContainer(containerName string) error {
handler, accept, err := container.NewContainerHandler(containerName) handler, accept, err := container.NewContainerHandler(containerName)
......
...@@ -99,3 +99,8 @@ func (c *ManagerMock) GetFsInfo() ([]v2.FsInfo, error) { ...@@ -99,3 +99,8 @@ func (c *ManagerMock) GetFsInfo() ([]v2.FsInfo, error) {
args := c.Called() args := c.Called()
return args.Get(0).([]v2.FsInfo), args.Error(1) return args.Get(0).([]v2.FsInfo), args.Error(1)
} }
func (c *ManagerMock) GetProcessList(name string, options v2.RequestOptions) ([]v2.ProcessInfo, error) {
args := c.Called()
return args.Get(0).([]v2.ProcessInfo), args.Error(1)
}
...@@ -108,6 +108,12 @@ const containersHtmlTemplate = ` ...@@ -108,6 +108,12 @@ const containersHtmlTemplate = `
</div> </div>
<div id="usage-gauge" class="panel-body"></div> <div id="usage-gauge" class="panel-body"></div>
</div> </div>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Processes</h3>
</div>
<div id="processes-top" class="panel-body"></div>
</div>
{{if .CpuAvailable}} {{if .CpuAvailable}}
<div class="panel panel-primary"> <div class="panel panel-primary">
<div class="panel-heading"> <div class="panel-heading">
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment