2019-01-12 04:58:27 +00:00
|
|
|
// +build linux
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2020-08-10 17:43:49 +00:00
|
|
|
"errors"
|
2019-01-12 04:58:27 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/intelrdt"
|
2020-05-04 20:46:48 +00:00
|
|
|
"github.com/opencontainers/runc/types"
|
2019-01-12 04:58:27 +00:00
|
|
|
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/urfave/cli"
|
|
|
|
)
|
|
|
|
|
|
|
|
var eventsCommand = cli.Command{
|
|
|
|
Name: "events",
|
|
|
|
Usage: "display container events such as OOM notifications, cpu, memory, and IO usage statistics",
|
|
|
|
ArgsUsage: `<container-id>
|
|
|
|
|
|
|
|
Where "<container-id>" is the name for the instance of the container.`,
|
|
|
|
Description: `The events command displays information about the container. By default the
|
|
|
|
information is displayed once every 5 seconds.`,
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
cli.DurationFlag{Name: "interval", Value: 5 * time.Second, Usage: "set the stats collection interval"},
|
|
|
|
cli.BoolFlag{Name: "stats", Usage: "display the container's stats then exit"},
|
|
|
|
},
|
|
|
|
Action: func(context *cli.Context) error {
|
|
|
|
if err := checkArgs(context, 1, exactArgs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
container, err := getContainer(context)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
duration := context.Duration("interval")
|
|
|
|
if duration <= 0 {
|
2020-08-10 17:43:49 +00:00
|
|
|
return errors.New("duration interval must be greater than 0")
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
status, err := container.Status()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if status == libcontainer.Stopped {
|
|
|
|
return fmt.Errorf("container with id %s is not running", container.ID())
|
|
|
|
}
|
|
|
|
var (
|
|
|
|
stats = make(chan *libcontainer.Stats, 1)
|
2020-05-04 20:46:48 +00:00
|
|
|
events = make(chan *types.Event, 1024)
|
2019-01-12 04:58:27 +00:00
|
|
|
group = &sync.WaitGroup{}
|
|
|
|
)
|
|
|
|
group.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer group.Done()
|
|
|
|
enc := json.NewEncoder(os.Stdout)
|
|
|
|
for e := range events {
|
|
|
|
if err := enc.Encode(e); err != nil {
|
|
|
|
logrus.Error(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if context.Bool("stats") {
|
|
|
|
s, err := container.Stats()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-04 20:46:48 +00:00
|
|
|
events <- &types.Event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)}
|
2019-01-12 04:58:27 +00:00
|
|
|
close(events)
|
|
|
|
group.Wait()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
for range time.Tick(context.Duration("interval")) {
|
|
|
|
s, err := container.Stats()
|
|
|
|
if err != nil {
|
|
|
|
logrus.Error(err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
stats <- s
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
n, err := container.NotifyOOM()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case _, ok := <-n:
|
|
|
|
if ok {
|
|
|
|
// this means an oom event was received, if it is !ok then
|
|
|
|
// the channel was closed because the container stopped and
|
|
|
|
// the cgroups no longer exist.
|
2020-05-04 20:46:48 +00:00
|
|
|
events <- &types.Event{Type: "oom", ID: container.ID()}
|
2019-01-12 04:58:27 +00:00
|
|
|
} else {
|
|
|
|
n = nil
|
|
|
|
}
|
|
|
|
case s := <-stats:
|
2020-05-04 20:46:48 +00:00
|
|
|
events <- &types.Event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)}
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
if n == nil {
|
|
|
|
close(events)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
group.Wait()
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:46:48 +00:00
|
|
|
func convertLibcontainerStats(ls *libcontainer.Stats) *types.Stats {
|
2019-01-12 04:58:27 +00:00
|
|
|
cg := ls.CgroupStats
|
|
|
|
if cg == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2020-05-04 20:46:48 +00:00
|
|
|
var s types.Stats
|
2019-01-12 04:58:27 +00:00
|
|
|
s.Pids.Current = cg.PidsStats.Current
|
|
|
|
s.Pids.Limit = cg.PidsStats.Limit
|
|
|
|
|
|
|
|
s.CPU.Usage.Kernel = cg.CpuStats.CpuUsage.UsageInKernelmode
|
|
|
|
s.CPU.Usage.User = cg.CpuStats.CpuUsage.UsageInUsermode
|
|
|
|
s.CPU.Usage.Total = cg.CpuStats.CpuUsage.TotalUsage
|
|
|
|
s.CPU.Usage.Percpu = cg.CpuStats.CpuUsage.PercpuUsage
|
2020-08-10 17:43:49 +00:00
|
|
|
s.CPU.Usage.PercpuKernel = cg.CpuStats.CpuUsage.PercpuUsageInKernelmode
|
|
|
|
s.CPU.Usage.PercpuUser = cg.CpuStats.CpuUsage.PercpuUsageInUsermode
|
2019-01-12 04:58:27 +00:00
|
|
|
s.CPU.Throttling.Periods = cg.CpuStats.ThrottlingData.Periods
|
|
|
|
s.CPU.Throttling.ThrottledPeriods = cg.CpuStats.ThrottlingData.ThrottledPeriods
|
|
|
|
s.CPU.Throttling.ThrottledTime = cg.CpuStats.ThrottlingData.ThrottledTime
|
|
|
|
|
|
|
|
s.Memory.Cache = cg.MemoryStats.Cache
|
|
|
|
s.Memory.Kernel = convertMemoryEntry(cg.MemoryStats.KernelUsage)
|
|
|
|
s.Memory.KernelTCP = convertMemoryEntry(cg.MemoryStats.KernelTCPUsage)
|
|
|
|
s.Memory.Swap = convertMemoryEntry(cg.MemoryStats.SwapUsage)
|
|
|
|
s.Memory.Usage = convertMemoryEntry(cg.MemoryStats.Usage)
|
|
|
|
s.Memory.Raw = cg.MemoryStats.Stats
|
|
|
|
|
|
|
|
s.Blkio.IoServiceBytesRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceBytesRecursive)
|
|
|
|
s.Blkio.IoServicedRecursive = convertBlkioEntry(cg.BlkioStats.IoServicedRecursive)
|
|
|
|
s.Blkio.IoQueuedRecursive = convertBlkioEntry(cg.BlkioStats.IoQueuedRecursive)
|
|
|
|
s.Blkio.IoServiceTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceTimeRecursive)
|
|
|
|
s.Blkio.IoWaitTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoWaitTimeRecursive)
|
|
|
|
s.Blkio.IoMergedRecursive = convertBlkioEntry(cg.BlkioStats.IoMergedRecursive)
|
|
|
|
s.Blkio.IoTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoTimeRecursive)
|
|
|
|
s.Blkio.SectorsRecursive = convertBlkioEntry(cg.BlkioStats.SectorsRecursive)
|
|
|
|
|
2020-05-04 20:46:48 +00:00
|
|
|
s.Hugetlb = make(map[string]types.Hugetlb)
|
2019-01-12 04:58:27 +00:00
|
|
|
for k, v := range cg.HugetlbStats {
|
|
|
|
s.Hugetlb[k] = convertHugtlb(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
if is := ls.IntelRdtStats; is != nil {
|
|
|
|
if intelrdt.IsCatEnabled() {
|
|
|
|
s.IntelRdt.L3CacheInfo = convertL3CacheInfo(is.L3CacheInfo)
|
|
|
|
s.IntelRdt.L3CacheSchemaRoot = is.L3CacheSchemaRoot
|
|
|
|
s.IntelRdt.L3CacheSchema = is.L3CacheSchema
|
|
|
|
}
|
|
|
|
if intelrdt.IsMbaEnabled() {
|
|
|
|
s.IntelRdt.MemBwInfo = convertMemBwInfo(is.MemBwInfo)
|
|
|
|
s.IntelRdt.MemBwSchemaRoot = is.MemBwSchemaRoot
|
|
|
|
s.IntelRdt.MemBwSchema = is.MemBwSchema
|
|
|
|
}
|
2020-08-10 17:43:49 +00:00
|
|
|
if intelrdt.IsMBMEnabled() {
|
|
|
|
s.IntelRdt.MBMStats = is.MBMStats
|
|
|
|
}
|
|
|
|
if intelrdt.IsCMTEnabled() {
|
|
|
|
s.IntelRdt.CMTStats = is.CMTStats
|
|
|
|
}
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
2020-05-04 20:46:48 +00:00
|
|
|
s.NetworkInterfaces = ls.Interfaces
|
2019-01-12 04:58:27 +00:00
|
|
|
return &s
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:46:48 +00:00
|
|
|
func convertHugtlb(c cgroups.HugetlbStats) types.Hugetlb {
|
|
|
|
return types.Hugetlb{
|
2019-01-12 04:58:27 +00:00
|
|
|
Usage: c.Usage,
|
|
|
|
Max: c.MaxUsage,
|
|
|
|
Failcnt: c.Failcnt,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:46:48 +00:00
|
|
|
func convertMemoryEntry(c cgroups.MemoryData) types.MemoryEntry {
|
|
|
|
return types.MemoryEntry{
|
2019-01-12 04:58:27 +00:00
|
|
|
Limit: c.Limit,
|
|
|
|
Usage: c.Usage,
|
|
|
|
Max: c.MaxUsage,
|
|
|
|
Failcnt: c.Failcnt,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:46:48 +00:00
|
|
|
func convertBlkioEntry(c []cgroups.BlkioStatEntry) []types.BlkioEntry {
|
|
|
|
var out []types.BlkioEntry
|
2019-01-12 04:58:27 +00:00
|
|
|
for _, e := range c {
|
2020-05-04 20:46:48 +00:00
|
|
|
out = append(out, types.BlkioEntry{
|
2019-01-12 04:58:27 +00:00
|
|
|
Major: e.Major,
|
|
|
|
Minor: e.Minor,
|
|
|
|
Op: e.Op,
|
|
|
|
Value: e.Value,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:46:48 +00:00
|
|
|
func convertL3CacheInfo(i *intelrdt.L3CacheInfo) *types.L3CacheInfo {
|
|
|
|
return &types.L3CacheInfo{
|
2019-01-12 04:58:27 +00:00
|
|
|
CbmMask: i.CbmMask,
|
|
|
|
MinCbmBits: i.MinCbmBits,
|
|
|
|
NumClosids: i.NumClosids,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:46:48 +00:00
|
|
|
func convertMemBwInfo(i *intelrdt.MemBwInfo) *types.MemBwInfo {
|
|
|
|
return &types.MemBwInfo{
|
2019-01-12 04:58:27 +00:00
|
|
|
BandwidthGran: i.BandwidthGran,
|
|
|
|
DelayLinear: i.DelayLinear,
|
|
|
|
MinBandwidth: i.MinBandwidth,
|
|
|
|
NumClosids: i.NumClosids,
|
|
|
|
}
|
|
|
|
}
|