cadvisor/container/lmctfy/lmctfy_container.go
2014-06-09 12:12:07 -07:00

196 lines
5.3 KiB
Go

// 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.
// A container object
package lmctfy
import (
"fmt"
"os/exec"
"strings"
"syscall"
"time"
"code.google.com/p/goprotobuf/proto"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/info"
)
type lmctfyContainerHandler struct {
// Container name
Name string
container.NoStatsSummary
}
const (
lmctfyBinary = "lmctfy"
notFoundExitCode = 5
)
// Create a new
func New(name string) (container.ContainerHandler, error) {
el := &lmctfyContainerHandler{
Name: name,
}
return el, nil
}
func getExitCode(err error) int {
msg, ok := err.(*exec.ExitError)
if ok {
return msg.Sys().(syscall.WaitStatus).ExitStatus()
}
return -1
}
func protobufToContainerSpec(pspec *ContainerSpec) *info.ContainerSpec {
ret := new(info.ContainerSpec)
if pspec.GetCpu() != nil {
cpuspec := new(info.CpuSpec)
cpuspec.Limit = pspec.GetCpu().GetLimit()
cpuspec.MaxLimit = pspec.GetCpu().GetMaxLimit()
if pspec.GetCpu().GetMask() != nil {
cpuspec.Mask.Data = pspec.GetCpu().GetMask().GetData()
}
ret.Cpu = cpuspec
}
if pspec.GetMemory() != nil {
pmem := pspec.GetMemory()
memspec := new(info.MemorySpec)
memspec.Limit = uint64(pmem.GetLimit())
memspec.Reservation = uint64(pmem.GetReservation())
memspec.SwapLimit = uint64(pmem.GetSwapLimit())
ret.Memory = memspec
}
return ret
}
// Gets spec.
func (c *lmctfyContainerHandler) GetSpec() (*info.ContainerSpec, error) {
// Run lmctfy spec "container_name" and get spec.
// Ignore if the container was not found.
cmd := exec.Command(lmctfyBinary, "spec", string(c.Name))
data, err := cmd.Output()
if err != nil && getExitCode(err) != notFoundExitCode {
return nil, fmt.Errorf("unable to run command %v spec %v: %v", lmctfyBinary, c.Name, err)
}
// Parse output into a protobuf.
pspec := &ContainerSpec{}
err = proto.UnmarshalText(string(data), pspec)
if err != nil {
return nil, err
}
spec := protobufToContainerSpec(pspec)
return spec, nil
}
func protobufToMemoryData(pmd *MemoryStats_MemoryData, data *info.MemoryStatsMemoryData) {
if pmd == nil {
return
}
data.Pgfault = uint64(pmd.GetPgfault())
data.Pgmajfault = uint64(pmd.GetPgmajfault())
return
}
func protobufToContainerStats(pstats *ContainerStats) *info.ContainerStats {
ret := new(info.ContainerStats)
if pstats.GetCpu() != nil {
pcpu := pstats.GetCpu()
cpustats := new(info.CpuStats)
cpustats.Usage.Total = pcpu.GetUsage().GetTotal()
percpu := pcpu.GetUsage().GetPerCpu()
if len(percpu) > 0 {
cpustats.Usage.PerCpu = make([]uint64, len(percpu))
for i, p := range percpu {
cpustats.Usage.PerCpu[i] = uint64(p)
}
}
cpustats.Usage.User = uint64(pcpu.GetUsage().GetUser())
cpustats.Usage.System = uint64(pcpu.GetUsage().GetSystem())
cpustats.Load = pcpu.GetLoad()
ret.Cpu = cpustats
}
if pstats.GetMemory() != nil {
pmem := pstats.GetMemory()
memstats := new(info.MemoryStats)
memstats.Limit = uint64(pmem.GetLimit())
memstats.Usage = uint64(pmem.GetUsage())
protobufToMemoryData(pmem.GetContainerData(), &memstats.ContainerData)
protobufToMemoryData(pmem.GetHierarchicalData(), &memstats.HierarchicalData)
ret.Memory = memstats
}
return ret
}
// Gets full stats.
func (c *lmctfyContainerHandler) GetStats() (*info.ContainerStats, error) {
// Ignore if the container was not found.
cmd := exec.Command(lmctfyBinary, "stats", "full", string(c.Name))
data, err := cmd.Output()
if err != nil && getExitCode(err) != notFoundExitCode {
return nil, fmt.Errorf("unable to run command %v stats full %v: %v", lmctfyBinary, c.Name, err)
}
// Parse output into a protobuf.
pstats := &ContainerStats{}
err = proto.UnmarshalText(string(data), pstats)
if err != nil {
return nil, err
}
stats := protobufToContainerStats(pstats)
stats.Timestamp = time.Now()
return stats, nil
}
// Gets all subcontainers.
func (c *lmctfyContainerHandler) ListContainers(listType container.ListType) ([]string, error) {
// Prepare the arguments.
args := []string{"list", "containers", "-v"}
if listType == container.LIST_RECURSIVE {
args = append(args, "-r")
}
args = append(args, c.Name)
// Run the command.
cmd := exec.Command(lmctfyBinary, args...)
data, err := cmd.Output()
if err != nil && getExitCode(err) != notFoundExitCode {
return nil, err
}
// Parse lines as container names.
if len(data) == 0 {
return []string{}, nil
}
names := strings.Split(string(data), "\n")
containerNames := make([]string, 0, len(names))
for _, name := range names {
if len(name) != 0 {
containerNames = append(containerNames, name)
}
}
return containerNames, nil
}
// TODO(vmarmol): Implement
func (c *lmctfyContainerHandler) ListThreads(listType container.ListType) ([]int, error) {
return []int{}, nil
}
func (c *lmctfyContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
return []int{}, nil
}