196 lines
5.3 KiB
Go
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
|
|
}
|