diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index a069afd3e66..70168a7036d 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -210,88 +210,88 @@ }, { "ImportPath": "github.com/google/cadvisor/api", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/collector", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/container", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/events", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/fs", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/healthz", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/http", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/info/v1", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/info/v2", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/manager", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/metrics", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/pages", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/storage", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/summary", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/utils", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/validate", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/cadvisor/version", - "Comment": "0.13.0-48-g9535dd2", - "Rev": "9535dd21e2aca94f024528679d22e3637178cda0" + "Comment": "0.13.0-56-ga9f4b69", + "Rev": "a9f4b691c1e0336e5d8e310d5664046012988437" }, { "ImportPath": "github.com/google/go-github/github", 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 a6c7ec87241..2e3f8cf4551 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 @@ -340,3 +340,27 @@ func (self *dockerContainerHandler) StopWatchingSubcontainers() error { func (self *dockerContainerHandler) Exists() bool { return containerLibcontainer.Exists(*dockerRootDir, *dockerRunDir, self.id) } + +func DockerInfo() (map[string]string, error) { + client, err := docker.NewClient(*ArgDockerEndpoint) + if err != nil { + return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + info, err := client.Info() + if err != nil { + return nil, err + } + return info.Map(), nil +} + +func DockerImages() ([]docker.APIImages, error) { + client, err := docker.NewClient(*ArgDockerEndpoint) + if err != nil { + return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err) + } + images, err := client.ListImages(docker.ListImagesOptions{All: false}) + if err != nil { + return nil, err + } + return images, nil +} 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 bfc8b86c530..1ea7d0dd8f4 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 @@ -171,14 +171,15 @@ 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"` - 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 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"` } 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 190c2a0a480..e9968ef0918 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go @@ -130,9 +130,9 @@ func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) { } } // TODO(rjnagal): Take format as an option? - format := "user,pid,ppid,stime,pcpu,rss,vsz,stat,time,comm" + format := "user,pid,ppid,stime,pcpu,pmem,rss,vsz,stat,time,comm" args := []string{"-e", "-o", format} - expectedFields := 10 + expectedFields := 11 out, err := exec.Command("ps", args...).Output() if err != nil { return nil, fmt.Errorf("failed to execute ps command: %v", err) @@ -157,16 +157,17 @@ func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) { } 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:], " "), + User: fields[0], + Pid: pid, + Ppid: ppid, + StartTime: fields[3], + PercentCpu: fields[4], + PercentMemory: fields[5], + RSS: fields[6], + VirtualSize: fields[7], + Status: fields[8], + RunningTime: fields[9], + Cmd: strings.Join(fields[10:], " "), }) } } 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 ef68e2a3245..6b9e3715f29 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go @@ -50,7 +50,8 @@ var eventStorageEventLimit = flag.String("event_storage_event_limit", "default=1 // The Manager interface defines operations for starting a manager and getting // container and machine information. type Manager interface { - // Start the manager. + // Start the manager. Calling other manager methods before this returns + // may produce undefined behavior. Start() error // Stops the manager. @@ -100,6 +101,12 @@ type Manager interface { GetPastEvents(request *events.Request) ([]*info.Event, error) CloseEventChannel(watch_id int) + + // Get status information about docker. + DockerInfo() (DockerStatus, error) + + // Get details about interesting docker images. + DockerImages() ([]DockerImage, error) } // New takes a memory storage and returns a new manager. @@ -144,19 +151,6 @@ func New(memoryStorage *memory.InMemoryStorage, sysfs sysfs.SysFs) (Manager, err glog.Infof("Version: %+v", newManager.versionInfo) newManager.eventHandler = events.NewEventManager(parseEventsStoragePolicy()) - - // Register Docker container factory. - err = docker.Register(newManager, fsInfo) - if err != nil { - glog.Errorf("Docker container factory registration failed: %v.", err) - } - - // Register the raw driver. - err = raw.Register(newManager, fsInfo) - if err != nil { - glog.Errorf("Registration of the raw container factory failed: %v", err) - } - return newManager, nil } @@ -186,6 +180,20 @@ type manager struct { // Start the container manager. func (self *manager) Start() error { + // Register Docker container factory. + err := docker.Register(self, self.fsInfo) + if err != nil { + glog.Errorf("Docker container factory registration failed: %v.", err) + } + + // Register the raw driver. + err = raw.Register(self, self.fsInfo) + if err != nil { + glog.Errorf("Registration of the raw container factory failed: %v", err) + } + + self.DockerInfo() + self.DockerImages() if *enableLoadReader { // Create cpu load reader. @@ -204,7 +212,7 @@ func (self *manager) Start() error { } // Watch for OOMs. - err := self.watchForNewOoms() + err = self.watchForNewOoms() if err != nil { glog.Errorf("Failed to start OOM watcher, will not get OOM events: %v", err) } @@ -1022,3 +1030,104 @@ func parseEventsStoragePolicy() events.StoragePolicy { return policy } + +type DockerStatus struct { + Version string `json:"version"` + KernelVersion string `json:"kernel_version"` + OS string `json:"os"` + Hostname string `json:"hostname"` + RootDir string `json:"root_dir"` + Driver string `json:"driver"` + DriverStatus map[string]string `json:"driver_status"` + ExecDriver string `json:"exec_driver"` + NumImages int `json:"num_images"` + NumContainers int `json:"num_containers"` +} + +type DockerImage struct { + ID string `json:"id"` + RepoTags []string `json:"repo_tags"` // repository name and tags. + Created int64 `json:"created"` // unix time since creation. + VirtualSize int64 `json:"virtual_size"` + Size int64 `json:"size"` +} + +func (m *manager) DockerImages() ([]DockerImage, error) { + images, err := docker.DockerImages() + if err != nil { + return nil, err + } + out := []DockerImage{} + const unknownTag = ":" + for _, image := range images { + if len(image.RepoTags) == 1 && image.RepoTags[0] == unknownTag { + // images with repo or tags are uninteresting. + continue + } + di := DockerImage{ + ID: image.ID, + RepoTags: image.RepoTags, + Created: image.Created, + VirtualSize: image.VirtualSize, + Size: image.Size, + } + out = append(out, di) + } + return out, nil +} + +func (m *manager) DockerInfo() (DockerStatus, error) { + info, err := docker.DockerInfo() + if err != nil { + return DockerStatus{}, err + } + out := DockerStatus{} + out.Version = m.versionInfo.DockerVersion + if val, ok := info["KernelVersion"]; ok { + out.KernelVersion = val + } + if val, ok := info["OperatingSystem"]; ok { + out.OS = val + } + if val, ok := info["Name"]; ok { + out.Hostname = val + } + if val, ok := info["DockerRootDir"]; ok { + out.RootDir = val + } + if val, ok := info["Driver"]; ok { + out.Driver = val + } + if val, ok := info["ExecutionDriver"]; ok { + out.ExecDriver = val + } + if val, ok := info["Images"]; ok { + n, err := strconv.Atoi(val) + if err == nil { + out.NumImages = n + } + } + if val, ok := info["Containers"]; ok { + n, err := strconv.Atoi(val) + if err == nil { + out.NumContainers = n + } + } + // cut, trim, cut - Example format: + // DriverStatus=[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirperm1 Supported","false"]] + if val, ok := info["DriverStatus"]; ok { + out.DriverStatus = make(map[string]string) + val = strings.TrimPrefix(val, "[[") + val = strings.TrimSuffix(val, "]]") + vals := strings.Split(val, "],[") + for _, v := range vals { + kv := strings.Split(v, "\",\"") + if len(kv) != 2 { + continue + } else { + out.DriverStatus[strings.Trim(kv[0], "\"")] = strings.Trim(kv[1], "\"") + } + } + } + return out, nil +} 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 bcc872d9d6a..75204118910 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 @@ -104,3 +104,13 @@ func (c *ManagerMock) GetProcessList(name string, options v2.RequestOptions) ([] args := c.Called() return args.Get(0).([]v2.ProcessInfo), args.Error(1) } + +func (c *ManagerMock) DockerInfo() (DockerStatus, error) { + args := c.Called() + return args.Get(0).(DockerStatus), args.Error(1) +} + +func (c *ManagerMock) DockerImages() ([]DockerImage, error) { + args := c.Called() + return args.Get(0).([]DockerImage), args.Error(1) +} diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_js.go b/Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_js.go index 94548957aed..6afaa842c62 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_js.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/pages/static/containers_js.go @@ -427,8 +427,8 @@ function drawFileSystemUsage(machineInfo, stats) { } function drawProcesses(processInfo) { - var titles = ["User", "PID", "PPID", "Start Time", "CPU %", "RSS", "Virtual Size", "Status", "Running Time", "Command"]; - var titleTypes = ['string', 'number', 'number', 'string', 'string', 'string', 'string', 'string', 'string', 'string']; + 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 data = [] for (var i = 1; i < processInfo.length; i++) { var elements = []; @@ -437,6 +437,7 @@ function drawProcesses(processInfo) { 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(processInfo[i].status); diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/storage/redis/redis.go b/Godeps/_workspace/src/github.com/google/cadvisor/storage/redis/redis.go new file mode 100644 index 00000000000..e4c8fd58a0a --- /dev/null +++ b/Godeps/_workspace/src/github.com/google/cadvisor/storage/redis/redis.go @@ -0,0 +1,125 @@ +// 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 redis + +import ( + "encoding/json" + redis "github.com/garyburd/redigo/redis" + info "github.com/google/cadvisor/info/v1" + storage "github.com/google/cadvisor/storage" + "sync" + "time" +) + +type redisStorage struct { + conn redis.Conn + machineName string + redisKey string + bufferDuration time.Duration + lastWrite time.Time + lock sync.Mutex + readyToFlush func() bool +} + +type detailSpec struct { + Timestamp int64 `json:"timestamp"` + MachineName string `json:"machine_name,omitempty"` + ContainerName string `json:"container_Name,omitempty"` + ContainerStats *info.ContainerStats `json:"container_stats,omitempty"` +} + +func (self *redisStorage) defaultReadyToFlush() bool { + return time.Since(self.lastWrite) >= self.bufferDuration +} + +//We must add some defaut params (for example: MachineName,ContainerName...)because containerStats do not include them +func (self *redisStorage) containerStatsAndDefautValues(ref info.ContainerReference, stats *info.ContainerStats) *detailSpec { + timestamp := stats.Timestamp.UnixNano() / 1E3 + var containerName string + if len(ref.Aliases) > 0 { + containerName = ref.Aliases[0] + } else { + containerName = ref.Name + } + detail := &detailSpec{ + Timestamp: timestamp, + MachineName: self.machineName, + ContainerName: containerName, + ContainerStats: stats, + } + return detail +} + +//Push the data into redis +func (self *redisStorage) AddStats(ref info.ContainerReference, stats *info.ContainerStats) error { + if stats == nil { + return nil + } + var seriesToFlush []byte + func() { + // AddStats will be invoked simultaneously from multiple threads and only one of them will perform a write. + self.lock.Lock() + defer self.lock.Unlock() + // Add some defaut params based on containerStats + detail := self.containerStatsAndDefautValues(ref, stats) + //To json + b, _ := json.Marshal(detail) + if self.readyToFlush() { + seriesToFlush = b + b = nil + self.lastWrite = time.Now() + } + }() + if len(seriesToFlush) > 0 { + //We use redis's "LPUSH" to push the data to the redis + self.conn.Send("LPUSH", self.redisKey, seriesToFlush) + } + return nil +} + +// We just need to push the data to the redis, do not need to pull from the redis, +//so we do not override RecentStats() +func (self *redisStorage) RecentStats(containerName string, numStats int) ([]*info.ContainerStats, error) { + return nil, nil +} + +func (self *redisStorage) Close() error { + return self.conn.Close() +} + +// Create a new redis storage driver. +// machineName: A unique identifier to identify the host that runs the current cAdvisor +// instance is running on. +// redisHost: The host which runs redis. +// redisKey: The key for the Data that stored in the redis +func New(machineName, + redisKey, + redisHost string, + bufferDuration time.Duration, +) (storage.StorageDriver, error) { + conn, err := redis.Dial("tcp", redisHost) + if err != nil { + return nil, err + } + ret := &redisStorage{ + conn: conn, + machineName: machineName, + redisKey: redisKey, + bufferDuration: bufferDuration, + lastWrite: time.Now(), + } + ret.readyToFlush = ret.defaultReadyToFlush + return ret, nil +}