mirror of https://github.com/k3s-io/k3s
commit
91ba6c44d2
|
@ -204,88 +204,88 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/api",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/collector",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/container",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/events",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/fs",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/healthz",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/http",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/info/v1",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/info/v2",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/manager",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/metrics",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/pages",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/storage",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/summary",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/utils",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/validate",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/version",
|
||||
"Comment": "0.13.0-56-ga9f4b69",
|
||||
"Rev": "a9f4b691c1e0336e5d8e310d5664046012988437"
|
||||
"Comment": "0.14.0-9-ga96f2f9",
|
||||
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/go-github/github",
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// 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"
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// 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 ()
|
|
@ -149,6 +149,10 @@ func (self *dockerFactory) CanHandleAndAccept(name string) (bool, bool, error) {
|
|||
return true, canAccept, nil
|
||||
}
|
||||
|
||||
func (self *dockerFactory) DebugInfo() map[string][]string {
|
||||
return map[string][]string{}
|
||||
}
|
||||
|
||||
func parseDockerVersion(full_version_string string) ([]int, error) {
|
||||
version_regexp_string := "(\\d+)\\.(\\d+)\\.(\\d+)"
|
||||
version_re := regexp.MustCompile(version_regexp_string)
|
||||
|
|
|
@ -30,6 +30,9 @@ type ContainerHandlerFactory interface {
|
|||
|
||||
// Name of the factory.
|
||||
String() string
|
||||
|
||||
// Returns debugging information. Map of lines per category.
|
||||
DebugInfo() map[string][]string
|
||||
}
|
||||
|
||||
// TODO(vmarmol): Consider not making this global.
|
||||
|
@ -90,3 +93,17 @@ func ClearContainerHandlerFactories() {
|
|||
|
||||
factories = make([]ContainerHandlerFactory, 0, 4)
|
||||
}
|
||||
|
||||
func DebugInfo() map[string][]string {
|
||||
factoriesLock.RLock()
|
||||
defer factoriesLock.RUnlock()
|
||||
|
||||
// Get debug information for all factories.
|
||||
out := make(map[string][]string)
|
||||
for _, factory := range factories {
|
||||
for k, v := range factory.DebugInfo() {
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@ func (self *mockContainerHandlerFactory) String() string {
|
|||
return self.Name
|
||||
}
|
||||
|
||||
func (self *mockContainerHandlerFactory) DebugInfo() map[string][]string {
|
||||
return map[string][]string{}
|
||||
}
|
||||
|
||||
func (self *mockContainerHandlerFactory) CanHandleAndAccept(name string) (bool, bool, error) {
|
||||
return self.CanHandleValue, self.CanAcceptValue, nil
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ type rawFactory struct {
|
|||
|
||||
// Information about mounted filesystems.
|
||||
fsInfo fs.FsInfo
|
||||
|
||||
// Watcher for inotify events.
|
||||
watcher *InotifyWatcher
|
||||
}
|
||||
|
||||
func (self *rawFactory) String() string {
|
||||
|
@ -43,7 +46,7 @@ func (self *rawFactory) String() string {
|
|||
}
|
||||
|
||||
func (self *rawFactory) NewContainerHandler(name string) (container.ContainerHandler, error) {
|
||||
return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo)
|
||||
return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, self.watcher)
|
||||
}
|
||||
|
||||
// The raw factory can handle any container. If --docker_only is set to false, non-docker containers are ignored.
|
||||
|
@ -52,6 +55,23 @@ func (self *rawFactory) CanHandleAndAccept(name string) (bool, bool, error) {
|
|||
return true, accept, nil
|
||||
}
|
||||
|
||||
func (self *rawFactory) DebugInfo() map[string][]string {
|
||||
out := make(map[string][]string)
|
||||
|
||||
// Get information about inotify watches.
|
||||
watches := self.watcher.GetWatches()
|
||||
lines := make([]string, 0, len(watches))
|
||||
for containerName, cgroupWatches := range watches {
|
||||
lines = append(lines, fmt.Sprintf("%s:", containerName))
|
||||
for _, cg := range cgroupWatches {
|
||||
lines = append(lines, fmt.Sprintf("\t%s", cg))
|
||||
}
|
||||
}
|
||||
out["Inotify watches"] = lines
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo) error {
|
||||
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
|
||||
if err != nil {
|
||||
|
@ -61,11 +81,17 @@ func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo) erro
|
|||
return fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
|
||||
}
|
||||
|
||||
watcher, err := NewInotifyWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.Infof("Registering Raw factory")
|
||||
factory := &rawFactory{
|
||||
machineInfoFactory: machineInfoFactory,
|
||||
fsInfo: fsInfo,
|
||||
cgroupSubsystems: &cgroupSubsystems,
|
||||
watcher: watcher,
|
||||
}
|
||||
container.RegisterContainerHandlerFactory(factory)
|
||||
return nil
|
||||
|
|
|
@ -43,17 +43,11 @@ type rawContainerHandler struct {
|
|||
machineInfoFactory info.MachineInfoFactory
|
||||
|
||||
// Inotify event watcher.
|
||||
watcher *inotify.Watcher
|
||||
watcher *InotifyWatcher
|
||||
|
||||
// Signal for watcher thread to stop.
|
||||
stopWatcher chan error
|
||||
|
||||
// Containers being watched for new subcontainers.
|
||||
watches map[string]struct{}
|
||||
|
||||
// Cgroup paths being watched for new subcontainers
|
||||
cgroupWatches map[string]struct{}
|
||||
|
||||
// Absolute path to the cgroup hierarchies of this container.
|
||||
// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
|
||||
cgroupPaths map[string]string
|
||||
|
@ -68,7 +62,7 @@ type rawContainerHandler struct {
|
|||
externalMounts []mount
|
||||
}
|
||||
|
||||
func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo) (container.ContainerHandler, error) {
|
||||
func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *InotifyWatcher) (container.ContainerHandler, error) {
|
||||
// Create the cgroup paths.
|
||||
cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
|
||||
for key, val := range cgroupSubsystems.MountPoints {
|
||||
|
@ -107,13 +101,12 @@ func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSu
|
|||
cgroupSubsystems: cgroupSubsystems,
|
||||
machineInfoFactory: machineInfoFactory,
|
||||
stopWatcher: make(chan error),
|
||||
watches: make(map[string]struct{}),
|
||||
cgroupWatches: make(map[string]struct{}),
|
||||
cgroupPaths: cgroupPaths,
|
||||
cgroupManager: cgroupManager,
|
||||
fsInfo: fsInfo,
|
||||
hasNetwork: hasNetwork,
|
||||
externalMounts: externalMounts,
|
||||
watcher: watcher,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -402,29 +395,43 @@ func (self *rawContainerHandler) ListProcesses(listType container.ListType) ([]i
|
|||
return libcontainer.GetProcesses(self.cgroupManager)
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) watchDirectory(dir string, containerName string) error {
|
||||
err := self.watcher.AddWatch(dir, inotify.IN_CREATE|inotify.IN_DELETE|inotify.IN_MOVE)
|
||||
// Watches the specified directory and all subdirectories. Returns whether the path was
|
||||
// already being watched and an error (if any).
|
||||
func (self *rawContainerHandler) watchDirectory(dir string, containerName string) (bool, error) {
|
||||
alreadyWatching, err := self.watcher.AddWatch(containerName, dir)
|
||||
if err != nil {
|
||||
return err
|
||||
return alreadyWatching, err
|
||||
}
|
||||
self.watches[containerName] = struct{}{}
|
||||
self.cgroupWatches[dir] = struct{}{}
|
||||
|
||||
// Remove the watch if further operations failed.
|
||||
cleanup := true
|
||||
defer func() {
|
||||
if cleanup {
|
||||
_, err := self.watcher.RemoveWatch(containerName, dir)
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to remove inotify watch for %q: %v", dir, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(vmarmol): We should re-do this once we're done to ensure directories were not added in the meantime.
|
||||
// Watch subdirectories as well.
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
return alreadyWatching, err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
err = self.watchDirectory(path.Join(dir, entry.Name()), path.Join(containerName, entry.Name()))
|
||||
// TODO(vmarmol): We don't have to fail here, maybe we can recover and try to get as many registrations as we can.
|
||||
_, err = self.watchDirectory(path.Join(dir, entry.Name()), path.Join(containerName, entry.Name()))
|
||||
if err != nil {
|
||||
return err
|
||||
return alreadyWatching, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
cleanup = false
|
||||
return alreadyWatching, nil
|
||||
}
|
||||
|
||||
func (self *rawContainerHandler) processEvent(event *inotify.Event, events chan container.SubcontainerEvent) error {
|
||||
|
@ -460,10 +467,8 @@ func (self *rawContainerHandler) processEvent(event *inotify.Event, events chan
|
|||
// Maintain the watch for the new or deleted container.
|
||||
switch {
|
||||
case eventType == container.SubcontainerAdd:
|
||||
_, alreadyWatched := self.watches[containerName]
|
||||
|
||||
// New container was created, watch it.
|
||||
err := self.watchDirectory(event.Name, containerName)
|
||||
alreadyWatched, err := self.watchDirectory(event.Name, containerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -473,20 +478,16 @@ func (self *rawContainerHandler) processEvent(event *inotify.Event, events chan
|
|||
return nil
|
||||
}
|
||||
case eventType == container.SubcontainerDelete:
|
||||
// Container was deleted, stop watching for it. Only delete the event if we registered it.
|
||||
if _, ok := self.cgroupWatches[event.Name]; ok {
|
||||
err := self.watcher.RemoveWatch(event.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(self.cgroupWatches, event.Name)
|
||||
// Container was deleted, stop watching for it.
|
||||
lastWatched, err := self.watcher.RemoveWatch(containerName, event.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only report container deletion once.
|
||||
if _, ok := self.watches[containerName]; !ok {
|
||||
if !lastWatched {
|
||||
return nil
|
||||
}
|
||||
delete(self.watches, containerName)
|
||||
default:
|
||||
return fmt.Errorf("unknown event type %v", eventType)
|
||||
}
|
||||
|
@ -501,18 +502,9 @@ func (self *rawContainerHandler) processEvent(event *inotify.Event, events chan
|
|||
}
|
||||
|
||||
func (self *rawContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error {
|
||||
// Lazily initialize the watcher so we don't use it when not asked to.
|
||||
if self.watcher == nil {
|
||||
w, err := inotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.watcher = w
|
||||
}
|
||||
|
||||
// Watch this container (all its cgroups) and all subdirectories.
|
||||
for _, cgroupPath := range self.cgroupPaths {
|
||||
err := self.watchDirectory(cgroupPath, self.name)
|
||||
_, err := self.watchDirectory(cgroupPath, self.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -522,18 +514,17 @@ func (self *rawContainerHandler) WatchSubcontainers(events chan container.Subcon
|
|||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-self.watcher.Event:
|
||||
case event := <-self.watcher.Event():
|
||||
err := self.processEvent(event, events)
|
||||
if err != nil {
|
||||
glog.Warningf("Error while processing event (%+v): %v", event, err)
|
||||
}
|
||||
case err := <-self.watcher.Error:
|
||||
case err := <-self.watcher.Error():
|
||||
glog.Warningf("Error while watching %q:", self.name, err)
|
||||
case <-self.stopWatcher:
|
||||
err := self.watcher.Close()
|
||||
if err == nil {
|
||||
self.stopWatcher <- err
|
||||
self.watcher = nil
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -544,10 +535,6 @@ func (self *rawContainerHandler) WatchSubcontainers(events chan container.Subcon
|
|||
}
|
||||
|
||||
func (self *rawContainerHandler) StopWatchingSubcontainers() error {
|
||||
if self.watcher == nil {
|
||||
return fmt.Errorf("can't stop watch that has not started for container %q", self.name)
|
||||
}
|
||||
|
||||
// Rendezvous with the watcher thread.
|
||||
self.stopWatcher <- nil
|
||||
return <-self.stopWatcher
|
||||
|
|
135
Godeps/_workspace/src/github.com/google/cadvisor/container/raw/inotify_watcher.go
generated
vendored
Normal file
135
Godeps/_workspace/src/github.com/google/cadvisor/container/raw/inotify_watcher.go
generated
vendored
Normal file
|
@ -0,0 +1,135 @@
|
|||
// 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 raw
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"golang.org/x/exp/inotify"
|
||||
)
|
||||
|
||||
// Watcher for container-related inotify events in the cgroup hierarchy.
|
||||
//
|
||||
// Implementation is thread-safe.
|
||||
type InotifyWatcher struct {
|
||||
// Underlying inotify watcher.
|
||||
watcher *inotify.Watcher
|
||||
|
||||
// Map of containers being watched to cgroup paths watched for that container.
|
||||
containersWatched map[string]map[string]bool
|
||||
|
||||
// Lock for all datastructure access.
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func NewInotifyWatcher() (*InotifyWatcher, error) {
|
||||
w, err := inotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &InotifyWatcher{
|
||||
watcher: w,
|
||||
containersWatched: make(map[string]map[string]bool),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Add a watch to the specified directory. Returns if the container was already being watched.
|
||||
func (iw *InotifyWatcher) AddWatch(containerName, dir string) (bool, error) {
|
||||
iw.lock.Lock()
|
||||
defer iw.lock.Unlock()
|
||||
|
||||
cgroupsWatched, alreadyWatched := iw.containersWatched[containerName]
|
||||
|
||||
// Register an inotify notification.
|
||||
if !cgroupsWatched[dir] {
|
||||
err := iw.watcher.AddWatch(dir, inotify.IN_CREATE|inotify.IN_DELETE|inotify.IN_MOVE)
|
||||
if err != nil {
|
||||
return alreadyWatched, err
|
||||
}
|
||||
|
||||
if cgroupsWatched == nil {
|
||||
cgroupsWatched = make(map[string]bool)
|
||||
}
|
||||
cgroupsWatched[dir] = true
|
||||
}
|
||||
|
||||
// Record our watching of the container.
|
||||
if !alreadyWatched {
|
||||
iw.containersWatched[containerName] = cgroupsWatched
|
||||
}
|
||||
return alreadyWatched, nil
|
||||
}
|
||||
|
||||
// Remove watch from the specified directory. Returns if this was the last watch on the specified container.
|
||||
func (iw *InotifyWatcher) RemoveWatch(containerName, dir string) (bool, error) {
|
||||
iw.lock.Lock()
|
||||
defer iw.lock.Unlock()
|
||||
|
||||
// If we don't have a watch registed for this, just return.
|
||||
cgroupsWatched, ok := iw.containersWatched[containerName]
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Remove the inotify watch if it exists.
|
||||
if cgroupsWatched[dir] {
|
||||
err := iw.watcher.RemoveWatch(dir)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
delete(cgroupsWatched, dir)
|
||||
}
|
||||
|
||||
// Remove the record if this is the last watch.
|
||||
if len(cgroupsWatched) == 0 {
|
||||
delete(iw.containersWatched, containerName)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Errors are returned on this channel.
|
||||
func (iw *InotifyWatcher) Error() chan error {
|
||||
return iw.watcher.Error
|
||||
}
|
||||
|
||||
// Events are returned on this channel.
|
||||
func (iw *InotifyWatcher) Event() chan *inotify.Event {
|
||||
return iw.watcher.Event
|
||||
}
|
||||
|
||||
// Closes the inotify watcher.
|
||||
func (iw *InotifyWatcher) Close() error {
|
||||
return iw.watcher.Close()
|
||||
}
|
||||
|
||||
// Returns a map of containers to the cgroup paths being watched.
|
||||
func (iw *InotifyWatcher) GetWatches() map[string][]string {
|
||||
out := make(map[string][]string, len(iw.containersWatched))
|
||||
for k, v := range iw.containersWatched {
|
||||
out[k] = mapToSlice(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mapToSlice(m map[string]bool) []string {
|
||||
out := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
out = append(out, k)
|
||||
}
|
||||
return out
|
||||
}
|
|
@ -171,15 +171,16 @@ type RequestOptions struct {
|
|||
}
|
||||
|
||||
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"`
|
||||
PercentMemory string `json:"percent_mem"`
|
||||
RSS string `json:"rss"`
|
||||
VirtualSize string `json:"virtual_size"`
|
||||
Status string `json:"status"`
|
||||
RunningTime string `json:"running_time"`
|
||||
Cmd string `json:"cmd"`
|
||||
User string `json:"user"`
|
||||
Pid int `json:"pid"`
|
||||
Ppid int `json:"parent_pid"`
|
||||
StartTime string `json:"start_time"`
|
||||
PercentCpu float32 `json:"percent_cpu"`
|
||||
PercentMemory float32 `json:"percent_mem"`
|
||||
RSS uint64 `json:"rss"`
|
||||
VirtualSize uint64 `json:"virtual_size"`
|
||||
Status string `json:"status"`
|
||||
RunningTime string `json:"running_time"`
|
||||
CgroupPath string `json:"cgroup_path"`
|
||||
Cmd string `json:"cmd"`
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -41,6 +42,8 @@ var HousekeepingInterval = flag.Duration("housekeeping_interval", 1*time.Second,
|
|||
var maxHousekeepingInterval = flag.Duration("max_housekeeping_interval", 60*time.Second, "Largest interval to allow between container housekeepings")
|
||||
var allowDynamicHousekeeping = flag.Bool("allow_dynamic_housekeeping", true, "Whether to allow the housekeeping interval to be dynamic")
|
||||
|
||||
var cgroupPathRegExp = regexp.MustCompile(".*:devices:(.*?),.*")
|
||||
|
||||
// Decay value used for load average smoothing. Interval length of 10 seconds is used.
|
||||
var loadDecay = math.Exp(float64(-1 * (*HousekeepingInterval).Seconds() / 10))
|
||||
|
||||
|
@ -116,6 +119,19 @@ func (c *containerData) DerivedStats() (v2.DerivedStats, error) {
|
|||
return c.summaryReader.DerivedStats()
|
||||
}
|
||||
|
||||
func (c *containerData) getCgroupPath(cgroups string) (string, error) {
|
||||
if cgroups == "-" {
|
||||
return "/", nil
|
||||
}
|
||||
matches := cgroupPathRegExp.FindSubmatch([]byte(cgroups))
|
||||
if len(matches) != 2 {
|
||||
glog.V(3).Infof("failed to get devices cgroup path from %q", cgroups)
|
||||
// return root in case of failures - devices hierarchy might not be enabled.
|
||||
return "/", nil
|
||||
}
|
||||
return string(matches[1]), nil
|
||||
}
|
||||
|
||||
func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
|
||||
// report all processes for root.
|
||||
isRoot := c.info.Name == "/"
|
||||
|
@ -130,9 +146,9 @@ func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
|
|||
}
|
||||
}
|
||||
// TODO(rjnagal): Take format as an option?
|
||||
format := "user,pid,ppid,stime,pcpu,pmem,rss,vsz,stat,time,comm"
|
||||
format := "user,pid,ppid,stime,pcpu,pmem,rss,vsz,stat,time,comm,cgroup"
|
||||
args := []string{"-e", "-o", format}
|
||||
expectedFields := 11
|
||||
expectedFields := 12
|
||||
out, err := exec.Command("ps", args...).Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute ps command: %v", err)
|
||||
|
@ -155,19 +171,44 @@ func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid ppid %q: %v", fields[2], err)
|
||||
}
|
||||
percentCpu, err := strconv.ParseFloat(fields[4], 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid cpu percent %q: %v", fields[4], err)
|
||||
}
|
||||
percentMem, err := strconv.ParseFloat(fields[5], 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid memory percent %q: %v", fields[5], err)
|
||||
}
|
||||
rss, err := strconv.ParseUint(fields[6], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid rss %q: %v", fields[6], err)
|
||||
}
|
||||
vs, err := strconv.ParseUint(fields[7], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid virtual size %q: %v", fields[7], err)
|
||||
}
|
||||
var cgroupPath string
|
||||
if isRoot {
|
||||
cgroupPath, err = c.getCgroupPath(fields[11])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse cgroup path from %q: %v", fields[10], err)
|
||||
}
|
||||
}
|
||||
|
||||
if isRoot || pidMap[pid] == true {
|
||||
processes = append(processes, v2.ProcessInfo{
|
||||
User: fields[0],
|
||||
Pid: pid,
|
||||
Ppid: ppid,
|
||||
StartTime: fields[3],
|
||||
PercentCpu: fields[4],
|
||||
PercentMemory: fields[5],
|
||||
RSS: fields[6],
|
||||
VirtualSize: fields[7],
|
||||
PercentCpu: float32(percentCpu),
|
||||
PercentMemory: float32(percentMem),
|
||||
RSS: rss,
|
||||
VirtualSize: vs,
|
||||
Status: fields[8],
|
||||
RunningTime: fields[9],
|
||||
Cmd: strings.Join(fields[10:], " "),
|
||||
Cmd: fields[10],
|
||||
CgroupPath: cgroupPath,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,9 @@ type Manager interface {
|
|||
|
||||
// Get details about interesting docker images.
|
||||
DockerImages() ([]DockerImage, error)
|
||||
|
||||
// Returns debugging information. Map of lines per category.
|
||||
DebugInfo() map[string][]string
|
||||
}
|
||||
|
||||
// New takes a memory storage and returns a new manager.
|
||||
|
@ -1131,3 +1134,38 @@ func (m *manager) DockerInfo() (DockerStatus, error) {
|
|||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (m *manager) DebugInfo() map[string][]string {
|
||||
debugInfo := container.DebugInfo()
|
||||
|
||||
// Get unique containers.
|
||||
var conts map[*containerData]struct{}
|
||||
func() {
|
||||
m.containersLock.RLock()
|
||||
defer m.containersLock.RUnlock()
|
||||
|
||||
conts = make(map[*containerData]struct{}, len(m.containers))
|
||||
for _, c := range m.containers {
|
||||
conts[c] = struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
// List containers.
|
||||
lines := make([]string, 0, len(conts))
|
||||
for cont := range conts {
|
||||
lines = append(lines, cont.info.Name)
|
||||
if cont.info.Namespace != "" {
|
||||
lines = append(lines, fmt.Sprintf("\tNamespace: %s", cont.info.Namespace))
|
||||
}
|
||||
|
||||
if len(cont.info.Aliases) != 0 {
|
||||
lines = append(lines, "\tAliases:")
|
||||
for _, alias := range cont.info.Aliases {
|
||||
lines = append(lines, fmt.Sprintf("\t\t%s", alias))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debugInfo["Managed containers"] = lines
|
||||
return debugInfo
|
||||
}
|
||||
|
|
|
@ -64,6 +64,36 @@ const containersHtmlTemplate = `
|
|||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .DockerStatus}}
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Driver Status</h3>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
{{range $dockerstatus := .DockerStatus}}
|
||||
<li class ="list-group-item"><span class="stat-label">{{$dockerstatus.Key}}</span> {{$dockerstatus.Value}}</li>
|
||||
{{end}}
|
||||
{{if .DockerDriverStatus}}
|
||||
<li class ="list-group-item"><span class="stat-label">Storage<br></span>
|
||||
<ul class="list-group">
|
||||
{{range $driverstatus := .DockerDriverStatus}}
|
||||
<li class="list-group-item"><span class="stat-label">{{$driverstatus.Key}}</span> {{$driverstatus.Value}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .DockerImages}}
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
<h3>Images</h3>
|
||||
</div>
|
||||
<div id="docker-images"></div>
|
||||
<br><br>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .ResourcesAvailable}}
|
||||
<div class="col-sm-12">
|
||||
<div class="page-header">
|
||||
|
@ -185,7 +215,8 @@ const containersHtmlTemplate = `
|
|||
{{end}}
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
startPage({{.ContainerName}}, {{.CpuAvailable}}, {{.MemoryAvailable}}, {{.Root}});
|
||||
startPage({{.ContainerName}}, {{.CpuAvailable}}, {{.MemoryAvailable}}, {{.Root}}, {{.IsRoot}});
|
||||
drawImages({{.DockerImages}});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
@ -29,6 +30,25 @@ import (
|
|||
|
||||
const DockerPage = "/docker/"
|
||||
|
||||
func toStatusKV(status manager.DockerStatus) ([]keyVal, []keyVal) {
|
||||
ds := []keyVal{
|
||||
{Key: "Driver", Value: status.Driver},
|
||||
}
|
||||
for k, v := range status.DriverStatus {
|
||||
ds = append(ds, keyVal{Key: k, Value: v})
|
||||
}
|
||||
return []keyVal{
|
||||
{Key: "Docker Version", Value: status.Version},
|
||||
{Key: "Kernel Version", Value: status.KernelVersion},
|
||||
{Key: "OS Version", Value: status.OS},
|
||||
{Key: "Host Name", Value: status.Hostname},
|
||||
{Key: "Docker Root Directory", Value: status.RootDir},
|
||||
{Key: "Execution Driver", Value: status.ExecDriver},
|
||||
{Key: "Number of Images", Value: strconv.Itoa(status.NumImages)},
|
||||
{Key: "Number of Containers", Value: strconv.Itoa(status.NumContainers)},
|
||||
}, ds
|
||||
}
|
||||
|
||||
func serveDockerPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
start := time.Now()
|
||||
|
||||
|
@ -54,6 +74,19 @@ func serveDockerPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error
|
|||
})
|
||||
}
|
||||
|
||||
// Get Docker status
|
||||
status, err := m.DockerInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dockerStatus, driverStatus := toStatusKV(status)
|
||||
// Get Docker Images
|
||||
images, err := m.DockerImages()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dockerContainersText := "Docker Containers"
|
||||
data = &pageData{
|
||||
DisplayName: dockerContainersText,
|
||||
|
@ -62,8 +95,11 @@ func serveDockerPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error
|
|||
Text: dockerContainersText,
|
||||
Link: DockerPage,
|
||||
}},
|
||||
Subcontainers: subcontainers,
|
||||
Root: rootDir,
|
||||
Subcontainers: subcontainers,
|
||||
Root: rootDir,
|
||||
DockerStatus: dockerStatus,
|
||||
DockerDriverStatus: driverStatus,
|
||||
DockerImages: images,
|
||||
}
|
||||
} else {
|
||||
// Get the container.
|
||||
|
@ -92,7 +128,6 @@ func serveDockerPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data = &pageData{
|
||||
DisplayName: displayName,
|
||||
ContainerName: cont.Name,
|
||||
|
|
|
@ -37,6 +37,11 @@ type link struct {
|
|||
Link string
|
||||
}
|
||||
|
||||
type keyVal struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type pageData struct {
|
||||
DisplayName string
|
||||
ContainerName string
|
||||
|
@ -52,6 +57,9 @@ type pageData struct {
|
|||
NetworkAvailable bool
|
||||
FsAvailable bool
|
||||
Root string
|
||||
DockerStatus []keyVal
|
||||
DockerDriverStatus []keyVal
|
||||
DockerImages []manager.DockerImage
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
11
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_css.go
generated
vendored
11
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_css.go
generated
vendored
|
@ -39,6 +39,17 @@ const containersCss = `
|
|||
.isolation-title {
|
||||
color:#FFFFFF;
|
||||
}
|
||||
.table-row {
|
||||
font-family: "courier", "monospace";
|
||||
font-size: 15px;
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
border: 5px;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
#logo {
|
||||
height: 200px;
|
||||
margin-top: 20px;
|
||||
|
|
90
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_js.go
generated
vendored
90
Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_js.go
generated
vendored
|
@ -25,10 +25,9 @@ function humanize(num, size, units) {
|
|||
|
||||
// Following the IEC naming convention
|
||||
function humanizeIEC(num) {
|
||||
var ret = humanize(num, 1024, ["TiB", "GiB", "MiB", "KiB", "Bytes"]);
|
||||
var ret = humanize(num, 1024, ["TiB", "GiB", "MiB", "KiB", "B"]);
|
||||
return ret[0].toFixed(2) + " " + ret[1];
|
||||
}
|
||||
|
||||
// Following the Metric naming convention
|
||||
function humanizeMetric(num) {
|
||||
var ret = humanize(num, 1000, ["TB", "GB", "MB", "KB", "Bytes"]);
|
||||
|
@ -36,7 +35,7 @@ function humanizeMetric(num) {
|
|||
}
|
||||
|
||||
// Draw a table.
|
||||
function drawTable(seriesTitles, titleTypes, data, elementId) {
|
||||
function drawTable(seriesTitles, titleTypes, data, elementId, numPages, sortIndex) {
|
||||
var dataTable = new google.visualization.DataTable();
|
||||
for (var i = 0; i < seriesTitles.length; i++) {
|
||||
dataTable.addColumn(titleTypes[i], seriesTitles[i]);
|
||||
|
@ -46,10 +45,19 @@ function drawTable(seriesTitles, titleTypes, data, elementId) {
|
|||
window.charts[elementId] = new google.visualization.Table(document.getElementById(elementId));
|
||||
}
|
||||
|
||||
var cssClassNames = {
|
||||
'headerRow': '',
|
||||
'tableRow': 'table-row',
|
||||
'oddTableRow': 'table-row'
|
||||
};
|
||||
var opts = {
|
||||
alternatingRowStyle: true,
|
||||
page: 'enable',
|
||||
pageSize: 25,
|
||||
pageSize: numPages,
|
||||
allowHtml: true,
|
||||
sortColumn: sortIndex,
|
||||
sortAscending: false,
|
||||
cssClassNames: cssClassNames,
|
||||
};
|
||||
window.charts[elementId].draw(dataTable, opts);
|
||||
}
|
||||
|
@ -167,8 +175,12 @@ function getMachineInfo(rootDir, callback) {
|
|||
|
||||
// Get ps info.
|
||||
function getProcessInfo(rootDir, containerName, callback) {
|
||||
$.getJSON(rootDir + "api/v2.0/ps" + containerName, function(data) {
|
||||
$.getJSON(rootDir + "api/v2.0/ps" + containerName)
|
||||
.done(function(data) {
|
||||
callback(data);
|
||||
})
|
||||
.fail(function(jqhxr, textStatus, error) {
|
||||
callback([]);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -426,26 +438,72 @@ function drawFileSystemUsage(machineInfo, stats) {
|
|||
}
|
||||
}
|
||||
|
||||
function drawProcesses(processInfo) {
|
||||
function drawImages(images) {
|
||||
if (images == null || images.length == 0) {
|
||||
return;
|
||||
}
|
||||
window.charts = {};
|
||||
var titles = ["Repository", "Tags", "ID", "Virtual Size", "Creation Time"];
|
||||
var titleTypes = ['string', 'string', 'string', 'number', 'number'];
|
||||
var sortIndex = 0;
|
||||
var data = [];
|
||||
for (var i = 0; i < images.length; i++) {
|
||||
var elements = [];
|
||||
var tags = [];
|
||||
var repos = images[i].repo_tags[0].split(":");
|
||||
repos.splice(-1,1)
|
||||
for (var j = 0; j < images[i].repo_tags.length; j++) {
|
||||
var splits = images[i].repo_tags[j].split(":")
|
||||
if (splits.length > 1) {
|
||||
tags.push(splits[splits.length - 1])
|
||||
}
|
||||
}
|
||||
elements.push(repos.join(":"));
|
||||
elements.push(tags.join(", "));
|
||||
elements.push(images[i].id.substr(0,24));
|
||||
elements.push({v: images[i].virtual_size, f: humanizeIEC(images[i].virtual_size)});
|
||||
var d = new Date(images[i].created * 1000);
|
||||
elements.push({v: images[i].created, f: d.toLocaleString()});
|
||||
data.push(elements);
|
||||
}
|
||||
drawTable(titles, titleTypes, data, "docker-images", 30, sortIndex);
|
||||
}
|
||||
|
||||
function drawProcesses(isRoot, rootDir, processInfo) {
|
||||
if (processInfo.length == 0) {
|
||||
$("#processes-top").text("No processes found");
|
||||
return;
|
||||
}
|
||||
var titles = ["User", "PID", "PPID", "Start Time", "CPU %", "MEM %", "RSS", "Virtual Size", "Status", "Running Time", "Command"];
|
||||
var titleTypes = ['string', 'number', 'number', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string'];
|
||||
var titleTypes = ['string', 'number', 'number', 'string', 'number', 'number', 'number', 'number', 'string', 'string', 'string'];
|
||||
var sortIndex = 4
|
||||
if (isRoot) {
|
||||
titles.push("Cgroup");
|
||||
titleTypes.push('string');
|
||||
}
|
||||
var data = []
|
||||
for (var i = 1; i < processInfo.length; i++) {
|
||||
for (var i = 0; i < processInfo.length; i++) {
|
||||
var elements = [];
|
||||
elements.push(processInfo[i].user);
|
||||
elements.push(processInfo[i].pid);
|
||||
elements.push(processInfo[i].parent_pid);
|
||||
elements.push(processInfo[i].start_time);
|
||||
elements.push(processInfo[i].percent_cpu);
|
||||
elements.push(processInfo[i].percent_mem);
|
||||
elements.push(processInfo[i].rss);
|
||||
elements.push(processInfo[i].virtual_size);
|
||||
elements.push({ v:processInfo[i].percent_cpu, f:processInfo[i].percent_cpu.toFixed(2)});
|
||||
elements.push({ v:processInfo[i].percent_mem, f:processInfo[i].percent_mem.toFixed(2)});
|
||||
elements.push({ v:processInfo[i].rss, f:humanizeIEC(processInfo[i].rss)});
|
||||
elements.push({ v:processInfo[i].virtual_size, f:humanizeIEC(processInfo[i].virtual_size)});
|
||||
elements.push(processInfo[i].status);
|
||||
elements.push(processInfo[i].running_time);
|
||||
elements.push(processInfo[i].cmd);
|
||||
if (isRoot) {
|
||||
var cgroup = processInfo[i].cgroup_path
|
||||
// Use the raw cgroup link as it works for all containers.
|
||||
var cgroupLink = '<a href="' + rootDir + 'containers/' + cgroup +'">' + cgroup.substr(0,30) + ' </a>';
|
||||
elements.push({v:cgroup, f:cgroupLink});
|
||||
}
|
||||
data.push(elements);
|
||||
}
|
||||
drawTable(titles, titleTypes, data, "processes-top");
|
||||
drawTable(titles, titleTypes, data, "processes-top", 25, sortIndex);
|
||||
}
|
||||
|
||||
// Draw the filesystem usage nodes.
|
||||
|
@ -561,7 +619,7 @@ function drawCharts(machineInfo, containerInfo) {
|
|||
}
|
||||
|
||||
// Executed when the page finishes loading.
|
||||
function startPage(containerName, hasCpu, hasMemory, rootDir) {
|
||||
function startPage(containerName, hasCpu, hasMemory, rootDir, isRoot) {
|
||||
// Don't fetch data if we don't have any resource.
|
||||
if (!hasCpu && !hasMemory) {
|
||||
return;
|
||||
|
@ -573,11 +631,11 @@ function startPage(containerName, hasCpu, hasMemory, rootDir) {
|
|||
|
||||
// Draw process information at start and refresh every 60s.
|
||||
getProcessInfo(rootDir, containerName, function(processInfo) {
|
||||
drawProcesses(processInfo)
|
||||
drawProcesses(isRoot, rootDir, processInfo)
|
||||
});
|
||||
setInterval(function() {
|
||||
getProcessInfo(rootDir, containerName, function(processInfo) {
|
||||
drawProcesses(processInfo)
|
||||
drawProcesses(isRoot, rootDir, processInfo)
|
||||
});
|
||||
}, 60000);
|
||||
|
||||
|
|
|
@ -313,6 +313,13 @@ func HandleRequest(w http.ResponseWriter, containerManager manager.Manager) erro
|
|||
|
||||
ioSchedulerValidation, desc := validateIoScheduler(containerManager)
|
||||
out += fmt.Sprintf(OutputFormat, "Block device setup", ioSchedulerValidation, desc)
|
||||
|
||||
// Output debug info.
|
||||
debugInfo := containerManager.DebugInfo()
|
||||
for category, lines := range debugInfo {
|
||||
out += fmt.Sprintf(OutputFormat, category, "", strings.Join(lines, "\n\t"))
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(out))
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -15,4 +15,4 @@
|
|||
package version
|
||||
|
||||
// Version of cAdvisor.
|
||||
const VERSION = "0.13.0"
|
||||
const VERSION = "0.14.0"
|
||||
|
|
Loading…
Reference in New Issue