diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index b2f84f5006b..876e217f852 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -206,88 +206,88 @@ }, { "ImportPath": "github.com/google/cadvisor/api", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/collector", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/container", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/events", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/fs", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/healthz", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/http", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/info/v1", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/info/v2", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/manager", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/metrics", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/pages", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/storage", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/summary", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/utils", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/validate", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/cadvisor/version", - "Comment": "0.13.0-32-g832c6e9", - "Rev": "832c6e94c325ecdf05da884880c503be45594818" + "Comment": "0.13.0-48-g9535dd2", + "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" }, { "ImportPath": "github.com/google/go-github/github", diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go b/Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go index 3dbf43b94d0..bba8be5121d 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go @@ -38,6 +38,7 @@ const ( storageApi = "storage" attributesApi = "attributes" versionApi = "version" + psApi = "ps" ) // Interface for a cAdvisor API version @@ -304,7 +305,7 @@ func (self *version2_0) Version() 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 { @@ -391,6 +392,17 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma return writeResult(fi, w) case eventsApi: 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: return fmt.Errorf("unknown request type %q", requestType) } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/collector/base/collector.go b/Godeps/_workspace/src/github.com/google/cadvisor/collector/base/collector.go new file mode 100644 index 00000000000..e3043cde9de --- /dev/null +++ b/Godeps/_workspace/src/github.com/google/cadvisor/collector/base/collector.go @@ -0,0 +1,35 @@ +// 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" +} diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/collector/base/metrics.go b/Godeps/_workspace/src/github.com/google/cadvisor/collector/base/metrics.go new file mode 100644 index 00000000000..3cabf53ac54 --- /dev/null +++ b/Godeps/_workspace/src/github.com/google/cadvisor/collector/base/metrics.go @@ -0,0 +1,17 @@ +// 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 () diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/collector/collector_manager.go b/Godeps/_workspace/src/github.com/google/cadvisor/collector/collector_manager.go index 0641d199dfc..a3df12ccbd9 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/collector/collector_manager.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/collector/collector_manager.go @@ -18,6 +18,8 @@ import ( "fmt" "strings" "time" + + "github.com/google/cadvisor/info/v2" ) type collectorManager struct { @@ -46,17 +48,19 @@ func (cm *collectorManager) RegisterCollector(collector Collector) error { return nil } -func (cm *collectorManager) Collect() (time.Time, error) { +func (cm *collectorManager) Collect() (time.Time, []v2.Metric, error) { var errors []error // Collect from all collectors that are ready. var next time.Time + var metrics []v2.Metric for _, c := range cm.collectors { if c.nextCollectionTime.Before(time.Now()) { - nextCollection, err := c.collector.Collect() + nextCollection, newMetrics, err := c.collector.Collect() if err != nil { errors = append(errors, err) } + metrics = append(metrics, newMetrics...) c.nextCollectionTime = nextCollection } @@ -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. diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/collector/collector_manager_test.go b/Godeps/_workspace/src/github.com/google/cadvisor/collector/collector_manager_test.go index 85d6bbc08ee..49877032e4f 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/collector/collector_manager_test.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/collector/collector_manager_test.go @@ -18,6 +18,7 @@ import ( "testing" "time" + "github.com/google/cadvisor/info/v2" "github.com/stretchr/testify/assert" ) @@ -27,9 +28,9 @@ type fakeCollector struct { collectedFrom int } -func (fc *fakeCollector) Collect() (time.Time, error) { +func (fc *fakeCollector) Collect() (time.Time, []v2.Metric, error) { fc.collectedFrom++ - return fc.nextCollectionTime, fc.err + return fc.nextCollectionTime, []v2.Metric{}, fc.err } func (fc *fakeCollector) Name() string { @@ -53,7 +54,7 @@ func TestCollect(t *testing.T) { assert.NoError(cm.RegisterCollector(f2)) // First collection, everyone gets collected from. - nextTime, err := cm.Collect() + nextTime, _, err := cm.Collect() assert.Equal(firstTime, nextTime) assert.NoError(err) assert.Equal(1, f1.collectedFrom) @@ -62,7 +63,7 @@ func TestCollect(t *testing.T) { f1.nextCollectionTime = time.Now().Add(2 * time.Hour) // Second collection, only the one that is ready gets collected from. - nextTime, err = cm.Collect() + nextTime, _, err = cm.Collect() assert.Equal(secondTime, nextTime) assert.NoError(err) assert.Equal(2, f1.collectedFrom) diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/collector/fakes.go b/Godeps/_workspace/src/github.com/google/cadvisor/collector/fakes.go index d36f113614d..388f3fc0bfe 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/collector/fakes.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/collector/fakes.go @@ -16,6 +16,8 @@ package collector import ( "time" + + "github.com/google/cadvisor/info/v2" ) type FakeCollectorManager struct { @@ -25,7 +27,7 @@ func (fkm *FakeCollectorManager) RegisterCollector(collector Collector) error { return nil } -func (fkm *FakeCollectorManager) Collect() (time.Time, error) { +func (fkm *FakeCollectorManager) Collect() (time.Time, []v2.Metric, error) { var zero time.Time - return zero, nil + return zero, []v2.Metric{}, nil } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/collector/types.go b/Godeps/_workspace/src/github.com/google/cadvisor/collector/types.go index 4967a6ce27f..8bd29d0604a 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/collector/types.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/collector/types.go @@ -15,6 +15,7 @@ package collector import ( + "github.com/google/cadvisor/info/v2" "time" ) @@ -26,7 +27,7 @@ type Collector interface { // Returns the next time this collector should be collected from. // Next collection time is always returned, even when an error occurs. // A collection time of zero means no more collection. - Collect() (time.Time, error) + Collect() (time.Time, []v2.Metric, error) // Name of this collector. Name() string @@ -41,5 +42,5 @@ type CollectorManager interface { // at which a collector will be ready to collect from. // Next collection time is always returned, even when an error occurs. // A collection time of zero means no more collection. - Collect() (time.Time, error) + Collect() (time.Time, []v2.Metric, error) } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/container/docker/handler.go b/Godeps/_workspace/src/github.com/google/cadvisor/container/docker/handler.go index abe35ec2c52..a6c7ec87241 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/container/docker/handler.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/container/docker/handler.go @@ -325,8 +325,7 @@ func (self *dockerContainerHandler) ListThreads(listType container.ListType) ([] } func (self *dockerContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { - // TODO(vmarmol): Implement. - return nil, nil + return containerLibcontainer.GetProcesses(self.cgroupManager) } func (self *dockerContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error { diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/container/libcontainer/helpers.go b/Godeps/_workspace/src/github.com/google/cadvisor/container/libcontainer/helpers.go index d5bf142ae92..53e41af8581 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/container/libcontainer/helpers.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/container/libcontainer/helpers.go @@ -96,6 +96,14 @@ func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info. 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 { return path.Join(dockerRoot, "containers") } @@ -186,10 +194,21 @@ func toContainerStats2(s *cgroups.Stats, ret *info.ContainerStats) { ret.Memory.HierarchicalData.Pgmajfault = v } if v, ok := s.MemoryStats.Stats["total_inactive_anon"]; ok { - ret.Memory.WorkingSet = ret.Memory.Usage - v - if v, ok := s.MemoryStats.Stats["total_active_file"]; ok { - ret.Memory.WorkingSet -= v + workingSet := ret.Memory.Usage + if 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 } } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/container/raw/handler.go b/Godeps/_workspace/src/github.com/google/cadvisor/container/raw/handler.go index 836d879a521..d8ea85240dc 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/container/raw/handler.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/container/raw/handler.go @@ -264,6 +264,7 @@ func (self *rawContainerHandler) getFsStats(stats *info.ContainerStats) error { Device: fs.Device, Limit: fs.Capacity, Usage: fs.Capacity - fs.Free, + Available: fs.Available, ReadsCompleted: fs.DiskStats.ReadsCompleted, ReadsMerged: fs.DiskStats.ReadsMerged, SectorsRead: fs.DiskStats.SectorsRead, @@ -398,8 +399,7 @@ func (self *rawContainerHandler) ListThreads(listType container.ListType) ([]int } func (self *rawContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { - // TODO(vmarmol): Implement - return nil, nil + return libcontainer.GetProcesses(self.cgroupManager) } func (self *rawContainerHandler) watchDirectory(dir string, containerName string) error { diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/fs/fs.go b/Godeps/_workspace/src/github.com/google/cadvisor/fs/fs.go index 613ebbcc5b5..13a4475fce4 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/fs/fs.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/fs/fs.go @@ -20,6 +20,7 @@ package fs /* extern int getBytesFree(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" @@ -176,7 +177,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er _, hasMount := mountSet[partition.mountpoint] _, hasDevice := deviceSet[device] if mountSet == nil || (hasMount && !hasDevice) { - total, free, err := getVfsStats(partition.mountpoint) + total, free, avail, err := getVfsStats(partition.mountpoint) if err != nil { glog.Errorf("Statvfs failed. Error: %v", err) } else { @@ -186,7 +187,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er Major: uint(partition.major), Minor: uint(partition.minor), } - fs := Fs{deviceInfo, total, free, diskStatsMap[device]} + fs := Fs{deviceInfo, total, free, avail, diskStatsMap[device]} filesystems = append(filesystems, fs) } } @@ -287,18 +288,22 @@ func (self *RealFsInfo) GetDirUsage(dir string) (uint64, error) { 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) 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))) 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))) 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 } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/fs/statvfs.c b/Godeps/_workspace/src/github.com/google/cadvisor/fs/statvfs.c index 6961df4f68a..3970f385541 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/fs/statvfs.c +++ b/Godeps/_workspace/src/github.com/google/cadvisor/fs/statvfs.c @@ -21,3 +21,14 @@ int getBytesTotal(const char *path, unsigned long long *bytes) { *bytes = buf.f_frsize * buf.f_blocks; 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; +} diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/fs/types.go b/Godeps/_workspace/src/github.com/google/cadvisor/fs/types.go index 77a46a88b16..37f0db2a9ea 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/fs/types.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/fs/types.go @@ -24,6 +24,7 @@ type Fs struct { DeviceInfo Capacity uint64 Free uint64 + Available uint64 DiskStats DiskStats } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go index 26e85e15a77..0e4b89bb38e 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go @@ -341,6 +341,9 @@ type FsStats struct { // Number of bytes that is consumed by the container on this filesystem. Usage uint64 `json:"usage"` + // Number of bytes available for non-root user. + Available uint64 `json:"available"` + // Number of reads completed // This is the total number of reads completed successfully. ReadsCompleted uint64 `json:"reads_completed"` diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/v2/container.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/v2/container.go index 8ed8aafb32b..bfc8b86c530 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/v2/container.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/v2/container.go @@ -151,6 +151,9 @@ type FsInfo struct { // Filesystem usage in bytes. Capacity uint64 `json:"capacity"` + // Bytes available for non-root use. + Available uint64 `json:"available"` + // Number of bytes used on this filesystem. Usage uint64 `json:"usage"` @@ -166,3 +169,16 @@ type RequestOptions struct { // Whether to include stats for child subcontainers. 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"` +} diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/v2/metric.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/v2/metric.go new file mode 100644 index 00000000000..1057980ce37 --- /dev/null +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/v2/metric.go @@ -0,0 +1,69 @@ +// 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"` +} diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go b/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go index f59a95ce5a6..190c2a0a480 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go @@ -18,7 +18,10 @@ import ( "flag" "fmt" "math" + "os/exec" "sort" + "strconv" + "strings" "sync" "time" @@ -113,6 +116,63 @@ func (c *containerData) DerivedStats() (v2.DerivedStats, error) { 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) { if memoryStorage == nil { return nil, fmt.Errorf("nil memory storage") @@ -232,8 +292,9 @@ func (c *containerData) housekeeping() { } } + // TODO(vmarmol): Export metrics. // Run custom collectors. - nextCollectionTime, err := c.collectorManager.Collect() + nextCollectionTime, _, err := c.collectorManager.Collect() if err != nil && c.allowErrorLogging() { glog.Warningf("[%s] Collection failed: %v", c.info.Name, err) } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go b/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go index 7fd26eb9907..ef68e2a3245 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go @@ -90,6 +90,9 @@ type Manager interface { // Returns information for all global filesystems if label is empty. 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. WatchForEvents(request *events.Request) (*events.EventChannel, error) @@ -609,6 +612,7 @@ func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) { Mountpoint: mountpoint, Capacity: fs.Limit, Usage: fs.Usage, + Available: fs.Available, Labels: labels, } fsInfo = append(fsInfo, fi) @@ -640,6 +644,27 @@ func (m *manager) Exists(containerName string) bool { 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. func (m *manager) createContainer(containerName string) error { handler, accept, err := container.NewContainerHandler(containerName) diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager_mock.go b/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager_mock.go index de18a31da90..bcc872d9d6a 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager_mock.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager_mock.go @@ -99,3 +99,8 @@ func (c *ManagerMock) GetFsInfo() ([]v2.FsInfo, error) { args := c.Called() 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) +} diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/pages/containers_html.go b/Godeps/_workspace/src/github.com/google/cadvisor/pages/containers_html.go index ee652eb246b..ba7e9356d1d 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/pages/containers_html.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/pages/containers_html.go @@ -108,6 +108,12 @@ const containersHtmlTemplate = `
+