...
1 package main
2
3 import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "os"
8 "sync"
9 "time"
10
11 "github.com/opencontainers/runc/libcontainer"
12 "github.com/opencontainers/runc/libcontainer/cgroups"
13 "github.com/opencontainers/runc/libcontainer/intelrdt"
14 "github.com/opencontainers/runc/types"
15
16 "github.com/sirupsen/logrus"
17 "github.com/urfave/cli"
18 )
19
20 var eventsCommand = cli.Command{
21 Name: "events",
22 Usage: "display container events such as OOM notifications, cpu, memory, and IO usage statistics",
23 ArgsUsage: `<container-id>
24
25 Where "<container-id>" is the name for the instance of the container.`,
26 Description: `The events command displays information about the container. By default the
27 information is displayed once every 5 seconds.`,
28 Flags: []cli.Flag{
29 cli.DurationFlag{Name: "interval", Value: 5 * time.Second, Usage: "set the stats collection interval"},
30 cli.BoolFlag{Name: "stats", Usage: "display the container's stats then exit"},
31 },
32 Action: func(context *cli.Context) error {
33 if err := checkArgs(context, 1, exactArgs); err != nil {
34 return err
35 }
36 container, err := getContainer(context)
37 if err != nil {
38 return err
39 }
40 duration := context.Duration("interval")
41 if duration <= 0 {
42 return errors.New("duration interval must be greater than 0")
43 }
44 status, err := container.Status()
45 if err != nil {
46 return err
47 }
48 if status == libcontainer.Stopped {
49 return fmt.Errorf("container with id %s is not running", container.ID())
50 }
51 var (
52 stats = make(chan *libcontainer.Stats, 1)
53 events = make(chan *types.Event, 1024)
54 group = &sync.WaitGroup{}
55 )
56 group.Add(1)
57 go func() {
58 defer group.Done()
59 enc := json.NewEncoder(os.Stdout)
60 for e := range events {
61 if err := enc.Encode(e); err != nil {
62 logrus.Error(err)
63 }
64 }
65 }()
66 if context.Bool("stats") {
67 s, err := container.Stats()
68 if err != nil {
69 return err
70 }
71 events <- &types.Event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)}
72 close(events)
73 group.Wait()
74 return nil
75 }
76 go func() {
77 for range time.Tick(context.Duration("interval")) {
78 s, err := container.Stats()
79 if err != nil {
80 logrus.Error(err)
81 continue
82 }
83 stats <- s
84 }
85 }()
86 n, err := container.NotifyOOM()
87 if err != nil {
88 return err
89 }
90 for {
91 select {
92 case _, ok := <-n:
93 if ok {
94
95
96
97 events <- &types.Event{Type: "oom", ID: container.ID()}
98 } else {
99 n = nil
100 }
101 case s := <-stats:
102 events <- &types.Event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)}
103 }
104 if n == nil {
105 close(events)
106 break
107 }
108 }
109 group.Wait()
110 return nil
111 },
112 }
113
114 func convertLibcontainerStats(ls *libcontainer.Stats) *types.Stats {
115 cg := ls.CgroupStats
116 if cg == nil {
117 return nil
118 }
119 var s types.Stats
120 s.Pids.Current = cg.PidsStats.Current
121 s.Pids.Limit = cg.PidsStats.Limit
122
123 s.CPU.Usage.Kernel = cg.CpuStats.CpuUsage.UsageInKernelmode
124 s.CPU.Usage.User = cg.CpuStats.CpuUsage.UsageInUsermode
125 s.CPU.Usage.Total = cg.CpuStats.CpuUsage.TotalUsage
126 s.CPU.Usage.Percpu = cg.CpuStats.CpuUsage.PercpuUsage
127 s.CPU.Usage.PercpuKernel = cg.CpuStats.CpuUsage.PercpuUsageInKernelmode
128 s.CPU.Usage.PercpuUser = cg.CpuStats.CpuUsage.PercpuUsageInUsermode
129 s.CPU.Throttling.Periods = cg.CpuStats.ThrottlingData.Periods
130 s.CPU.Throttling.ThrottledPeriods = cg.CpuStats.ThrottlingData.ThrottledPeriods
131 s.CPU.Throttling.ThrottledTime = cg.CpuStats.ThrottlingData.ThrottledTime
132
133 s.CPUSet = types.CPUSet(cg.CPUSetStats)
134
135 s.Memory.Cache = cg.MemoryStats.Cache
136 s.Memory.Kernel = convertMemoryEntry(cg.MemoryStats.KernelUsage)
137 s.Memory.KernelTCP = convertMemoryEntry(cg.MemoryStats.KernelTCPUsage)
138 s.Memory.Swap = convertMemoryEntry(cg.MemoryStats.SwapUsage)
139 s.Memory.Usage = convertMemoryEntry(cg.MemoryStats.Usage)
140 s.Memory.Raw = cg.MemoryStats.Stats
141
142 s.Blkio.IoServiceBytesRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceBytesRecursive)
143 s.Blkio.IoServicedRecursive = convertBlkioEntry(cg.BlkioStats.IoServicedRecursive)
144 s.Blkio.IoQueuedRecursive = convertBlkioEntry(cg.BlkioStats.IoQueuedRecursive)
145 s.Blkio.IoServiceTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceTimeRecursive)
146 s.Blkio.IoWaitTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoWaitTimeRecursive)
147 s.Blkio.IoMergedRecursive = convertBlkioEntry(cg.BlkioStats.IoMergedRecursive)
148 s.Blkio.IoTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoTimeRecursive)
149 s.Blkio.SectorsRecursive = convertBlkioEntry(cg.BlkioStats.SectorsRecursive)
150
151 s.Hugetlb = make(map[string]types.Hugetlb)
152 for k, v := range cg.HugetlbStats {
153 s.Hugetlb[k] = convertHugtlb(v)
154 }
155
156 if is := ls.IntelRdtStats; is != nil {
157 if intelrdt.IsCATEnabled() {
158 s.IntelRdt.L3CacheInfo = convertL3CacheInfo(is.L3CacheInfo)
159 s.IntelRdt.L3CacheSchemaRoot = is.L3CacheSchemaRoot
160 s.IntelRdt.L3CacheSchema = is.L3CacheSchema
161 }
162 if intelrdt.IsMBAEnabled() {
163 s.IntelRdt.MemBwInfo = convertMemBwInfo(is.MemBwInfo)
164 s.IntelRdt.MemBwSchemaRoot = is.MemBwSchemaRoot
165 s.IntelRdt.MemBwSchema = is.MemBwSchema
166 }
167 if intelrdt.IsMBMEnabled() {
168 s.IntelRdt.MBMStats = is.MBMStats
169 }
170 if intelrdt.IsCMTEnabled() {
171 s.IntelRdt.CMTStats = is.CMTStats
172 }
173 }
174
175 s.NetworkInterfaces = ls.Interfaces
176 return &s
177 }
178
179 func convertHugtlb(c cgroups.HugetlbStats) types.Hugetlb {
180 return types.Hugetlb{
181 Usage: c.Usage,
182 Max: c.MaxUsage,
183 Failcnt: c.Failcnt,
184 }
185 }
186
187 func convertMemoryEntry(c cgroups.MemoryData) types.MemoryEntry {
188 return types.MemoryEntry{
189 Limit: c.Limit,
190 Usage: c.Usage,
191 Max: c.MaxUsage,
192 Failcnt: c.Failcnt,
193 }
194 }
195
196 func convertBlkioEntry(c []cgroups.BlkioStatEntry) []types.BlkioEntry {
197 var out []types.BlkioEntry
198 for _, e := range c {
199 out = append(out, types.BlkioEntry(e))
200 }
201 return out
202 }
203
204 func convertL3CacheInfo(i *intelrdt.L3CacheInfo) *types.L3CacheInfo {
205 ci := types.L3CacheInfo(*i)
206 return &ci
207 }
208
209 func convertMemBwInfo(i *intelrdt.MemBwInfo) *types.MemBwInfo {
210 mi := types.MemBwInfo(*i)
211 return &mi
212 }
213
View as plain text