diff --git a/events.go b/events.go index cc12eaef69a..05c7cb712a2 100644 --- a/events.go +++ b/events.go @@ -11,6 +11,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/opencontainers/runc/libcontainer" + "github.com/opencontainers/runc/libcontainer/cgroups" ) // event struct for encoding the event data to json. @@ -20,9 +21,82 @@ type event struct { Data interface{} `json:"data,omitempty"` } +// stats is the runc specific stats structure for stability when encoding and decoding stats. +type stats struct { + Cpu cpu `json:"cpu"` + Memory memory `json:"memory"` + Pids pids `json:"pids"` + Blkio blkio `json:"blkio"` + Hugetlb map[string]hugetlb `json:"hugetlb"` +} + +type hugetlb struct { + Usage uint64 `json:"usage,omitempty"` + Max uint64 `json:"max,omitempty"` + Failcnt uint64 `json:"failcnt"` +} + +type blkioEntry struct { + Major uint64 `json:"major,omitempty"` + Minor uint64 `json:"minor,omitempty"` + Op string `json:"op,omitempty"` + Value uint64 `json:"value,omitempty"` +} + +type blkio struct { + IoServiceBytesRecursive []blkioEntry `json:"ioServiceBytesRecursive,omitempty"` + IoServicedRecursive []blkioEntry `json:"ioServicedRecursive,omitempty"` + IoQueuedRecursive []blkioEntry `json:"ioQueueRecursive,omitempty"` + IoServiceTimeRecursive []blkioEntry `json:"ioServiceTimeRecursive,omitempty"` + IoWaitTimeRecursive []blkioEntry `json:"ioWaitTimeRecursive,omitempty"` + IoMergedRecursive []blkioEntry `json:"ioMergedRecursive,omitempty"` + IoTimeRecursive []blkioEntry `json:"ioTimeRecursive,omitempty"` + SectorsRecursive []blkioEntry `json:"sectorsRecursive,omitempty"` +} + +type pids struct { + Current uint64 `json:"current,omitempty"` + Limit uint64 `json:"limit,omitempty"` +} + +type throttling struct { + Periods uint64 `json:"periods,omitempty"` + ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"` + ThrottledTime uint64 `json:"throttledTime,omitempty"` +} + +type cpuUsage struct { + // Units: nanoseconds. + Total uint64 `json:"total,omitempty"` + Percpu []uint64 `json:"percpu,omitempty"` + Kernel uint64 `json:"kernel"` + User uint64 `json:"user"` +} + +type cpu struct { + Usage cpuUsage `json:"usage,omitempty"` + Throttling throttling `json:"throttling,omitempty"` +} + +type memoryEntry struct { + Limit uint64 `json:"limit"` + Usage uint64 `json:"usage,omitempty"` + Max uint64 `json:"max,omitempty"` + Failcnt uint64 `json:"failcnt"` +} + +type memory struct { + Cache uint64 `json:"cache,omitempty"` + Usage memoryEntry `json:"usage,omitempty"` + Swap memoryEntry `json:"swap,omitempty"` + Kernel memoryEntry `json:"kernel,omitempty"` + KernelTCP memoryEntry `json:"kernelTCP,omitempty"` + Raw map[string]uint64 `json:"raw,omitempty"` +} + var eventsCommand = cli.Command{ Name: "events", - Usage: "display container events such as OOM notifications, cpu, memory, IO and network stats", + Usage: "display container events such as OOM notifications, cpu, memory, and IO usage statistics", ArgsUsage: ` Where "" is the name for the instance of the container.`, @@ -64,7 +138,7 @@ information is displayed once every 5 seconds.`, if err != nil { fatal(err) } - events <- &event{Type: "stats", ID: container.ID(), Data: s} + events <- &event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)} close(events) group.Wait() return @@ -95,7 +169,7 @@ information is displayed once every 5 seconds.`, n = nil } case s := <-stats: - events <- &event{Type: "stats", ID: container.ID(), Data: s} + events <- &event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)} } if n == nil { close(events) @@ -105,3 +179,73 @@ information is displayed once every 5 seconds.`, group.Wait() }, } + +func convertLibcontainerStats(ls *libcontainer.Stats) *stats { + cg := ls.CgroupStats + if cg == nil { + return nil + } + var s stats + 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 + 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) + + s.Hugetlb = make(map[string]hugetlb) + for k, v := range cg.HugetlbStats { + s.Hugetlb[k] = convertHugtlb(v) + } + return &s +} + +func convertHugtlb(c cgroups.HugetlbStats) hugetlb { + return hugetlb{ + Usage: c.Usage, + Max: c.MaxUsage, + Failcnt: c.Failcnt, + } +} + +func convertMemoryEntry(c cgroups.MemoryData) memoryEntry { + return memoryEntry{ + Limit: c.Limit, + Usage: c.Usage, + Max: c.MaxUsage, + Failcnt: c.Failcnt, + } +} + +func convertBlkioEntry(c []cgroups.BlkioStatEntry) []blkioEntry { + var out []blkioEntry + for _, e := range c { + out = append(out, blkioEntry{ + Major: e.Major, + Minor: e.Minor, + Op: e.Op, + Value: e.Value, + }) + } + return out +}