From 7e10398a507731705797fca6e91c2258bfeed738 Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Fri, 28 Aug 2015 18:15:25 +0100 Subject: [PATCH] Use proc fs to get network stats. Reasons discussed in https://github.com/google/cadvisor/issues/822#issuecomment-135811901 & following. --- Godeps/Godeps.json | 6 +- .../src/github.com/vishvananda/netns/LICENSE | 192 ---------------- .../github.com/vishvananda/netns/README.md | 49 ----- .../src/github.com/vishvananda/netns/netns.go | 67 ------ .../vishvananda/netns/netns_linux.go | 205 ------------------ .../vishvananda/netns/netns_linux_386.go | 7 - .../vishvananda/netns/netns_linux_amd64.go | 7 - .../vishvananda/netns/netns_linux_arm.go | 7 - .../vishvananda/netns/netns_linux_arm64.go | 7 - .../vishvananda/netns/netns_linux_ppc64le.go | 7 - .../vishvananda/netns/netns_linux_s390x.go | 7 - .../vishvananda/netns/netns_test.go | 66 ------ .../vishvananda/netns/netns_unspecified.go | 35 --- container/docker/factory.go | 3 +- container/docker/handler.go | 85 +++----- container/factory.go | 6 +- container/factory_test.go | 14 +- container/libcontainer/helpers.go | 103 +++------ container/mock.go | 2 +- container/raw/factory.go | 8 +- container/raw/handler.go | 15 +- manager/manager.go | 2 +- 22 files changed, 87 insertions(+), 813 deletions(-) delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/README.md delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns.go delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux.go delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_386.go delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_amd64.go delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm.go delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm64.go delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_ppc64le.go delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_s390x.go delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_test.go delete mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_unspecified.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 530c8160..2486acd1 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/google/cadvisor", - "GoVersion": "go1.4.2", + "GoVersion": "go1.4", "Packages": [ "./..." ], @@ -184,10 +184,6 @@ "ImportPath": "github.com/syndtr/gocapability/capability", "Rev": "8e4cdcb3c22b40d5e330ade0b68cb2e2a3cf6f98" }, - { - "ImportPath": "github.com/vishvananda/netns", - "Rev": "604eaf189ee867d8c147fafc28def2394e878d25" - }, { "ImportPath": "golang.org/x/exp/inotify", "Rev": "d00e13ec443927751b2bd49e97dea7bf3b6a6487" diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/LICENSE b/Godeps/_workspace/src/github.com/vishvananda/netns/LICENSE deleted file mode 100644 index 9f64db85..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/LICENSE +++ /dev/null @@ -1,192 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2014 Vishvananda Ishaya. - Copyright 2014 Docker, Inc. - - 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. diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/README.md b/Godeps/_workspace/src/github.com/vishvananda/netns/README.md deleted file mode 100644 index 24a4003a..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# netns - network namespaces in go # - -The netns package provides an ultra-simple interface for handling -network namespaces in go. Changing namespaces requires elevated -privileges, so in most cases this code needs to be run as root. - -## Local Build and Test ## - -You can use go get command: - - go get github.com/vishvananda/netns - -Testing (requires root): - - sudo -E go test github.com/vishvananda/netns - -## Example ## - -```go -package main - -import ( - "net" - "runtime" - "github.com/vishvananada/netns" -) - -func main() { - // Lock the OS Thread so we don't accidentally switch namespaces - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - // Save the current network namespace - origns, _ := netns.Get() - defer origns.Close() - - // Create a new network namespace - newns, _ := netns.New() - defer newns.Close() - - // Do something with tne network namespace - ifaces, _ := net.Interfaces() - fmt.Printf("Interfaces: %v\n", ifaces) - - // Switch back to the original namespace - netns.Set(origns) -} - -``` diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns.go deleted file mode 100644 index 2ca0feed..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns.go +++ /dev/null @@ -1,67 +0,0 @@ -// Package netns allows ultra-simple network namespace handling. NsHandles -// can be retrieved and set. Note that the current namespace is thread -// local so actions that set and reset namespaces should use LockOSThread -// to make sure the namespace doesn't change due to a goroutine switch. -// It is best to close NsHandles when you are done with them. This can be -// accomplished via a `defer ns.Close()` on the handle. Changing namespaces -// requires elevated privileges, so in most cases this code needs to be run -// as root. -package netns - -import ( - "fmt" - "syscall" -) - -// NsHandle is a handle to a network namespace. It can be cast directly -// to an int and used as a file descriptor. -type NsHandle int - -// Equal determines if two network handles refer to the same network -// namespace. This is done by comparing the device and inode that the -// file descripors point to. -func (ns NsHandle) Equal(other NsHandle) bool { - if ns == other { - return true - } - var s1, s2 syscall.Stat_t - if err := syscall.Fstat(int(ns), &s1); err != nil { - return false - } - if err := syscall.Fstat(int(other), &s2); err != nil { - return false - } - return (s1.Dev == s2.Dev) && (s1.Ino == s2.Ino) -} - -// String shows the file descriptor number and its dev and inode. -func (ns NsHandle) String() string { - var s syscall.Stat_t - if ns == -1 { - return "NS(None)" - } - if err := syscall.Fstat(int(ns), &s); err != nil { - return fmt.Sprintf("NS(%d: unknown)", ns) - } - return fmt.Sprintf("NS(%d: %d, %d)", ns, s.Dev, s.Ino) -} - -// IsOpen returns true if Close() has not been called. -func (ns NsHandle) IsOpen() bool { - return ns != -1 -} - -// Close closes the NsHandle and resets its file descriptor to -1. -// It is not safe to use an NsHandle after Close() is called. -func (ns *NsHandle) Close() error { - if err := syscall.Close(int(*ns)); err != nil { - return err - } - (*ns) = -1 - return nil -} - -// Get an empty (closed) NsHandle -func None() NsHandle { - return NsHandle(-1) -} diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux.go deleted file mode 100644 index abdc3082..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux.go +++ /dev/null @@ -1,205 +0,0 @@ -// +build linux - -package netns - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - "syscall" -) - -const ( - // These constants belong in the syscall library but have not been - // added yet. - CLONE_NEWUTS = 0x04000000 /* New utsname group? */ - CLONE_NEWIPC = 0x08000000 /* New ipcs */ - CLONE_NEWUSER = 0x10000000 /* New user namespace */ - CLONE_NEWPID = 0x20000000 /* New pid namespace */ - CLONE_NEWNET = 0x40000000 /* New network namespace */ - CLONE_IO = 0x80000000 /* Get io context */ -) - -// Setns sets namespace using syscall. Note that this should be a method -// in syscall but it has not been added. -func Setns(ns NsHandle, nstype int) (err error) { - _, _, e1 := syscall.Syscall(SYS_SETNS, uintptr(ns), uintptr(nstype), 0) - if e1 != 0 { - err = e1 - } - return -} - -// Set sets the current network namespace to the namespace represented -// by NsHandle. -func Set(ns NsHandle) (err error) { - return Setns(ns, CLONE_NEWNET) -} - -// New creates a new network namespace and returns a handle to it. -func New() (ns NsHandle, err error) { - if err := syscall.Unshare(CLONE_NEWNET); err != nil { - return -1, err - } - return Get() -} - -// Get gets a handle to the current threads network namespace. -func Get() (NsHandle, error) { - return GetFromThread(os.Getpid(), syscall.Gettid()) -} - -// GetFromPath gets a handle to a network namespace -// identified by the path -func GetFromPath(path string) (NsHandle, error) { - fd, err := syscall.Open(path, syscall.O_RDONLY, 0) - if err != nil { - return -1, err - } - return NsHandle(fd), nil -} - -// GetFromName gets a handle to a named network namespace such as one -// created by `ip netns add`. -func GetFromName(name string) (NsHandle, error) { - return GetFromPath(fmt.Sprintf("/var/run/netns/%s", name)) -} - -// GetFromPid gets a handle to the network namespace of a given pid. -func GetFromPid(pid int) (NsHandle, error) { - return GetFromPath(fmt.Sprintf("/proc/%d/ns/net", pid)) -} - -// GetFromThread gets a handle to the network namespace of a given pid and tid. -func GetFromThread(pid, tid int) (NsHandle, error) { - return GetFromPath(fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid)) -} - -// GetFromDocker gets a handle to the network namespace of a docker container. -// Id is prefixed matched against the running docker containers, so a short -// identifier can be used as long as it isn't ambiguous. -func GetFromDocker(id string) (NsHandle, error) { - pid, err := getPidForContainer(id) - if err != nil { - return -1, err - } - return GetFromPid(pid) -} - -// borrowed from docker/utils/utils.go -func findCgroupMountpoint(cgroupType string) (string, error) { - output, err := ioutil.ReadFile("/proc/mounts") - if err != nil { - return "", err - } - - // /proc/mounts has 6 fields per line, one mount per line, e.g. - // cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0 - for _, line := range strings.Split(string(output), "\n") { - parts := strings.Split(line, " ") - if len(parts) == 6 && parts[2] == "cgroup" { - for _, opt := range strings.Split(parts[3], ",") { - if opt == cgroupType { - return parts[1], nil - } - } - } - } - - return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) -} - -// Returns the relative path to the cgroup docker is running in. -// borrowed from docker/utils/utils.go -// modified to get the docker pid instead of using /proc/self -func getThisCgroup(cgroupType string) (string, error) { - dockerpid, err := ioutil.ReadFile("/var/run/docker.pid") - if err != nil { - return "", err - } - result := strings.Split(string(dockerpid), "\n") - if len(result) == 0 || len(result[0]) == 0 { - return "", fmt.Errorf("docker pid not found in /var/run/docker.pid") - } - pid, err := strconv.Atoi(result[0]) - - output, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid)) - if err != nil { - return "", err - } - for _, line := range strings.Split(string(output), "\n") { - parts := strings.Split(line, ":") - // any type used by docker should work - if parts[1] == cgroupType { - return parts[2], nil - } - } - return "", fmt.Errorf("cgroup '%s' not found in /proc/%d/cgroup", cgroupType, pid) -} - -// Returns the first pid in a container. -// borrowed from docker/utils/utils.go -// modified to only return the first pid -// modified to glob with id -// modified to search for newer docker containers -func getPidForContainer(id string) (int, error) { - pid := 0 - - // memory is chosen randomly, any cgroup used by docker works - cgroupType := "memory" - - cgroupRoot, err := findCgroupMountpoint(cgroupType) - if err != nil { - return pid, err - } - - cgroupThis, err := getThisCgroup(cgroupType) - if err != nil { - return pid, err - } - - id += "*" - - attempts := []string{ - filepath.Join(cgroupRoot, cgroupThis, id, "tasks"), - // With more recent lxc versions use, cgroup will be in lxc/ - filepath.Join(cgroupRoot, cgroupThis, "lxc", id, "tasks"), - // With more recent dockee, cgroup will be in docker/ - filepath.Join(cgroupRoot, cgroupThis, "docker", id, "tasks"), - } - - var filename string - for _, attempt := range attempts { - filenames, _ := filepath.Glob(attempt) - if len(filenames) > 1 { - return pid, fmt.Errorf("Ambiguous id supplied: %v", filenames) - } else if len(filenames) == 1 { - filename = filenames[0] - break - } - } - - if filename == "" { - return pid, fmt.Errorf("Unable to find container: %v", id[:len(id)-1]) - } - - output, err := ioutil.ReadFile(filename) - if err != nil { - return pid, err - } - - result := strings.Split(string(output), "\n") - if len(result) == 0 || len(result[0]) == 0 { - return pid, fmt.Errorf("No pid found for container") - } - - pid, err = strconv.Atoi(result[0]) - if err != nil { - return pid, fmt.Errorf("Invalid pid '%s': %s", result[0], err) - } - - return pid, nil -} diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_386.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_386.go deleted file mode 100644 index 1d769bb1..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_386.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build linux,386 - -package netns - -const ( - SYS_SETNS = 346 -) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_amd64.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_amd64.go deleted file mode 100644 index b124666f..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_amd64.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build linux,amd64 - -package netns - -const ( - SYS_SETNS = 308 -) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm.go deleted file mode 100644 index 9c74eb55..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build linux,arm - -package netns - -const ( - SYS_SETNS = 375 -) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm64.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm64.go deleted file mode 100644 index 741a3020..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm64.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build linux,arm64 - -package netns - -const ( - SYS_SETNS = 268 -) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_ppc64le.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_ppc64le.go deleted file mode 100644 index c49eba5e..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_ppc64le.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build linux,ppc64le - -package netns - -const ( - SYS_SETNS = 350 -) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_s390x.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_s390x.go deleted file mode 100644 index cc13e626..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_s390x.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build linux,s390x - -package netns - -const ( - SYS_SETNS = 339 -) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_test.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_test.go deleted file mode 100644 index e51981cc..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package netns - -import ( - "runtime" - "sync" - "testing" -) - -func TestGetNewSetDelete(t *testing.T) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - origns, err := Get() - if err != nil { - t.Fatal(err) - } - newns, err := New() - if err != nil { - t.Fatal(err) - } - if origns.Equal(newns) { - t.Fatal("New ns failed") - } - if err := Set(origns); err != nil { - t.Fatal(err) - } - newns.Close() - if newns.IsOpen() { - t.Fatal("newns still open after close", newns) - } - ns, err := Get() - if err != nil { - t.Fatal(err) - } - if !ns.Equal(origns) { - t.Fatal("Reset ns failed", origns, newns, ns) - } -} - -func TestNone(t *testing.T) { - ns := None() - if ns.IsOpen() { - t.Fatal("None ns is open", ns) - } -} - -func TestThreaded(t *testing.T) { - ncpu := runtime.GOMAXPROCS(-1) - if ncpu < 2 { - t.Skip("-cpu=2 or larger required") - } - - // Lock this thread simply to ensure other threads get used. - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - wg := &sync.WaitGroup{} - for i := 0; i < ncpu; i++ { - wg.Add(1) - go func() { - defer wg.Done() - TestGetNewSetDelete(t) - }() - } - wg.Wait() -} diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_unspecified.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_unspecified.go deleted file mode 100644 index b2edc565..00000000 --- a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_unspecified.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build !linux - -package netns - -import ( - "errors" -) - -var ( - ErrNotImplemented = errors.New("not implemented") -) - -func Set(ns NsHandle) (err error) { - return ErrNotImplemented -} - -func New() (ns NsHandle, err error) { - return -1, ErrNotImplemented -} - -func Get() (NsHandle, error) { - return -1, ErrNotImplemented -} - -func GetFromName(name string) (NsHandle, error) { - return -1, ErrNotImplemented -} - -func GetFromPid(pid int) (NsHandle, error) { - return -1, ErrNotImplemented -} - -func GetFromDocker(id string) (NsHandle, error) { - return -1, ErrNotImplemented -} diff --git a/container/docker/factory.go b/container/docker/factory.go index 7dd3b1ed..e449586e 100644 --- a/container/docker/factory.go +++ b/container/docker/factory.go @@ -96,7 +96,7 @@ func (self *dockerFactory) String() string { return DockerNamespace } -func (self *dockerFactory) NewContainerHandler(name string) (handler container.ContainerHandler, err error) { +func (self *dockerFactory) NewContainerHandler(name string, inHostNamespace bool) (handler container.ContainerHandler, err error) { client, err := docker.NewClient(*ArgDockerEndpoint) if err != nil { return @@ -108,6 +108,7 @@ func (self *dockerFactory) NewContainerHandler(name string) (handler container.C self.fsInfo, self.usesAufsDriver, &self.cgroupSubsystems, + inHostNamespace, ) return } diff --git a/container/docker/handler.go b/container/docker/handler.go index e789c5cd..c2ff1af6 100644 --- a/container/docker/handler.go +++ b/container/docker/handler.go @@ -77,6 +77,12 @@ type dockerContainerHandler struct { // Image name used for this container. image string + + // The host root FS to read + rootFs string + + // The network mode of the container + networkMode string } func newDockerContainerHandler( @@ -86,6 +92,7 @@ func newDockerContainerHandler( fsInfo fs.FsInfo, usesAufsDriver bool, cgroupSubsystems *containerLibcontainer.CgroupSubsystems, + inHostNamespace bool, ) (container.ContainerHandler, error) { // Create the cgroup paths. cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints)) @@ -101,6 +108,11 @@ func newDockerContainerHandler( Paths: cgroupPaths, } + rootFs := "/" + if !inHostNamespace { + rootFs = "/rootfs" + } + id := ContainerNameToDockerId(name) handler := &dockerContainerHandler{ id: id, @@ -111,6 +123,7 @@ func newDockerContainerHandler( cgroupManager: cgroupManager, usesAufsDriver: usesAufsDriver, fsInfo: fsInfo, + rootFs: rootFs, } handler.storageDirs = append(handler.storageDirs, path.Join(*dockerRootDir, pathToAufsDir, id)) @@ -127,6 +140,7 @@ func newDockerContainerHandler( handler.aliases = append(handler.aliases, id) handler.labels = ctnr.Config.Labels handler.image = ctnr.Config.Image + handler.networkMode = ctnr.HostConfig.NetworkMode return handler, nil } @@ -175,28 +189,21 @@ func libcontainerConfigToContainerSpec(config *libcontainerConfigs.Config, mi *i } spec.Cpu.Mask = utils.FixCpuMask(config.Cgroups.CpusetCpus, mi.NumCores) - // Docker reports a loop device for containers with --net=host. Ignore - // those too. - networkCount := 0 - for _, n := range config.Networks { - if n.Type != "loopback" { - networkCount += 1 - } - } - - spec.HasNetwork = networkCount > 0 || hasNetNs(config.Namespaces) spec.HasDiskIo = true return spec } -func hasNetNs(namespaces libcontainerConfigs.Namespaces) bool { - for _, ns := range namespaces { - if ns.Type == libcontainerConfigs.NEWNET { - return true - } +var ( + hasNetworkModes = map[string]bool{ + "host": true, + "bridge": true, + "default": true, } - return false +) + +func hasNet(networkMode string) bool { + return hasNetworkModes[networkMode] } func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) { @@ -216,6 +223,7 @@ func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) { } spec.Labels = self.labels spec.Image = self.image + spec.HasNetwork = hasNet(self.networkMode) return spec, err } @@ -265,32 +273,16 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error // TODO(vmarmol): Get from libcontainer API instead of cgroup manager when we don't have to support older Dockers. func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error) { - config, err := self.readLibcontainerConfig() - if err != nil { - return nil, err - } - - var networkInterfaces []string - if len(config.Networks) > 0 { - // ContainerStats only reports stat for one network device. - // TODO(vmarmol): Handle multiple physical network devices. - for _, n := range config.Networks { - // Take the first non-loopback. - if n.Type != "loopback" { - networkInterfaces = []string{n.HostInterfaceName} - break - } - } - } - stats, err := containerLibcontainer.GetStats(self.cgroupManager, networkInterfaces, self.pid) + stats, err := containerLibcontainer.GetStats(self.cgroupManager, self.rootFs, self.pid) if err != nil { return stats, err } - - // TODO(rjnagal): Remove the conversion when network stats are read from libcontainer. - convertInterfaceStats(&stats.Network.InterfaceStats) - for i := range stats.Network.Interfaces { - convertInterfaceStats(&stats.Network.Interfaces[i]) + // Clean up stats for containers that don't have their own network - this + // includes containers running in Kubernetes pods that use the network of the + // infrastructure container. This stops metrics being reported multiple times + // for each container in a pod. + if !hasNet(self.networkMode) { + stats.Network = info.NetworkStats{} } // Get filesystem stats. @@ -302,21 +294,6 @@ func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error) { return stats, nil } -func convertInterfaceStats(stats *info.InterfaceStats) { - net := *stats - - // Ingress for host veth is from the container. - // Hence tx_bytes stat on the host veth is actually number of bytes received by the container. - stats.RxBytes = net.TxBytes - stats.RxPackets = net.TxPackets - stats.RxErrors = net.TxErrors - stats.RxDropped = net.TxDropped - stats.TxBytes = net.RxBytes - stats.TxPackets = net.RxPackets - stats.TxErrors = net.RxErrors - stats.TxDropped = net.RxDropped -} - func (self *dockerContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { if self.name != "/docker" { return []info.ContainerReference{}, nil diff --git a/container/factory.go b/container/factory.go index d5ab8290..9a120f7c 100644 --- a/container/factory.go +++ b/container/factory.go @@ -23,7 +23,7 @@ import ( type ContainerHandlerFactory interface { // Create a new ContainerHandler using this factory. CanHandleAndAccept() must have returned true. - NewContainerHandler(name string) (c ContainerHandler, err error) + NewContainerHandler(name string, inHostNamespace bool) (c ContainerHandler, err error) // Returns whether this factory can handle and accept the specified container. CanHandleAndAccept(name string) (handle bool, accept bool, err error) @@ -60,7 +60,7 @@ func HasFactories() bool { } // Create a new ContainerHandler for the specified container. -func NewContainerHandler(name string) (ContainerHandler, bool, error) { +func NewContainerHandler(name string, inHostNamespace bool) (ContainerHandler, bool, error) { factoriesLock.RLock() defer factoriesLock.RUnlock() @@ -76,7 +76,7 @@ func NewContainerHandler(name string) (ContainerHandler, bool, error) { return nil, false, nil } glog.V(3).Infof("Using factory %q for container %q", factory, name) - handle, err := factory.NewContainerHandler(name) + handle, err := factory.NewContainerHandler(name, inHostNamespace) return handle, canAccept, err } else { glog.V(4).Infof("Factory %q was unable to handle container %q", factory, name) diff --git a/container/factory_test.go b/container/factory_test.go index 991c365a..8c4bb042 100644 --- a/container/factory_test.go +++ b/container/factory_test.go @@ -39,7 +39,7 @@ func (self *mockContainerHandlerFactory) CanHandleAndAccept(name string) (bool, return self.CanHandleValue, self.CanAcceptValue, nil } -func (self *mockContainerHandlerFactory) NewContainerHandler(name string) (ContainerHandler, error) { +func (self *mockContainerHandlerFactory) NewContainerHandler(name string, isHostNamespace bool) (ContainerHandler, error) { args := self.Called(name) return args.Get(0).(ContainerHandler), args.Error(1) } @@ -60,13 +60,13 @@ func TestNewContainerHandler_FirstMatches(t *testing.T) { RegisterContainerHandlerFactory(allwaysYes) // The yes factory should be asked to create the ContainerHandler. - mockContainer, err := mockFactory.NewContainerHandler(testContainerName) + mockContainer, err := mockFactory.NewContainerHandler(testContainerName, true) if err != nil { t.Error(err) } allwaysYes.On("NewContainerHandler", testContainerName).Return(mockContainer, nil) - cont, _, err := NewContainerHandler(testContainerName) + cont, _, err := NewContainerHandler(testContainerName, true) if err != nil { t.Error(err) } @@ -93,13 +93,13 @@ func TestNewContainerHandler_SecondMatches(t *testing.T) { RegisterContainerHandlerFactory(allwaysYes) // The yes factory should be asked to create the ContainerHandler. - mockContainer, err := mockFactory.NewContainerHandler(testContainerName) + mockContainer, err := mockFactory.NewContainerHandler(testContainerName, true) if err != nil { t.Error(err) } allwaysYes.On("NewContainerHandler", testContainerName).Return(mockContainer, nil) - cont, _, err := NewContainerHandler(testContainerName) + cont, _, err := NewContainerHandler(testContainerName, true) if err != nil { t.Error(err) } @@ -125,7 +125,7 @@ func TestNewContainerHandler_NoneMatch(t *testing.T) { } RegisterContainerHandlerFactory(allwaysNo2) - _, _, err := NewContainerHandler(testContainerName) + _, _, err := NewContainerHandler(testContainerName, true) if err == nil { t.Error("Expected NewContainerHandler to fail") } @@ -148,7 +148,7 @@ func TestNewContainerHandler_Accept(t *testing.T) { } RegisterContainerHandlerFactory(cannotAccept) - _, accept, err := NewContainerHandler(testContainerName) + _, accept, err := NewContainerHandler(testContainerName, true) if err != nil { t.Error("Expected NewContainerHandler to succeed") } diff --git a/container/libcontainer/helpers.go b/container/libcontainer/helpers.go index e9c8c615..a3b2e50a 100644 --- a/container/libcontainer/helpers.go +++ b/container/libcontainer/helpers.go @@ -17,11 +17,10 @@ package libcontainer import ( "bufio" "fmt" - "net" - "os/exec" + "io/ioutil" "path" "regexp" - "runtime" + "strconv" "strings" "time" @@ -29,8 +28,6 @@ import ( "github.com/docker/libcontainer/cgroups" "github.com/golang/glog" info "github.com/google/cadvisor/info/v1" - "github.com/google/cadvisor/utils/sysinfo" - "github.com/vishvananda/netns" ) type CgroupSubsystems struct { @@ -82,7 +79,7 @@ var supportedSubsystems map[string]struct{} = map[string]struct{}{ } // Get cgroup and networking stats of the specified container -func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string, pid int) (*info.ContainerStats, error) { +func GetStats(cgroupManager cgroups.Manager, rootFs string, pid int) (*info.ContainerStats, error) { cgroupStats, err := cgroupManager.GetStats() if err != nil { return nil, err @@ -92,24 +89,13 @@ func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string, pid int } stats := toContainerStats(libcontainerStats) - // TODO(rjnagal): Use networking stats directly from libcontainer. - stats.Network.Interfaces = make([]info.InterfaceStats, len(networkInterfaces)) - for i := range networkInterfaces { - interfaceStats, err := sysinfo.GetNetworkStats(networkInterfaces[i]) - if err != nil { - return stats, err - } - stats.Network.Interfaces[i] = interfaceStats - } - - // If we know the pid & we haven't discovered any network interfaces yet - // try the network namespace. - if pid > 0 && len(stats.Network.Interfaces) == 0 { - nsStats, err := networkStatsFromNs(pid) + // If we know the pid then get network stats from /proc//net/dev + if pid > 0 { + netStats, err := networkStatsFromProc(rootFs, pid) if err != nil { glog.V(2).Infof("Unable to get network stats from pid %d: %v", pid, err) } else { - stats.Network.Interfaces = append(stats.Network.Interfaces, nsStats...) + stats.Network.Interfaces = append(stats.Network.Interfaces, netStats...) } } @@ -121,70 +107,39 @@ func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string, pid int return stats, nil } -func networkStatsFromNs(pid int) ([]info.InterfaceStats, error) { - // Lock the OS Thread so we only change the ns for this thread exclusively - runtime.LockOSThread() - defer runtime.UnlockOSThread() +func networkStatsFromProc(rootFs string, pid int) ([]info.InterfaceStats, error) { + netStatsFile := path.Join(rootFs, "proc", strconv.Itoa(pid), "/net/dev") - stats := []info.InterfaceStats{} - - // Save the current network namespace - origns, _ := netns.Get() - defer origns.Close() - - // Switch to the pid netns - pidns, err := netns.GetFromPid(pid) - defer pidns.Close() + ifaceStats, err := scanInterfaceStats(netStatsFile) if err != nil { - return stats, nil - } - netns.Set(pidns) - - // Defer setting back to original ns - defer netns.Set(origns) - - ifaceStats, err := scanInterfaceStats() - if err != nil { - return stats, fmt.Errorf("couldn't read network stats: %v", err) + return []info.InterfaceStats{}, fmt.Errorf("couldn't read network stats: %v", err) } - ifaces, err := net.Interfaces() - if err != nil { - return stats, fmt.Errorf("cannot find interfaces: %v", err) - } - - for _, iface := range ifaces { - if iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagLoopback == 0 { - if s, ok := ifaceStats[iface.Name]; ok { - stats = append(stats, s) - } - } - } - - return stats, nil + return ifaceStats, nil } -// Borrowed from libnetwork Stats - https://github.com/docker/libnetwork/blob/master/sandbox/interface_linux.go -// In older kernels (like the one in Centos 6.6 distro) sysctl does not have netns support. Therefore -// we cannot gather the statistics from /sys/class/net//statistics/ files. Per-netns stats -// are naturally found in /proc/net/dev in kernels which support netns (ifconfig relyes on that). -const ( - netStatsFile = "/proc/net/dev" +var ( + ignoredDevicePrefixes = []string{"lo", "veth", "docker"} + netStatLineRE = regexp.MustCompile("[ ]*(.+):([ ]+[0-9]+){16}") ) -func scanInterfaceStats() (map[string]info.InterfaceStats, error) { - re := regexp.MustCompile("[ ]*(.+):([ ]+[0-9]+){16}") +func isIgnoredDevice(ifName string) bool { + for _, prefix := range ignoredDevicePrefixes { + if strings.HasPrefix(strings.ToLower(ifName), prefix) { + return true + } + } + return false +} +func scanInterfaceStats(netStatsFile string) ([]info.InterfaceStats, error) { var ( bkt uint64 ) - stats := map[string]info.InterfaceStats{} + stats := []info.InterfaceStats{} - // For some reason ioutil.ReadFile(netStatsFile) reads the file in - // the default netns when this code is invoked from docker. - // Executing "cat " works as expected. - data, err := exec.Command("cat", netStatsFile).Output() + data, err := ioutil.ReadFile(netStatsFile) if err != nil { return stats, fmt.Errorf("failure opening %s: %v", netStatsFile, err) } @@ -196,7 +151,7 @@ func scanInterfaceStats() (map[string]info.InterfaceStats, error) { for scanner.Scan() { line := scanner.Text() - if re.MatchString(line) { + if netStatLineRE.MatchString(line) { line = strings.Replace(line, ":", "", -1) i := info.InterfaceStats{} @@ -209,7 +164,9 @@ func scanInterfaceStats() (map[string]info.InterfaceStats, error) { return stats, fmt.Errorf("failure opening %s: %v", netStatsFile, err) } - stats[i.Name] = i + if !isIgnoredDevice(i.Name) { + stats = append(stats, i) + } } } diff --git a/container/mock.go b/container/mock.go index 7422b3dd..f949c579 100644 --- a/container/mock.go +++ b/container/mock.go @@ -109,7 +109,7 @@ func (self *FactoryForMockContainerHandler) String() string { return self.Name } -func (self *FactoryForMockContainerHandler) NewContainerHandler(name string) (ContainerHandler, error) { +func (self *FactoryForMockContainerHandler) NewContainerHandler(name string, inHostNamespace bool) (ContainerHandler, error) { handler := &MockContainerHandler{} if self.PrepareContainerHandlerFunc != nil { self.PrepareContainerHandlerFunc(name, handler) diff --git a/container/raw/factory.go b/container/raw/factory.go index be1c799b..5b45ee92 100644 --- a/container/raw/factory.go +++ b/container/raw/factory.go @@ -45,8 +45,12 @@ func (self *rawFactory) String() string { return "raw" } -func (self *rawFactory) NewContainerHandler(name string) (container.ContainerHandler, error) { - return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, self.watcher) +func (self *rawFactory) NewContainerHandler(name string, inHostNamespace bool) (container.ContainerHandler, error) { + rootFs := "/" + if !inHostNamespace { + rootFs = "/rootfs" + } + return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, self.watcher, rootFs) } // The raw factory can handle any container. If --docker_only is set to false, non-docker containers are ignored. diff --git a/container/raw/handler.go b/container/raw/handler.go index 64392208..8223b6e5 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -61,9 +61,11 @@ type rawContainerHandler struct { fsInfo fs.FsInfo externalMounts []mount + + rootFs string } -func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *InotifyWatcher) (container.ContainerHandler, error) { +func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *InotifyWatcher, rootFs string) (container.ContainerHandler, error) { // Create the cgroup paths. cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints)) for key, val := range cgroupSubsystems.MountPoints { @@ -108,6 +110,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSu hasNetwork: hasNetwork, externalMounts: externalMounts, watcher: watcher, + rootFs: rootFs, }, nil } @@ -326,15 +329,7 @@ func (self *rawContainerHandler) getFsStats(stats *info.ContainerStats) error { } func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { - nd, err := self.GetRootNetworkDevices() - if err != nil { - return new(info.ContainerStats), err - } - networkInterfaces := make([]string, len(nd)) - for i := range nd { - networkInterfaces[i] = nd[i].Name - } - stats, err := libcontainer.GetStats(self.cgroupManager, networkInterfaces, -1) + stats, err := libcontainer.GetStats(self.cgroupManager, self.rootFs, os.Getpid()) if err != nil { return stats, err } diff --git a/manager/manager.go b/manager/manager.go index 9f121c6a..8526bc45 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -737,7 +737,7 @@ func (m *manager) registerCollectors(collectorConfigs map[string]string, cont *c // Create a container. func (m *manager) createContainer(containerName string) error { - handler, accept, err := container.NewContainerHandler(containerName) + handler, accept, err := container.NewContainerHandler(containerName, m.inHostNamespace) if err != nil { return err }