diff --git a/manager/container.go b/manager/container.go index 98a46b7b..135ff30b 100644 --- a/manager/container.go +++ b/manager/container.go @@ -44,7 +44,7 @@ type containerData struct { info containerInfo storageDriver storage.StorageDriver lock sync.Mutex - loadReader *cpuload.CpuLoadReader + loadReader cpuload.CpuLoadReader housekeepingInterval time.Duration lastUpdatedTime time.Time lastErrorTime time.Time @@ -93,7 +93,7 @@ func (c *containerData) GetInfo() (*containerInfo, error) { return &c.info, nil } -func newContainerData(containerName string, driver storage.StorageDriver, handler container.ContainerHandler, loadReader *cpuload.CpuLoadReader, logUsage bool) (*containerData, error) { +func newContainerData(containerName string, driver storage.StorageDriver, handler container.ContainerHandler, loadReader cpuload.CpuLoadReader, logUsage bool) (*containerData, error) { if driver == nil { return nil, fmt.Errorf("nil storage driver") } diff --git a/manager/manager.go b/manager/manager.go index fbaaaaec..4644d1ad 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -120,7 +120,7 @@ type manager struct { quitChannels []chan error cadvisorContainer string dockerContainersRegexp *regexp.Regexp - loadReader *cpuload.CpuLoadReader + loadReader cpuload.CpuLoadReader } // Start the container manager. @@ -129,9 +129,14 @@ func (self *manager) Start() error { cpuLoadReader, err := cpuload.New() if err != nil { // TODO(rjnagal): Promote to warning once we support cpu load inside namespaces. - glog.Infof("could not initialize cpu load reader: %s", err) + glog.Infof("Could not initialize cpu load reader: %s", err) } else { - self.loadReader = cpuLoadReader + err = cpuLoadReader.Start() + if err != nil { + glog.Warning("Could not start cpu load stat collector: %s", err) + } else { + self.loadReader = cpuLoadReader + } } // Create root and then recover all containers. @@ -176,7 +181,7 @@ func (self *manager) Stop() error { } self.quitChannels = make([]chan error, 0, 2) if self.loadReader != nil { - self.loadReader.Close() + self.loadReader.Stop() self.loadReader = nil } return nil diff --git a/utils/cpuload/cpuload.go b/utils/cpuload/cpuload.go index df9eabc7..d077f757 100644 --- a/utils/cpuload/cpuload.go +++ b/utils/cpuload/cpuload.go @@ -16,58 +16,27 @@ package cpuload import ( "fmt" - "os" - "github.com/golang/glog" "github.com/google/cadvisor/info" + "github.com/google/cadvisor/utils/cpuload/netlink" ) -type CpuLoadReader struct { - familyId uint16 - conn *Connection +type CpuLoadReader interface { + // Start the reader. + Start() error + + // Stop the reader and clean up internal state. + Stop() + + // Retrieve Cpu load for a given group. + // Path is an absolute filesystem path for a container under CPU cgroup hierarchy. + GetCpuLoad(path string) (info.LoadStats, error) } -func New() (*CpuLoadReader, error) { - conn, err := newConnection() +func New() (CpuLoadReader, error) { + reader, err := netlink.New() if err != nil { - return nil, fmt.Errorf("failed to create a new connection: %s", err) + return nil, fmt.Errorf("failed to create a netlink-based cpu load reader: %s", err) } - - id, err := getFamilyId(conn) - if err != nil { - return nil, fmt.Errorf("failed to get netlink family id for task stats: %s", err) - } - glog.V(2).Infof("Family id for taskstats: %d", id) - return &CpuLoadReader{ - familyId: id, - conn: conn, - }, nil -} - -func (self *CpuLoadReader) Close() { - if self.conn != nil { - self.conn.Close() - } -} - -// Returns instantaneous number of running tasks in a group. -// Caller can use historical data to calculate cpu load. -// path is an absolute filesystem path for a container under the CPU cgroup hierarchy. -// NOTE: non-hierarchical load is returned. It does not include load for subcontainers. -func (self *CpuLoadReader) GetCpuLoad(path string) (info.LoadStats, error) { - if len(path) == 0 { - return info.LoadStats{}, fmt.Errorf("cgroup path can not be empty!") - } - - cfd, err := os.Open(path) - if err != nil { - return info.LoadStats{}, fmt.Errorf("failed to open cgroup path %s: %q", path, err) - } - - stats, err := getLoadStats(self.familyId, cfd.Fd(), self.conn) - if err != nil { - return info.LoadStats{}, err - } - glog.V(1).Infof("Task stats for %q: %+v", path, stats) - return stats, nil + return reader, nil } diff --git a/utils/cpuload/conn.go b/utils/cpuload/netlink/conn.go similarity index 99% rename from utils/cpuload/conn.go rename to utils/cpuload/netlink/conn.go index cc42dfe1..7eb2204d 100644 --- a/utils/cpuload/conn.go +++ b/utils/cpuload/netlink/conn.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cpuload +package netlink import ( "bufio" diff --git a/utils/cpuload/defs.go b/utils/cpuload/netlink/defs.go similarity index 97% rename from utils/cpuload/defs.go rename to utils/cpuload/netlink/defs.go index 3aa25d93..a45d8703 100644 --- a/utils/cpuload/defs.go +++ b/utils/cpuload/netlink/defs.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cpuload +package netlink /* #include diff --git a/utils/cpuload/example/example.go b/utils/cpuload/netlink/example/example.go similarity index 88% rename from utils/cpuload/example/example.go rename to utils/cpuload/netlink/example/example.go index d07a307e..289e9f69 100644 --- a/utils/cpuload/example/example.go +++ b/utils/cpuload/netlink/example/example.go @@ -17,20 +17,20 @@ package main import ( "log" - "github.com/google/cadvisor/utils/cpuload" + "github.com/google/cadvisor/utils/cpuload/netlink" ) func main() { - c, err := cpuload.New() + n, err := netlink.New() if err != nil { log.Printf("Failed to create cpu load util: %s", err) return } - defer c.Close() + defer n.Stop() paths := []string{"/sys/fs/cgroup/cpu", "/sys/fs/cgroup/cpu/docker"} for _, path := range paths { - stats, err := c.GetCpuLoad(path) + stats, err := n.GetCpuLoad(path) if err != nil { log.Printf("Error getting cpu load for %q: %s", path, err) } diff --git a/utils/cpuload/netlink.go b/utils/cpuload/netlink/netlink.go similarity index 99% rename from utils/cpuload/netlink.go rename to utils/cpuload/netlink/netlink.go index 0353ee2b..0f5f1c51 100644 --- a/utils/cpuload/netlink.go +++ b/utils/cpuload/netlink/netlink.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cpuload +package netlink import ( "bytes" diff --git a/utils/cpuload/netlink/reader.go b/utils/cpuload/netlink/reader.go new file mode 100644 index 00000000..b37fc360 --- /dev/null +++ b/utils/cpuload/netlink/reader.go @@ -0,0 +1,78 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package netlink + +import ( + "fmt" + "os" + + "github.com/golang/glog" + "github.com/google/cadvisor/info" +) + +type NetlinkReader struct { + familyId uint16 + conn *Connection +} + +func New() (*NetlinkReader, error) { + conn, err := newConnection() + if err != nil { + return nil, fmt.Errorf("failed to create a new connection: %s", err) + } + + id, err := getFamilyId(conn) + if err != nil { + return nil, fmt.Errorf("failed to get netlink family id for task stats: %s", err) + } + glog.V(2).Infof("Family id for taskstats: %d", id) + return &NetlinkReader{ + familyId: id, + conn: conn, + }, nil +} + +func (self *NetlinkReader) Stop() { + if self.conn != nil { + self.conn.Close() + } +} + +func (self *NetlinkReader) Start() error { + // We do the start setup for netlink in New(). Nothing to do here. + return nil +} + +// Returns instantaneous number of running tasks in a group. +// Caller can use historical data to calculate cpu load. +// path is an absolute filesystem path for a container under the CPU cgroup hierarchy. +// NOTE: non-hierarchical load is returned. It does not include load for subcontainers. +func (self *NetlinkReader) GetCpuLoad(path string) (info.LoadStats, error) { + if len(path) == 0 { + return info.LoadStats{}, fmt.Errorf("cgroup path can not be empty!") + } + + cfd, err := os.Open(path) + if err != nil { + return info.LoadStats{}, fmt.Errorf("failed to open cgroup path %s: %q", path, err) + } + + stats, err := getLoadStats(self.familyId, cfd.Fd(), self.conn) + if err != nil { + return info.LoadStats{}, err + } + glog.V(1).Infof("Task stats for %q: %+v", path, stats) + return stats, nil +}