From d00926498673a1ab98a2f758c4f838b2bc94255f Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Sun, 5 Oct 2014 06:07:54 +0000 Subject: [PATCH 01/10] LXC Factory and Handler This factory and handler can retrieve memory, cpu, diskio, and network information of lxc containers. The handler can be passed additional container information(e.g. network interface) using a cdesc.json file --- .gitignore | 2 + cadvisor.go | 6 + container/lxc/container_desc.go | 42 +++ container/lxc/container_desc_test.go | 17 + container/lxc/factory.go | 103 ++++++ container/lxc/handler.go | 400 ++++++++++++++++++++++++ container/lxc/test_resources/cdesc.json | 58 ++++ 7 files changed, 628 insertions(+) create mode 100644 container/lxc/container_desc.go create mode 100644 container/lxc/container_desc_test.go create mode 100644 container/lxc/factory.go create mode 100644 container/lxc/handler.go create mode 100644 container/lxc/test_resources/cdesc.json diff --git a/.gitignore b/.gitignore index cd786fa2..193082f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ cadvisor *.swp +*.idea +*.iml diff --git a/cadvisor.go b/cadvisor.go index bf499ea8..cd4aaac6 100644 --- a/cadvisor.go +++ b/cadvisor.go @@ -27,6 +27,7 @@ import ( "github.com/golang/glog" "github.com/google/cadvisor/api" "github.com/google/cadvisor/container/docker" + "github.com/google/cadvisor/container/lxc" "github.com/google/cadvisor/container/raw" "github.com/google/cadvisor/healthz" "github.com/google/cadvisor/info" @@ -61,6 +62,11 @@ func main() { glog.Errorf("Docker registration failed: %v.", err) } + // Register the raw driver. + if err := lxc.Register(containerManager); err != nil { + glog.Fatalf("lxc registration failed: %v.", err) + } + // Register the raw driver. if err := raw.Register(containerManager); err != nil { glog.Fatalf("raw registration failed: %v.", err) diff --git a/container/lxc/container_desc.go b/container/lxc/container_desc.go new file mode 100644 index 00000000..0d6fdef1 --- /dev/null +++ b/container/lxc/container_desc.go @@ -0,0 +1,42 @@ +// Copyright 2014 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. + +// Unmarshal's a Containers description json file. The json file contains +// an array of ContainerDesc structs, each with a container's id and network_interface +// This allows collecting stats about network interfaces configured outside docker +// and lxc +package lxc + +import ( + "encoding/json" + "io/ioutil" +) + +type ContainersDesc struct { + All_hosts []ContainerDesc +} + +type ContainerDesc struct { + Id string + Network_interface string +} + +func Unmarshal(containerDescFile string) (ContainersDesc, error) { + dat, err := ioutil.ReadFile(containerDescFile) + var cDesc ContainersDesc + if err == nil { + err = json.Unmarshal(dat, &cDesc) + } + return cDesc, err +} diff --git a/container/lxc/container_desc_test.go b/container/lxc/container_desc_test.go new file mode 100644 index 00000000..1b6e0b4f --- /dev/null +++ b/container/lxc/container_desc_test.go @@ -0,0 +1,17 @@ +package lxc + +import ( + "testing" +) + +func TestUnmarshal(t *testing.T) { + cDesc, err := Unmarshal("test_resources/cdesc.json") + if err != nil { + t.Fatalf("Error in unmarshalling: %s", err) + } + + t.Logf("Cdesc: %s", cDesc) + if cDesc.All_hosts[0].Network_interface != "veth7ASIQc" { + t.Errorf("Cannot find network interface in %s", cDesc) + } +} diff --git a/container/lxc/factory.go b/container/lxc/factory.go new file mode 100644 index 00000000..72402e02 --- /dev/null +++ b/container/lxc/factory.go @@ -0,0 +1,103 @@ +// Copyright 2014 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. + +// Creates handler to handle containers launched by lxc (under /var/lib/docker/lxc) +package lxc + +import ( + "fmt" + "github.com/docker/libcontainer/cgroups" + "github.com/golang/glog" + "github.com/google/cadvisor/container" + "github.com/google/cadvisor/info" + "strings" +) + +type cgroupSubsystems struct { + // Cgroup subsystem mounts. + mounts []cgroups.Mount + + // Cgroup subsystem to their mount location. + mountPoints map[string]string +} + +type lxcFactory struct { + // Factory for machine information. + machineInfoFactory info.MachineInfoFactory + cgroupSubsystems *cgroupSubsystems +} + +func (self *lxcFactory) String() string { + return "lxc" +} + +func (self *lxcFactory) NewContainerHandler(name string) (container.ContainerHandler, error) { + fmt.Printf("Invoking NewLxcContainerhandler for %s by %q\n", name, self.String()) + return newLxcContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory) +} + +// The raw factory can handle any container. +func (self *lxcFactory) CanHandle(name string) (bool, error) { + if name == "/lxc" { + return true, nil + } else if strings.HasPrefix(name, "/lxc/") { + return true, nil + } + return false, nil +} + +func Register(machineInfoFactory info.MachineInfoFactory) error { + // Get all cgroup mounts. + allCgroups, err := cgroups.GetCgroupMounts() + if err != nil { + return err + } + if len(allCgroups) == 0 { + return fmt.Errorf("failed to find cgroup mounts for the lxc factory") + } + + // Trim the mounts to only the subsystems we care about. + supportedCgroups := make([]cgroups.Mount, 0, len(allCgroups)) + mountPoints := make(map[string]string, len(allCgroups)) + for _, mount := range allCgroups { + for _, subsystem := range mount.Subsystems { + if _, ok := supportedSubsystems[subsystem]; ok { + supportedCgroups = append(supportedCgroups, mount) + mountPoints[subsystem] = mount.Mountpoint + } + } + } + if len(supportedCgroups) == 0 { + return fmt.Errorf("failed to find supported cgroup mounts for the lxc factory") + } + + glog.Infof("Registering Lxc factory") + factory := &lxcFactory{ + machineInfoFactory: machineInfoFactory, + cgroupSubsystems: &cgroupSubsystems{ + mounts: supportedCgroups, + mountPoints: mountPoints, + }, + } + container.RegisterContainerHandlerFactory(factory) + return nil +} + +// Cgroup subsystems we support listing (should be the minimal set we need stats from). +var supportedSubsystems map[string]struct{} = map[string]struct{}{ + "cpu": {}, + "cpuacct": {}, + "memory": {}, + "cpuset": {}, +} diff --git a/container/lxc/handler.go b/container/lxc/handler.go new file mode 100644 index 00000000..e4b521b1 --- /dev/null +++ b/container/lxc/handler.go @@ -0,0 +1,400 @@ +// Copyright 2014 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. + +// Handles containers launched by lxc (under /var/lib/docker/lxc) +// Gathers stats about memory, cpu, network, and diskio +package lxc + +import ( + "code.google.com/p/go.exp/inotify" + "flag" + "fmt" + dockerlibcontainer "github.com/docker/libcontainer" + "github.com/docker/libcontainer/cgroups" + cgroup_fs "github.com/docker/libcontainer/cgroups/fs" + "github.com/docker/libcontainer/network" + "github.com/golang/glog" + "github.com/google/cadvisor/container" + "github.com/google/cadvisor/container/libcontainer" + "github.com/google/cadvisor/fs" + "github.com/google/cadvisor/info" + "github.com/google/cadvisor/utils" + "io/ioutil" + "path" + "strconv" + "strings" +) + +var containersDesc = flag.String("cDesc", "/etc/docker/cdesc.json", "container description file") + +type lxcContainerHandler struct { + name string + cgroup *cgroups.Cgroup + cgroupSubsystems *cgroupSubsystems + machineInfoFactory info.MachineInfoFactory + watcher *inotify.Watcher + stopWatcher chan error + watches map[string]struct{} + fsInfo fs.FsInfo + network_interface string +} + +func newLxcContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { + fsInfo, err := fs.NewFsInfo() + if err != nil { + return nil, err + } + cDesc, err := Unmarshal(*containersDesc) + var network_interface string + for _, container := range cDesc.All_hosts { + cName := "/lxc/" + container.Id + glog.Infof("container %s Name %s \n\n", container, name) + if cName == name { + network_interface = container.Network_interface + fmt.Printf("Found network interface %s \n\n", network_interface) + break + } + } + return &lxcContainerHandler{ + name: name, + cgroup: &cgroups.Cgroup{ + Parent: "/", + Name: name, + }, + cgroupSubsystems: cgroupSubsystems, + machineInfoFactory: machineInfoFactory, + stopWatcher: make(chan error), + watches: make(map[string]struct{}), + fsInfo: fsInfo, + network_interface: network_interface, + }, nil +} + +func (self *lxcContainerHandler) ContainerReference() (info.ContainerReference, error) { + // We only know the container by its one name. + return info.ContainerReference{ + Name: self.name, + }, nil +} + +func readString(dirpath string, file string) string { + cgroupFile := path.Join(dirpath, file) + + // Ignore non-existent files + if !utils.FileExists(cgroupFile) { + return "" + } + + // Read + out, err := ioutil.ReadFile(cgroupFile) + if err != nil { + glog.Errorf("lxc driver: Failed to read %q: %s", cgroupFile, err) + return "" + } + return string(out) +} + +func readInt64(dirpath string, file string) uint64 { + out := readString(dirpath, file) + if out == "" { + return 0 + } + + val, err := strconv.ParseUint(strings.TrimSpace(out), 10, 64) + if err != nil { + glog.Errorf("lxc driver: Failed to parse int %q from file %q: %s", out, path.Join(dirpath, file), err) + return 0 + } + + return val +} + +func (self *lxcContainerHandler) GetSpec() (info.ContainerSpec, error) { + var spec info.ContainerSpec + + // The lxc driver assumes unified hierarchy containers. + + // Get machine info. + mi, err := self.machineInfoFactory.GetMachineInfo() + if err != nil { + return spec, err + } + + // CPU. + cpuRoot, ok := self.cgroupSubsystems.mountPoints["cpu"] + if ok { + cpuRoot = path.Join(cpuRoot, self.name) + if utils.FileExists(cpuRoot) { + spec.HasCpu = true + spec.Cpu.Limit = readInt64(cpuRoot, "cpu.shares") + } + } + + // Cpu Mask. + // This will fail for non-unified hierarchies. We'll return the whole machine mask in that case. + cpusetRoot, ok := self.cgroupSubsystems.mountPoints["cpuset"] + if ok { + cpusetRoot = path.Join(cpusetRoot, self.name) + if utils.FileExists(cpusetRoot) { + spec.HasCpu = true + spec.Cpu.Mask = readString(cpusetRoot, "cpuset.cpus") + if spec.Cpu.Mask == "" { + spec.Cpu.Mask = fmt.Sprintf("0-%d", mi.NumCores-1) + } + } + } + + // Memory. + memoryRoot, ok := self.cgroupSubsystems.mountPoints["memory"] + if ok { + memoryRoot = path.Join(memoryRoot, self.name) + if utils.FileExists(memoryRoot) { + spec.HasMemory = true + spec.Memory.Limit = readInt64(memoryRoot, "memory.limit_in_bytes") + spec.Memory.SwapLimit = readInt64(memoryRoot, "memory.memsw.limit_in_bytes") + } + } + + // Fs. + if self.name == "/" { + spec.HasFilesystem = true + } + return spec, nil +} + +func (self *lxcContainerHandler) GetStats() (*info.ContainerStats, error) { + var stats *info.ContainerStats + var err error + if len(self.network_interface) > 0 { + n := network.NetworkState{VethHost: self.network_interface, VethChild: "unknown", NsPath: "unknown"} + s := dockerlibcontainer.State{NetworkState: n} + stats, err = libcontainer.GetStats(self.cgroup, &s) + } else { + stats, err = libcontainer.GetStatsCgroupOnly(self.cgroup) + } + if err != nil { + return nil, err + } + // Get Filesystem information only for the root cgroup. + if self.name == "/" { + stats.Filesystem, err = self.fsInfo.GetFsStats() + if err != nil { + return nil, err + } + } + + return stats, nil +} + +// Lists all directories under "path" and outputs the results as children of "parent". +func listDirectories(dirpath string, parent string, recursive bool, output map[string]struct{}) error { + // Ignore if this hierarchy does not exist. + if !utils.FileExists(dirpath) { + return nil + } + + entries, err := ioutil.ReadDir(dirpath) + if err != nil { + return err + } + for _, entry := range entries { + // We only grab directories. + if entry.IsDir() { + name := path.Join(parent, entry.Name()) + output[name] = struct{}{} + + // List subcontainers if asked to. + if recursive { + err := listDirectories(path.Join(dirpath, entry.Name()), name, true, output) + if err != nil { + return err + } + } + } + } + return nil +} + +func (self *lxcContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { + containers := make(map[string]struct{}) + for _, subsystem := range self.cgroupSubsystems.mounts { + err := listDirectories(path.Join(subsystem.Mountpoint, self.name), self.name, listType == container.ListRecursive, containers) + if err != nil { + return nil, err + } + } + + // Make into container references. + ret := make([]info.ContainerReference, 0, len(containers)) + for cont := range containers { + ret = append(ret, info.ContainerReference{ + Name: cont, + }) + } + + return ret, nil +} + +func (self *lxcContainerHandler) ListThreads(listType container.ListType) ([]int, error) { + // TODO(vmarmol): Implement + return nil, nil +} + +func (self *lxcContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { + return cgroup_fs.GetPids(self.cgroup) +} + +func (self *lxcContainerHandler) watchDirectory(dir string, containerName string) error { + err := self.watcher.AddWatch(dir, inotify.IN_CREATE|inotify.IN_DELETE|inotify.IN_MOVE) + if err != nil { + return err + } + self.watches[containerName] = struct{}{} + + // Watch subdirectories as well. + entries, err := ioutil.ReadDir(dir) + if err != nil { + return err + } + for _, entry := range entries { + if entry.IsDir() { + err = self.watchDirectory(path.Join(dir, entry.Name()), path.Join(containerName, entry.Name())) + if err != nil { + return err + } + } + } + return nil +} + +func (self *lxcContainerHandler) processEvent(event *inotify.Event, events chan container.SubcontainerEvent) error { + // Convert the inotify event type to a container create or delete. + var eventType container.SubcontainerEventType + switch { + case (event.Mask & inotify.IN_CREATE) > 0: + eventType = container.SubcontainerAdd + case (event.Mask & inotify.IN_DELETE) > 0: + eventType = container.SubcontainerDelete + case (event.Mask & inotify.IN_MOVED_FROM) > 0: + eventType = container.SubcontainerDelete + case (event.Mask & inotify.IN_MOVED_TO) > 0: + eventType = container.SubcontainerAdd + default: + // Ignore other events. + return nil + } + + // Derive the container name from the path name. + var containerName string + for _, mount := range self.cgroupSubsystems.mounts { + mountLocation := path.Clean(mount.Mountpoint) + "/" + if strings.HasPrefix(event.Name, mountLocation) { + containerName = event.Name[len(mountLocation)-1:] + break + } + } + if containerName == "" { + return fmt.Errorf("unable to detect container from watch event on directory %q", event.Name) + } + + // Maintain the watch for the new or deleted container. + switch { + case eventType == container.SubcontainerAdd: + // If we've already seen this event, return. + if _, ok := self.watches[containerName]; ok { + return nil + } + + // New container was created, watch it. + err := self.watchDirectory(event.Name, containerName) + if err != nil { + return err + } + case eventType == container.SubcontainerDelete: + // If we've already seen this event, return. + if _, ok := self.watches[containerName]; !ok { + return nil + } + delete(self.watches, containerName) + + // Container was deleted, stop watching for it. + err := self.watcher.RemoveWatch(event.Name) + if err != nil { + return err + } + default: + return fmt.Errorf("unknown event type %v", eventType) + } + + // Deliver the event. + events <- container.SubcontainerEvent{ + EventType: eventType, + Name: containerName, + } + + return nil +} + +func (self *lxcContainerHandler) 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 _, mnt := range self.cgroupSubsystems.mounts { + err := self.watchDirectory(path.Join(mnt.Mountpoint, self.name), self.name) + if err != nil { + return err + } + } + + // Process the events received from the kernel. + go func() { + for { + select { + 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: + 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 + } + } + } + }() + + return nil +} + +func (self *lxcContainerHandler) 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 +} diff --git a/container/lxc/test_resources/cdesc.json b/container/lxc/test_resources/cdesc.json new file mode 100644 index 00000000..fcc6b07f --- /dev/null +++ b/container/lxc/test_resources/cdesc.json @@ -0,0 +1,58 @@ +{ + "name": "Container specification", + "description": "Container specification", + "all_hosts": [ + { + "name": "10-10-10-27", + "ip": "10.10.10.27/24", + "image": "registry.test.altiscale.com:5000/prometheus-init-201405270218", + "public_key": "/root/.ssh/authorized_keys", + "publish": null, + "gateway": null, + "hostname": "10-10-10-27.local.altiscale.com", + "privileged": "true", + "init": null, + "cpu": null, + "memory": null, + "role": "compute_node", + "net": null, + "netinit": "false", + "network_interface": "veth7ASIQc", + "mounts": [ + { + "host-dir": "/var/run/nm-sdc1", + "container-dir": "/var/run/nm-sdc1", + "permission": "rw" + }, + { + "host-dir": "/var/run/nm-sdb3", + "container-dir": "/var/run/nm-sdb3", + "permission": "rw" + }, + { + "host-dir": "/var/run/nm-sda3", + "container-dir": "/var/run/nm-sda3", + "permission": "rw" + }, + { + "host-dir": "/var/run/netns/root", + "container-dir": "/var/run/netns/root", + "permission": "ro" + }, + { + "host-dir": "/var/run/openvswitch/db.sock", + "container-dir": "/var/run/openvswitch/db.sock", + "permission": "rw" + } + ], + "host": { + "hostname": "localhost", + "ip": "127.0.0.1", + "bridge": "alti0", + "key": "" + }, + "id": "18a4585950db428e4d5a65c216a5d708d241254709626f4cb300ee963fb4b144", + "initpid": "10741" + } + ] +} \ No newline at end of file From 808fd821acd0a8ae6c6040682ade367487a5baf8 Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Mon, 13 Oct 2014 21:03:07 +0000 Subject: [PATCH 02/10] NetworkInterface now contains both VethHost and VethChild --- container/lxc/container_desc.go | 8 +++++++- container/lxc/container_desc_test.go | 3 ++- container/lxc/handler.go | 8 ++++---- container/lxc/test_resources/cdesc.json | 5 ++++- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/container/lxc/container_desc.go b/container/lxc/container_desc.go index 0d6fdef1..c1f2f0f8 100644 --- a/container/lxc/container_desc.go +++ b/container/lxc/container_desc.go @@ -29,7 +29,13 @@ type ContainersDesc struct { type ContainerDesc struct { Id string - Network_interface string + Network_interface *NetworkInterface +} + +type NetworkInterface struct { + VethHost string + VethChild string + NsPath string } func Unmarshal(containerDescFile string) (ContainersDesc, error) { diff --git a/container/lxc/container_desc_test.go b/container/lxc/container_desc_test.go index 1b6e0b4f..f5cbd8cc 100644 --- a/container/lxc/container_desc_test.go +++ b/container/lxc/container_desc_test.go @@ -11,7 +11,8 @@ func TestUnmarshal(t *testing.T) { } t.Logf("Cdesc: %s", cDesc) - if cDesc.All_hosts[0].Network_interface != "veth7ASIQc" { + if cDesc.All_hosts[0].Network_interface.VethHost != "veth24031eth1" && + cDesc.All_hosts[0].Network_interface.VethChild != "eth1" { t.Errorf("Cannot find network interface in %s", cDesc) } } diff --git a/container/lxc/handler.go b/container/lxc/handler.go index e4b521b1..836175fa 100644 --- a/container/lxc/handler.go +++ b/container/lxc/handler.go @@ -47,7 +47,7 @@ type lxcContainerHandler struct { stopWatcher chan error watches map[string]struct{} fsInfo fs.FsInfo - network_interface string + network_interface *NetworkInterface } func newLxcContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { @@ -56,7 +56,7 @@ func newLxcContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac return nil, err } cDesc, err := Unmarshal(*containersDesc) - var network_interface string + var network_interface *NetworkInterface for _, container := range cDesc.All_hosts { cName := "/lxc/" + container.Id glog.Infof("container %s Name %s \n\n", container, name) @@ -176,8 +176,8 @@ func (self *lxcContainerHandler) GetSpec() (info.ContainerSpec, error) { func (self *lxcContainerHandler) GetStats() (*info.ContainerStats, error) { var stats *info.ContainerStats var err error - if len(self.network_interface) > 0 { - n := network.NetworkState{VethHost: self.network_interface, VethChild: "unknown", NsPath: "unknown"} + if self.network_interface != nil { + n := network.NetworkState{VethHost: self.network_interface.VethHost, VethChild: self.network_interface.VethChild, NsPath: "unknown"} s := dockerlibcontainer.State{NetworkState: n} stats, err = libcontainer.GetStats(self.cgroup, &s) } else { diff --git a/container/lxc/test_resources/cdesc.json b/container/lxc/test_resources/cdesc.json index fcc6b07f..33c86735 100644 --- a/container/lxc/test_resources/cdesc.json +++ b/container/lxc/test_resources/cdesc.json @@ -17,7 +17,10 @@ "role": "compute_node", "net": null, "netinit": "false", - "network_interface": "veth7ASIQc", + "network_interface": { + "VethChild": "eth1", + "VethHost": "veth24031eth1" + }, "mounts": [ { "host-dir": "/var/run/nm-sdc1", From e038729d675d7a6620d4e3370949291f5d37bbce Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Mon, 13 Oct 2014 21:10:20 +0000 Subject: [PATCH 03/10] Updated import and comments --- cadvisor.go | 2 +- container/lxc/factory.go | 4 ++-- container/lxc/handler.go | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cadvisor.go b/cadvisor.go index cd4aaac6..ac81ab04 100644 --- a/cadvisor.go +++ b/cadvisor.go @@ -62,7 +62,7 @@ func main() { glog.Errorf("Docker registration failed: %v.", err) } - // Register the raw driver. + // Register the lxc driver. if err := lxc.Register(containerManager); err != nil { glog.Fatalf("lxc registration failed: %v.", err) } diff --git a/container/lxc/factory.go b/container/lxc/factory.go index 72402e02..d9d52fa3 100644 --- a/container/lxc/factory.go +++ b/container/lxc/factory.go @@ -17,11 +17,11 @@ package lxc import ( "fmt" + "strings" "github.com/docker/libcontainer/cgroups" "github.com/golang/glog" "github.com/google/cadvisor/container" "github.com/google/cadvisor/info" - "strings" ) type cgroupSubsystems struct { @@ -47,7 +47,7 @@ func (self *lxcFactory) NewContainerHandler(name string) (container.ContainerHan return newLxcContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory) } -// The raw factory can handle any container. +// The lxc factory can handle any container. func (self *lxcFactory) CanHandle(name string) (bool, error) { if name == "/lxc" { return true, nil diff --git a/container/lxc/handler.go b/container/lxc/handler.go index 836175fa..a9d16bdb 100644 --- a/container/lxc/handler.go +++ b/container/lxc/handler.go @@ -20,6 +20,10 @@ import ( "code.google.com/p/go.exp/inotify" "flag" "fmt" + "io/ioutil" + "path" + "strconv" + "strings" dockerlibcontainer "github.com/docker/libcontainer" "github.com/docker/libcontainer/cgroups" cgroup_fs "github.com/docker/libcontainer/cgroups/fs" @@ -30,10 +34,6 @@ import ( "github.com/google/cadvisor/fs" "github.com/google/cadvisor/info" "github.com/google/cadvisor/utils" - "io/ioutil" - "path" - "strconv" - "strings" ) var containersDesc = flag.String("cDesc", "/etc/docker/cdesc.json", "container description file") From a02b7f162016dbd186d21f0549a3292b8eac950b Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Tue, 14 Oct 2014 02:01:30 +0000 Subject: [PATCH 04/10] Added network stats to raw handler Raw handler now parses an optional json file with the network interface information, and emits network stats. --- cadvisor.go | 6 - container/lxc/factory.go | 103 ----- container/lxc/handler.go | 400 ------------------ container/{lxc => raw}/container_desc.go | 19 +- container/{lxc => raw}/container_desc_test.go | 4 +- container/raw/handler.go | 28 +- .../{lxc => raw}/test_resources/cdesc.json | 0 7 files changed, 38 insertions(+), 522 deletions(-) delete mode 100644 container/lxc/factory.go delete mode 100644 container/lxc/handler.go rename container/{lxc => raw}/container_desc.go (75%) rename container/{lxc => raw}/container_desc_test.go (90%) rename container/{lxc => raw}/test_resources/cdesc.json (100%) diff --git a/cadvisor.go b/cadvisor.go index ac81ab04..bf499ea8 100644 --- a/cadvisor.go +++ b/cadvisor.go @@ -27,7 +27,6 @@ import ( "github.com/golang/glog" "github.com/google/cadvisor/api" "github.com/google/cadvisor/container/docker" - "github.com/google/cadvisor/container/lxc" "github.com/google/cadvisor/container/raw" "github.com/google/cadvisor/healthz" "github.com/google/cadvisor/info" @@ -62,11 +61,6 @@ func main() { glog.Errorf("Docker registration failed: %v.", err) } - // Register the lxc driver. - if err := lxc.Register(containerManager); err != nil { - glog.Fatalf("lxc registration failed: %v.", err) - } - // Register the raw driver. if err := raw.Register(containerManager); err != nil { glog.Fatalf("raw registration failed: %v.", err) diff --git a/container/lxc/factory.go b/container/lxc/factory.go deleted file mode 100644 index d9d52fa3..00000000 --- a/container/lxc/factory.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2014 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. - -// Creates handler to handle containers launched by lxc (under /var/lib/docker/lxc) -package lxc - -import ( - "fmt" - "strings" - "github.com/docker/libcontainer/cgroups" - "github.com/golang/glog" - "github.com/google/cadvisor/container" - "github.com/google/cadvisor/info" -) - -type cgroupSubsystems struct { - // Cgroup subsystem mounts. - mounts []cgroups.Mount - - // Cgroup subsystem to their mount location. - mountPoints map[string]string -} - -type lxcFactory struct { - // Factory for machine information. - machineInfoFactory info.MachineInfoFactory - cgroupSubsystems *cgroupSubsystems -} - -func (self *lxcFactory) String() string { - return "lxc" -} - -func (self *lxcFactory) NewContainerHandler(name string) (container.ContainerHandler, error) { - fmt.Printf("Invoking NewLxcContainerhandler for %s by %q\n", name, self.String()) - return newLxcContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory) -} - -// The lxc factory can handle any container. -func (self *lxcFactory) CanHandle(name string) (bool, error) { - if name == "/lxc" { - return true, nil - } else if strings.HasPrefix(name, "/lxc/") { - return true, nil - } - return false, nil -} - -func Register(machineInfoFactory info.MachineInfoFactory) error { - // Get all cgroup mounts. - allCgroups, err := cgroups.GetCgroupMounts() - if err != nil { - return err - } - if len(allCgroups) == 0 { - return fmt.Errorf("failed to find cgroup mounts for the lxc factory") - } - - // Trim the mounts to only the subsystems we care about. - supportedCgroups := make([]cgroups.Mount, 0, len(allCgroups)) - mountPoints := make(map[string]string, len(allCgroups)) - for _, mount := range allCgroups { - for _, subsystem := range mount.Subsystems { - if _, ok := supportedSubsystems[subsystem]; ok { - supportedCgroups = append(supportedCgroups, mount) - mountPoints[subsystem] = mount.Mountpoint - } - } - } - if len(supportedCgroups) == 0 { - return fmt.Errorf("failed to find supported cgroup mounts for the lxc factory") - } - - glog.Infof("Registering Lxc factory") - factory := &lxcFactory{ - machineInfoFactory: machineInfoFactory, - cgroupSubsystems: &cgroupSubsystems{ - mounts: supportedCgroups, - mountPoints: mountPoints, - }, - } - container.RegisterContainerHandlerFactory(factory) - return nil -} - -// Cgroup subsystems we support listing (should be the minimal set we need stats from). -var supportedSubsystems map[string]struct{} = map[string]struct{}{ - "cpu": {}, - "cpuacct": {}, - "memory": {}, - "cpuset": {}, -} diff --git a/container/lxc/handler.go b/container/lxc/handler.go deleted file mode 100644 index a9d16bdb..00000000 --- a/container/lxc/handler.go +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright 2014 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. - -// Handles containers launched by lxc (under /var/lib/docker/lxc) -// Gathers stats about memory, cpu, network, and diskio -package lxc - -import ( - "code.google.com/p/go.exp/inotify" - "flag" - "fmt" - "io/ioutil" - "path" - "strconv" - "strings" - dockerlibcontainer "github.com/docker/libcontainer" - "github.com/docker/libcontainer/cgroups" - cgroup_fs "github.com/docker/libcontainer/cgroups/fs" - "github.com/docker/libcontainer/network" - "github.com/golang/glog" - "github.com/google/cadvisor/container" - "github.com/google/cadvisor/container/libcontainer" - "github.com/google/cadvisor/fs" - "github.com/google/cadvisor/info" - "github.com/google/cadvisor/utils" -) - -var containersDesc = flag.String("cDesc", "/etc/docker/cdesc.json", "container description file") - -type lxcContainerHandler struct { - name string - cgroup *cgroups.Cgroup - cgroupSubsystems *cgroupSubsystems - machineInfoFactory info.MachineInfoFactory - watcher *inotify.Watcher - stopWatcher chan error - watches map[string]struct{} - fsInfo fs.FsInfo - network_interface *NetworkInterface -} - -func newLxcContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { - fsInfo, err := fs.NewFsInfo() - if err != nil { - return nil, err - } - cDesc, err := Unmarshal(*containersDesc) - var network_interface *NetworkInterface - for _, container := range cDesc.All_hosts { - cName := "/lxc/" + container.Id - glog.Infof("container %s Name %s \n\n", container, name) - if cName == name { - network_interface = container.Network_interface - fmt.Printf("Found network interface %s \n\n", network_interface) - break - } - } - return &lxcContainerHandler{ - name: name, - cgroup: &cgroups.Cgroup{ - Parent: "/", - Name: name, - }, - cgroupSubsystems: cgroupSubsystems, - machineInfoFactory: machineInfoFactory, - stopWatcher: make(chan error), - watches: make(map[string]struct{}), - fsInfo: fsInfo, - network_interface: network_interface, - }, nil -} - -func (self *lxcContainerHandler) ContainerReference() (info.ContainerReference, error) { - // We only know the container by its one name. - return info.ContainerReference{ - Name: self.name, - }, nil -} - -func readString(dirpath string, file string) string { - cgroupFile := path.Join(dirpath, file) - - // Ignore non-existent files - if !utils.FileExists(cgroupFile) { - return "" - } - - // Read - out, err := ioutil.ReadFile(cgroupFile) - if err != nil { - glog.Errorf("lxc driver: Failed to read %q: %s", cgroupFile, err) - return "" - } - return string(out) -} - -func readInt64(dirpath string, file string) uint64 { - out := readString(dirpath, file) - if out == "" { - return 0 - } - - val, err := strconv.ParseUint(strings.TrimSpace(out), 10, 64) - if err != nil { - glog.Errorf("lxc driver: Failed to parse int %q from file %q: %s", out, path.Join(dirpath, file), err) - return 0 - } - - return val -} - -func (self *lxcContainerHandler) GetSpec() (info.ContainerSpec, error) { - var spec info.ContainerSpec - - // The lxc driver assumes unified hierarchy containers. - - // Get machine info. - mi, err := self.machineInfoFactory.GetMachineInfo() - if err != nil { - return spec, err - } - - // CPU. - cpuRoot, ok := self.cgroupSubsystems.mountPoints["cpu"] - if ok { - cpuRoot = path.Join(cpuRoot, self.name) - if utils.FileExists(cpuRoot) { - spec.HasCpu = true - spec.Cpu.Limit = readInt64(cpuRoot, "cpu.shares") - } - } - - // Cpu Mask. - // This will fail for non-unified hierarchies. We'll return the whole machine mask in that case. - cpusetRoot, ok := self.cgroupSubsystems.mountPoints["cpuset"] - if ok { - cpusetRoot = path.Join(cpusetRoot, self.name) - if utils.FileExists(cpusetRoot) { - spec.HasCpu = true - spec.Cpu.Mask = readString(cpusetRoot, "cpuset.cpus") - if spec.Cpu.Mask == "" { - spec.Cpu.Mask = fmt.Sprintf("0-%d", mi.NumCores-1) - } - } - } - - // Memory. - memoryRoot, ok := self.cgroupSubsystems.mountPoints["memory"] - if ok { - memoryRoot = path.Join(memoryRoot, self.name) - if utils.FileExists(memoryRoot) { - spec.HasMemory = true - spec.Memory.Limit = readInt64(memoryRoot, "memory.limit_in_bytes") - spec.Memory.SwapLimit = readInt64(memoryRoot, "memory.memsw.limit_in_bytes") - } - } - - // Fs. - if self.name == "/" { - spec.HasFilesystem = true - } - return spec, nil -} - -func (self *lxcContainerHandler) GetStats() (*info.ContainerStats, error) { - var stats *info.ContainerStats - var err error - if self.network_interface != nil { - n := network.NetworkState{VethHost: self.network_interface.VethHost, VethChild: self.network_interface.VethChild, NsPath: "unknown"} - s := dockerlibcontainer.State{NetworkState: n} - stats, err = libcontainer.GetStats(self.cgroup, &s) - } else { - stats, err = libcontainer.GetStatsCgroupOnly(self.cgroup) - } - if err != nil { - return nil, err - } - // Get Filesystem information only for the root cgroup. - if self.name == "/" { - stats.Filesystem, err = self.fsInfo.GetFsStats() - if err != nil { - return nil, err - } - } - - return stats, nil -} - -// Lists all directories under "path" and outputs the results as children of "parent". -func listDirectories(dirpath string, parent string, recursive bool, output map[string]struct{}) error { - // Ignore if this hierarchy does not exist. - if !utils.FileExists(dirpath) { - return nil - } - - entries, err := ioutil.ReadDir(dirpath) - if err != nil { - return err - } - for _, entry := range entries { - // We only grab directories. - if entry.IsDir() { - name := path.Join(parent, entry.Name()) - output[name] = struct{}{} - - // List subcontainers if asked to. - if recursive { - err := listDirectories(path.Join(dirpath, entry.Name()), name, true, output) - if err != nil { - return err - } - } - } - } - return nil -} - -func (self *lxcContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { - containers := make(map[string]struct{}) - for _, subsystem := range self.cgroupSubsystems.mounts { - err := listDirectories(path.Join(subsystem.Mountpoint, self.name), self.name, listType == container.ListRecursive, containers) - if err != nil { - return nil, err - } - } - - // Make into container references. - ret := make([]info.ContainerReference, 0, len(containers)) - for cont := range containers { - ret = append(ret, info.ContainerReference{ - Name: cont, - }) - } - - return ret, nil -} - -func (self *lxcContainerHandler) ListThreads(listType container.ListType) ([]int, error) { - // TODO(vmarmol): Implement - return nil, nil -} - -func (self *lxcContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { - return cgroup_fs.GetPids(self.cgroup) -} - -func (self *lxcContainerHandler) watchDirectory(dir string, containerName string) error { - err := self.watcher.AddWatch(dir, inotify.IN_CREATE|inotify.IN_DELETE|inotify.IN_MOVE) - if err != nil { - return err - } - self.watches[containerName] = struct{}{} - - // Watch subdirectories as well. - entries, err := ioutil.ReadDir(dir) - if err != nil { - return err - } - for _, entry := range entries { - if entry.IsDir() { - err = self.watchDirectory(path.Join(dir, entry.Name()), path.Join(containerName, entry.Name())) - if err != nil { - return err - } - } - } - return nil -} - -func (self *lxcContainerHandler) processEvent(event *inotify.Event, events chan container.SubcontainerEvent) error { - // Convert the inotify event type to a container create or delete. - var eventType container.SubcontainerEventType - switch { - case (event.Mask & inotify.IN_CREATE) > 0: - eventType = container.SubcontainerAdd - case (event.Mask & inotify.IN_DELETE) > 0: - eventType = container.SubcontainerDelete - case (event.Mask & inotify.IN_MOVED_FROM) > 0: - eventType = container.SubcontainerDelete - case (event.Mask & inotify.IN_MOVED_TO) > 0: - eventType = container.SubcontainerAdd - default: - // Ignore other events. - return nil - } - - // Derive the container name from the path name. - var containerName string - for _, mount := range self.cgroupSubsystems.mounts { - mountLocation := path.Clean(mount.Mountpoint) + "/" - if strings.HasPrefix(event.Name, mountLocation) { - containerName = event.Name[len(mountLocation)-1:] - break - } - } - if containerName == "" { - return fmt.Errorf("unable to detect container from watch event on directory %q", event.Name) - } - - // Maintain the watch for the new or deleted container. - switch { - case eventType == container.SubcontainerAdd: - // If we've already seen this event, return. - if _, ok := self.watches[containerName]; ok { - return nil - } - - // New container was created, watch it. - err := self.watchDirectory(event.Name, containerName) - if err != nil { - return err - } - case eventType == container.SubcontainerDelete: - // If we've already seen this event, return. - if _, ok := self.watches[containerName]; !ok { - return nil - } - delete(self.watches, containerName) - - // Container was deleted, stop watching for it. - err := self.watcher.RemoveWatch(event.Name) - if err != nil { - return err - } - default: - return fmt.Errorf("unknown event type %v", eventType) - } - - // Deliver the event. - events <- container.SubcontainerEvent{ - EventType: eventType, - Name: containerName, - } - - return nil -} - -func (self *lxcContainerHandler) 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 _, mnt := range self.cgroupSubsystems.mounts { - err := self.watchDirectory(path.Join(mnt.Mountpoint, self.name), self.name) - if err != nil { - return err - } - } - - // Process the events received from the kernel. - go func() { - for { - select { - 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: - 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 - } - } - } - }() - - return nil -} - -func (self *lxcContainerHandler) 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 -} diff --git a/container/lxc/container_desc.go b/container/raw/container_desc.go similarity index 75% rename from container/lxc/container_desc.go rename to container/raw/container_desc.go index c1f2f0f8..09b7ed23 100644 --- a/container/lxc/container_desc.go +++ b/container/raw/container_desc.go @@ -16,31 +16,32 @@ // an array of ContainerDesc structs, each with a container's id and network_interface // This allows collecting stats about network interfaces configured outside docker // and lxc -package lxc +package raw import ( + "flag" "encoding/json" "io/ioutil" ) - -type ContainersDesc struct { - All_hosts []ContainerDesc +var argContainersDesc = flag.String("cDescription", "/etc/docker/cdesc.json", "container description file") +type containersDesc struct { + All_hosts []containerDesc } -type ContainerDesc struct { +type containerDesc struct { Id string - Network_interface *NetworkInterface + Network_interface *networkInterface } -type NetworkInterface struct { +type networkInterface struct { VethHost string VethChild string NsPath string } -func Unmarshal(containerDescFile string) (ContainersDesc, error) { +func Unmarshal(containerDescFile string) (containersDesc, error) { dat, err := ioutil.ReadFile(containerDescFile) - var cDesc ContainersDesc + var cDesc containersDesc if err == nil { err = json.Unmarshal(dat, &cDesc) } diff --git a/container/lxc/container_desc_test.go b/container/raw/container_desc_test.go similarity index 90% rename from container/lxc/container_desc_test.go rename to container/raw/container_desc_test.go index f5cbd8cc..faecd88b 100644 --- a/container/lxc/container_desc_test.go +++ b/container/raw/container_desc_test.go @@ -1,4 +1,4 @@ -package lxc +package raw import ( "testing" @@ -6,11 +6,11 @@ import ( func TestUnmarshal(t *testing.T) { cDesc, err := Unmarshal("test_resources/cdesc.json") + if err != nil { t.Fatalf("Error in unmarshalling: %s", err) } - t.Logf("Cdesc: %s", cDesc) if cDesc.All_hosts[0].Network_interface.VethHost != "veth24031eth1" && cDesc.All_hosts[0].Network_interface.VethChild != "eth1" { t.Errorf("Cannot find network interface in %s", cDesc) diff --git a/container/raw/handler.go b/container/raw/handler.go index 9a3c521c..1233e17d 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -23,8 +23,10 @@ import ( "strings" "code.google.com/p/go.exp/inotify" + dockerlibcontainer "github.com/docker/libcontainer" "github.com/docker/libcontainer/cgroups" cgroup_fs "github.com/docker/libcontainer/cgroups/fs" + "github.com/docker/libcontainer/network" "github.com/golang/glog" "github.com/google/cadvisor/container" "github.com/google/cadvisor/container/libcontainer" @@ -42,6 +44,7 @@ type rawContainerHandler struct { stopWatcher chan error watches map[string]struct{} fsInfo fs.FsInfo + network_interface *networkInterface } func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { @@ -49,6 +52,18 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac if err != nil { return nil, err } + cDesc, err := Unmarshal(*argContainersDesc) + var network_interface *networkInterface + for _, container := range cDesc.All_hosts { + names := strings.SplitAfter(name, "/") + cName := names[len(names) - 1] + glog.Infof("container %s Name %s \n\n", container, name) + if cName == container.Id { + network_interface = container.Network_interface + fmt.Printf("Found network interface %s \n\n", network_interface) + break + } + } return &rawContainerHandler{ name: name, cgroup: &cgroups.Cgroup{ @@ -60,6 +75,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac stopWatcher: make(chan error), watches: make(map[string]struct{}), fsInfo: fsInfo, + network_interface: network_interface, }, nil } @@ -156,9 +172,17 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) { } func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { - stats, err := libcontainer.GetStatsCgroupOnly(self.cgroup) + var stats *info.ContainerStats + var err error + if self.network_interface != nil { + n := network.NetworkState{VethHost: self.network_interface.VethHost, VethChild: self.network_interface.VethChild, NsPath: "unknown"} + s := dockerlibcontainer.State{NetworkState: n} + stats, err = libcontainer.GetStats(self.cgroup, &s) + } else { + stats, err = libcontainer.GetStatsCgroupOnly(self.cgroup) + } if err != nil { - return nil, err + return nil, err } // Get Filesystem information only for the root cgroup. if self.name == "/" { diff --git a/container/lxc/test_resources/cdesc.json b/container/raw/test_resources/cdesc.json similarity index 100% rename from container/lxc/test_resources/cdesc.json rename to container/raw/test_resources/cdesc.json From 07fbd1ddab3e00f38ecb41938237a6be821fe96a Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Tue, 14 Oct 2014 02:59:14 +0000 Subject: [PATCH 05/10] Fixed formatting --- container/raw/handler.go | 6 +++--- container/raw/test_resources/cdesc.json | 14 ++------------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/container/raw/handler.go b/container/raw/handler.go index 1233e17d..781280f4 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -177,12 +177,12 @@ func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { if self.network_interface != nil { n := network.NetworkState{VethHost: self.network_interface.VethHost, VethChild: self.network_interface.VethChild, NsPath: "unknown"} s := dockerlibcontainer.State{NetworkState: n} - stats, err = libcontainer.GetStats(self.cgroup, &s) + stats, err = libcontainer.GetStats(self.cgroup, &s) } else { - stats, err = libcontainer.GetStatsCgroupOnly(self.cgroup) + stats, err = libcontainer.GetStatsCgroupOnly(self.cgroup) } if err != nil { - return nil, err + return nil, err } // Get Filesystem information only for the root cgroup. if self.name == "/" { diff --git a/container/raw/test_resources/cdesc.json b/container/raw/test_resources/cdesc.json index 33c86735..713eb7e1 100644 --- a/container/raw/test_resources/cdesc.json +++ b/container/raw/test_resources/cdesc.json @@ -5,8 +5,6 @@ { "name": "10-10-10-27", "ip": "10.10.10.27/24", - "image": "registry.test.altiscale.com:5000/prometheus-init-201405270218", - "public_key": "/root/.ssh/authorized_keys", "publish": null, "gateway": null, "hostname": "10-10-10-27.local.altiscale.com", @@ -14,7 +12,6 @@ "init": null, "cpu": null, "memory": null, - "role": "compute_node", "net": null, "netinit": "false", "network_interface": { @@ -48,14 +45,7 @@ "permission": "rw" } ], - "host": { - "hostname": "localhost", - "ip": "127.0.0.1", - "bridge": "alti0", - "key": "" - }, - "id": "18a4585950db428e4d5a65c216a5d708d241254709626f4cb300ee963fb4b144", - "initpid": "10741" + "id": "18a4585950db428e4d5a65c216a5d708d241254709626f4cb300ee963fb4b144" } ] -} \ No newline at end of file +} From 99d2c31f4d994e1397c7fce73abb8411a3ccedaf Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Tue, 14 Oct 2014 08:45:18 +0000 Subject: [PATCH 06/10] Renamed container description types --- container/raw/container_desc.go | 26 +++++++++---------- container/raw/container_desc_test.go | 4 +-- container/raw/handler.go | 38 ++++++++++++++++++---------- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/container/raw/container_desc.go b/container/raw/container_desc.go index 09b7ed23..7038c70b 100644 --- a/container/raw/container_desc.go +++ b/container/raw/container_desc.go @@ -13,7 +13,7 @@ // limitations under the License. // Unmarshal's a Containers description json file. The json file contains -// an array of ContainerDesc structs, each with a container's id and network_interface +// an array of ContainerHint structs, each with a container's id and networkInterface // This allows collecting stats about network interfaces configured outside docker // and lxc package raw @@ -23,25 +23,25 @@ import ( "encoding/json" "io/ioutil" ) -var argContainersDesc = flag.String("cDescription", "/etc/docker/cdesc.json", "container description file") -type containersDesc struct { - All_hosts []containerDesc +var argContainerHints = flag.String("container_hints", "/etc/cadvisor/container_description.json", "container hints file") +type containerHints struct { + All_hosts []containerHint `json:"all_hosts,omitempty"` } -type containerDesc struct { - Id string - Network_interface *networkInterface +type containerHint struct { + Id string `json:"id,omitempty"` + NetworkInterface *networkInterface `json:"network_interface,omitempty"` } type networkInterface struct { - VethHost string - VethChild string - NsPath string + VethHost string `json:"VethHost,omitempty"` + VethChild string `json:"VethChild,omitempty"` + NsPath string `json:"NsPath,omitempty"` } -func Unmarshal(containerDescFile string) (containersDesc, error) { - dat, err := ioutil.ReadFile(containerDescFile) - var cDesc containersDesc +func Unmarshal(containerHintsFile string) (containerHints, error) { + dat, err := ioutil.ReadFile(containerHintsFile) + var cDesc containerHints if err == nil { err = json.Unmarshal(dat, &cDesc) } diff --git a/container/raw/container_desc_test.go b/container/raw/container_desc_test.go index faecd88b..e67c627d 100644 --- a/container/raw/container_desc_test.go +++ b/container/raw/container_desc_test.go @@ -11,8 +11,8 @@ func TestUnmarshal(t *testing.T) { t.Fatalf("Error in unmarshalling: %s", err) } - if cDesc.All_hosts[0].Network_interface.VethHost != "veth24031eth1" && - cDesc.All_hosts[0].Network_interface.VethChild != "eth1" { + if cDesc.All_hosts[0].NetworkInterface.VethHost != "veth24031eth1" && + cDesc.All_hosts[0].NetworkInterface.VethChild != "eth1" { t.Errorf("Cannot find network interface in %s", cDesc) } } diff --git a/container/raw/handler.go b/container/raw/handler.go index 781280f4..44e253cd 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -44,7 +44,7 @@ type rawContainerHandler struct { stopWatcher chan error watches map[string]struct{} fsInfo fs.FsInfo - network_interface *networkInterface + networkInterface *networkInterface } func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { @@ -52,15 +52,21 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac if err != nil { return nil, err } - cDesc, err := Unmarshal(*argContainersDesc) - var network_interface *networkInterface + cDesc, err := Unmarshal(*argContainerHints) + if err != nil { + return nil, err + } + var networkInterface *networkInterface for _, container := range cDesc.All_hosts { - names := strings.SplitAfter(name, "/") - cName := names[len(names) - 1] - glog.Infof("container %s Name %s \n\n", container, name) + var cName string + if strings.Contains(container.Id, "/") { + cName = name + } else { + names := strings.SplitAfter(name, "/") + cName = names[len(names)-1] + } if cName == container.Id { - network_interface = container.Network_interface - fmt.Printf("Found network interface %s \n\n", network_interface) + networkInterface = container.NetworkInterface break } } @@ -75,7 +81,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac stopWatcher: make(chan error), watches: make(map[string]struct{}), fsInfo: fsInfo, - network_interface: network_interface, + networkInterface: networkInterface, }, nil } @@ -174,10 +180,16 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) { func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { var stats *info.ContainerStats var err error - if self.network_interface != nil { - n := network.NetworkState{VethHost: self.network_interface.VethHost, VethChild: self.network_interface.VethChild, NsPath: "unknown"} - s := dockerlibcontainer.State{NetworkState: n} - stats, err = libcontainer.GetStats(self.cgroup, &s) + if self.networkInterface != nil { + state := dockerlibcontainer.State{ + NetworkState: network.NetworkState{ + VethHost: self.networkInterface.VethHost, + VethChild: self.networkInterface.VethChild, + NsPath: "unknown", + }, + } + + stats, err = libcontainer.GetStats(self.cgroup, &state) } else { stats, err = libcontainer.GetStatsCgroupOnly(self.cgroup) } From 4c740cbc54ed98288b44cd4dff9943d196435c47 Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Tue, 14 Oct 2014 19:56:40 +0000 Subject: [PATCH 07/10] matches with the full path of the container --- container/raw/container_desc_test.go | 18 --------------- .../{container_desc.go => container_hints.go} | 22 ++++++++++--------- container/raw/container_hints_test.go | 18 +++++++++++++++ container/raw/handler.go | 16 +++++--------- .../{cdesc.json => container_hints.json} | 21 +++++------------- 5 files changed, 41 insertions(+), 54 deletions(-) delete mode 100644 container/raw/container_desc_test.go rename container/raw/{container_desc.go => container_hints.go} (71%) create mode 100644 container/raw/container_hints_test.go rename container/raw/test_resources/{cdesc.json => container_hints.json} (61%) diff --git a/container/raw/container_desc_test.go b/container/raw/container_desc_test.go deleted file mode 100644 index e67c627d..00000000 --- a/container/raw/container_desc_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package raw - -import ( - "testing" -) - -func TestUnmarshal(t *testing.T) { - cDesc, err := Unmarshal("test_resources/cdesc.json") - - if err != nil { - t.Fatalf("Error in unmarshalling: %s", err) - } - - if cDesc.All_hosts[0].NetworkInterface.VethHost != "veth24031eth1" && - cDesc.All_hosts[0].NetworkInterface.VethChild != "eth1" { - t.Errorf("Cannot find network interface in %s", cDesc) - } -} diff --git a/container/raw/container_desc.go b/container/raw/container_hints.go similarity index 71% rename from container/raw/container_desc.go rename to container/raw/container_hints.go index 7038c70b..aa819b9c 100644 --- a/container/raw/container_desc.go +++ b/container/raw/container_hints.go @@ -23,27 +23,29 @@ import ( "encoding/json" "io/ioutil" ) -var argContainerHints = flag.String("container_hints", "/etc/cadvisor/container_description.json", "container hints file") +var argContainerHints = flag.String("container_hints", "/etc/cadvisor/container_hints.json", "container hints file") type containerHints struct { - All_hosts []containerHint `json:"all_hosts,omitempty"` + AllHosts []containerHint `json:"all_hosts,omitempty"` } type containerHint struct { - Id string `json:"id,omitempty"` + FullPath string `json:"full_path,omitempty"` NetworkInterface *networkInterface `json:"network_interface,omitempty"` } type networkInterface struct { - VethHost string `json:"VethHost,omitempty"` - VethChild string `json:"VethChild,omitempty"` - NsPath string `json:"NsPath,omitempty"` + VethHost string `json:"veth_host,omitempty"` + VethChild string `json:"veth_child,omitempty"` + NsPath string `json:"ns_path,omitempty"` } -func Unmarshal(containerHintsFile string) (containerHints, error) { +func getContainerHintsFromFile(containerHintsFile string) (containerHints, error) { dat, err := ioutil.ReadFile(containerHintsFile) - var cDesc containerHints + + var cHints containerHints if err == nil { - err = json.Unmarshal(dat, &cDesc) + err = json.Unmarshal(dat, &cHints) } - return cDesc, err + + return cHints, err } diff --git a/container/raw/container_hints_test.go b/container/raw/container_hints_test.go new file mode 100644 index 00000000..04fee7a7 --- /dev/null +++ b/container/raw/container_hints_test.go @@ -0,0 +1,18 @@ +package raw + +import ( + "testing" +) + +func TestUnmarshal(t *testing.T) { + cHints, err := getContainerHintsFromFile("test_resources/container_hints.json") + + if err != nil { + t.Fatalf("Error in unmarshalling: %s", err) + } + + if cHints.AllHosts[0].NetworkInterface.VethHost != "veth24031eth1" && + cHints.AllHosts[0].NetworkInterface.VethChild != "eth1" { + t.Errorf("Cannot find network interface in %s", cHints) + } +} diff --git a/container/raw/handler.go b/container/raw/handler.go index 44e253cd..e0cbf062 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -52,20 +52,16 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac if err != nil { return nil, err } - cDesc, err := Unmarshal(*argContainerHints) + cHints, err := getContainerHintsFromFile(*argContainerHints) if err != nil { - return nil, err + glog.Fatalf("Error unmarshalling json %s Error: %s", *argContainerHints, err) } var networkInterface *networkInterface - for _, container := range cDesc.All_hosts { - var cName string - if strings.Contains(container.Id, "/") { - cName = name - } else { - names := strings.SplitAfter(name, "/") - cName = names[len(names)-1] + for _, container := range cHints.AllHosts { + if !strings.Contains(container.FullPath, "/") { + glog.Fatalf("Invalid container fullPath %s", container.FullPath) } - if cName == container.Id { + if name == container.FullPath { networkInterface = container.NetworkInterface break } diff --git a/container/raw/test_resources/cdesc.json b/container/raw/test_resources/container_hints.json similarity index 61% rename from container/raw/test_resources/cdesc.json rename to container/raw/test_resources/container_hints.json index 713eb7e1..d6d95fb8 100644 --- a/container/raw/test_resources/cdesc.json +++ b/container/raw/test_resources/container_hints.json @@ -1,22 +1,11 @@ { - "name": "Container specification", - "description": "Container specification", + "name": "Container Hints", + "description": "Container hints file", "all_hosts": [ { - "name": "10-10-10-27", - "ip": "10.10.10.27/24", - "publish": null, - "gateway": null, - "hostname": "10-10-10-27.local.altiscale.com", - "privileged": "true", - "init": null, - "cpu": null, - "memory": null, - "net": null, - "netinit": "false", "network_interface": { - "VethChild": "eth1", - "VethHost": "veth24031eth1" + "veth_child": "eth1", + "veth_host": "veth24031eth1" }, "mounts": [ { @@ -45,7 +34,7 @@ "permission": "rw" } ], - "id": "18a4585950db428e4d5a65c216a5d708d241254709626f4cb300ee963fb4b144" + "full_path": "18a4585950db428e4d5a65c216a5d708d241254709626f4cb300ee963fb4b144" } ] } From 9264114895e6fb6a011fab91ba7962ec67985132 Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Tue, 14 Oct 2014 23:19:46 +0000 Subject: [PATCH 08/10] some more renames --- container/raw/container_hints.go | 2 +- container/raw/handler.go | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/container/raw/container_hints.go b/container/raw/container_hints.go index aa819b9c..c3d9221a 100644 --- a/container/raw/container_hints.go +++ b/container/raw/container_hints.go @@ -29,7 +29,7 @@ type containerHints struct { } type containerHint struct { - FullPath string `json:"full_path,omitempty"` + FullName string `json:"full_path,omitempty"` NetworkInterface *networkInterface `json:"network_interface,omitempty"` } diff --git a/container/raw/handler.go b/container/raw/handler.go index e0cbf062..c0dfb8a1 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -54,14 +54,11 @@ func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, mac } cHints, err := getContainerHintsFromFile(*argContainerHints) if err != nil { - glog.Fatalf("Error unmarshalling json %s Error: %s", *argContainerHints, err) + return nil, err } var networkInterface *networkInterface for _, container := range cHints.AllHosts { - if !strings.Contains(container.FullPath, "/") { - glog.Fatalf("Invalid container fullPath %s", container.FullPath) - } - if name == container.FullPath { + if name == container.FullName { networkInterface = container.NetworkInterface break } From f170df0a7617d422f3054b5388b2d7d2812a6bac Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Wed, 15 Oct 2014 07:58:58 +0000 Subject: [PATCH 09/10] Spec consistency --- container/raw/container_hints.go | 6 ++++-- container/raw/container_hints_test.go | 9 ++++++++- container/raw/handler.go | 16 +++++++++------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/container/raw/container_hints.go b/container/raw/container_hints.go index c3d9221a..a5473899 100644 --- a/container/raw/container_hints.go +++ b/container/raw/container_hints.go @@ -22,6 +22,7 @@ import ( "flag" "encoding/json" "io/ioutil" + "os" ) var argContainerHints = flag.String("container_hints", "/etc/cadvisor/container_hints.json", "container hints file") type containerHints struct { @@ -36,12 +37,13 @@ type containerHint struct { type networkInterface struct { VethHost string `json:"veth_host,omitempty"` VethChild string `json:"veth_child,omitempty"` - NsPath string `json:"ns_path,omitempty"` } func getContainerHintsFromFile(containerHintsFile string) (containerHints, error) { dat, err := ioutil.ReadFile(containerHintsFile) - + if os.IsNotExist(err) { + return containerHints{}, nil + } var cHints containerHints if err == nil { err = json.Unmarshal(dat, &cHints) diff --git a/container/raw/container_hints_test.go b/container/raw/container_hints_test.go index 04fee7a7..6dfd2704 100644 --- a/container/raw/container_hints_test.go +++ b/container/raw/container_hints_test.go @@ -4,7 +4,7 @@ import ( "testing" ) -func TestUnmarshal(t *testing.T) { +func TestGetContainerHintsFromFile(t *testing.T) { cHints, err := getContainerHintsFromFile("test_resources/container_hints.json") if err != nil { @@ -16,3 +16,10 @@ func TestUnmarshal(t *testing.T) { t.Errorf("Cannot find network interface in %s", cHints) } } + +func TestFileNotExist(t *testing.T) { + _, err := getContainerHintsFromFile("/file_does_not_exist.json") + if err != nil { + t.Fatalf("getContainerHintsFromFile must not error for blank file: %s", err) + } +} diff --git a/container/raw/handler.go b/container/raw/handler.go index c0dfb8a1..007a0fc9 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -167,25 +167,27 @@ func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) { if self.name == "/" { spec.HasFilesystem = true } + + //Network + if self.networkInterface != nil { + spec.HasNetwork = true + } return spec, nil } func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { - var stats *info.ContainerStats - var err error + state := dockerlibcontainer.State{} if self.networkInterface != nil { - state := dockerlibcontainer.State{ + state = dockerlibcontainer.State{ NetworkState: network.NetworkState{ VethHost: self.networkInterface.VethHost, VethChild: self.networkInterface.VethChild, NsPath: "unknown", }, } - - stats, err = libcontainer.GetStats(self.cgroup, &state) - } else { - stats, err = libcontainer.GetStatsCgroupOnly(self.cgroup) } + + stats, err := libcontainer.GetStats(self.cgroup, &state) if err != nil { return nil, err } From c4d663c6ab55536cb72d84bf8ccb6a01f8c38776 Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Wed, 15 Oct 2014 11:10:54 +0000 Subject: [PATCH 10/10] minor fixes and gofmt --- container/raw/container_hints.go | 8 +++++--- container/raw/container_hints_test.go | 7 +++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/container/raw/container_hints.go b/container/raw/container_hints.go index a5473899..b1ea21c1 100644 --- a/container/raw/container_hints.go +++ b/container/raw/container_hints.go @@ -19,18 +19,20 @@ package raw import ( - "flag" "encoding/json" + "flag" "io/ioutil" "os" ) + var argContainerHints = flag.String("container_hints", "/etc/cadvisor/container_hints.json", "container hints file") + type containerHints struct { AllHosts []containerHint `json:"all_hosts,omitempty"` } type containerHint struct { - FullName string `json:"full_path,omitempty"` + FullName string `json:"full_path,omitempty"` NetworkInterface *networkInterface `json:"network_interface,omitempty"` } @@ -41,7 +43,7 @@ type networkInterface struct { func getContainerHintsFromFile(containerHintsFile string) (containerHints, error) { dat, err := ioutil.ReadFile(containerHintsFile) - if os.IsNotExist(err) { + if os.IsNotExist(err) { return containerHints{}, nil } var cHints containerHints diff --git a/container/raw/container_hints_test.go b/container/raw/container_hints_test.go index 6dfd2704..f885f6ac 100644 --- a/container/raw/container_hints_test.go +++ b/container/raw/container_hints_test.go @@ -18,8 +18,11 @@ func TestGetContainerHintsFromFile(t *testing.T) { } func TestFileNotExist(t *testing.T) { - _, err := getContainerHintsFromFile("/file_does_not_exist.json") - if err != nil { + cHints, err := getContainerHintsFromFile("/file_does_not_exist.json") + if err != nil { t.Fatalf("getContainerHintsFromFile must not error for blank file: %s", err) } + for _, container := range cHints.AllHosts { + t.Logf("Container: %s", container) + } }