github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/cli/events.go (about) 1 // Copyright (c) 2014,2015,2016,2017 Docker, Inc. 2 // Copyright (c) 2018 Huawei Corporation. 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 package main 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "os" 13 "sync" 14 "time" 15 16 vc "github.com/kata-containers/runtime/virtcontainers" 17 "github.com/kata-containers/runtime/virtcontainers/types" 18 19 "github.com/kata-containers/runtime/pkg/katautils" 20 "github.com/sirupsen/logrus" 21 "github.com/urfave/cli" 22 ) 23 24 type event struct { 25 Type string `json:"type"` 26 ID string `json:"id"` 27 Data interface{} `json:"data,omitempty"` 28 } 29 30 // stats is the runc specific stats structure for stability when encoding and decoding stats. 31 type stats struct { 32 CPU cpu `json:"cpu"` 33 Memory memory `json:"memory"` 34 Pids pids `json:"pids"` 35 Blkio blkio `json:"blkio"` 36 Hugetlb map[string]hugetlb `json:"hugetlb"` 37 IntelRdt intelRdt `json:"intel_rdt"` 38 } 39 40 type hugetlb struct { 41 Usage uint64 `json:"usage,omitempty"` 42 Max uint64 `json:"max,omitempty"` 43 Failcnt uint64 `json:"failcnt"` 44 } 45 46 type blkioEntry struct { 47 Major uint64 `json:"major,omitempty"` 48 Minor uint64 `json:"minor,omitempty"` 49 Op string `json:"op,omitempty"` 50 Value uint64 `json:"value,omitempty"` 51 } 52 53 type blkio struct { 54 IoServiceBytesRecursive []blkioEntry `json:"ioServiceBytesRecursive,omitempty"` 55 IoServicedRecursive []blkioEntry `json:"ioServicedRecursive,omitempty"` 56 IoQueuedRecursive []blkioEntry `json:"ioQueueRecursive,omitempty"` 57 IoServiceTimeRecursive []blkioEntry `json:"ioServiceTimeRecursive,omitempty"` 58 IoWaitTimeRecursive []blkioEntry `json:"ioWaitTimeRecursive,omitempty"` 59 IoMergedRecursive []blkioEntry `json:"ioMergedRecursive,omitempty"` 60 IoTimeRecursive []blkioEntry `json:"ioTimeRecursive,omitempty"` 61 SectorsRecursive []blkioEntry `json:"sectorsRecursive,omitempty"` 62 } 63 64 type pids struct { 65 Current uint64 `json:"current,omitempty"` 66 Limit uint64 `json:"limit,omitempty"` 67 } 68 69 type throttling struct { 70 Periods uint64 `json:"periods,omitempty"` 71 ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"` 72 ThrottledTime uint64 `json:"throttledTime,omitempty"` 73 } 74 75 type cpuUsage struct { 76 // Units: nanoseconds. 77 Total uint64 `json:"total,omitempty"` 78 Percpu []uint64 `json:"percpu,omitempty"` 79 Kernel uint64 `json:"kernel"` 80 User uint64 `json:"user"` 81 } 82 83 type cpu struct { 84 Usage cpuUsage `json:"usage,omitempty"` 85 Throttling throttling `json:"throttling,omitempty"` 86 } 87 88 type memoryEntry struct { 89 Limit uint64 `json:"limit"` 90 Usage uint64 `json:"usage,omitempty"` 91 Max uint64 `json:"max,omitempty"` 92 Failcnt uint64 `json:"failcnt"` 93 } 94 95 type memory struct { 96 Cache uint64 `json:"cache,omitempty"` 97 Usage memoryEntry `json:"usage,omitempty"` 98 Swap memoryEntry `json:"swap,omitempty"` 99 Kernel memoryEntry `json:"kernel,omitempty"` 100 KernelTCP memoryEntry `json:"kernelTCP,omitempty"` 101 Raw map[string]uint64 `json:"raw,omitempty"` 102 } 103 104 type l3CacheInfo struct { 105 CbmMask string `json:"cbm_mask,omitempty"` 106 MinCbmBits uint64 `json:"min_cbm_bits,omitempty"` 107 NumClosids uint64 `json:"num_closids,omitempty"` 108 } 109 110 type intelRdt struct { 111 // The read-only L3 cache information 112 L3CacheInfo *l3CacheInfo `json:"l3_cache_info,omitempty"` 113 114 // The read-only L3 cache schema in root 115 L3CacheSchemaRoot string `json:"l3_cache_schema_root,omitempty"` 116 117 // The L3 cache schema in 'container_id' group 118 L3CacheSchema string `json:"l3_cache_schema,omitempty"` 119 } 120 121 var eventsCLICommand = cli.Command{ 122 Name: "events", 123 Usage: "display container events such as OOM notifications, cpu, memory, and IO usage statistics", 124 ArgsUsage: `<container-id> 125 126 Where "<container-id>" is the name for the instance of the container.`, 127 Description: `The events command displays information about the container. By default the 128 information is displayed once every 5 seconds.`, 129 Flags: []cli.Flag{ 130 cli.DurationFlag{ 131 Name: "interval", 132 Value: 5 * time.Second, 133 Usage: "set the stats collection interval", 134 }, 135 cli.BoolFlag{ 136 Name: "stats", 137 Usage: "display the container's stats then exit", 138 }, 139 }, 140 Action: func(context *cli.Context) error { 141 ctx, err := cliContextToContext(context) 142 if err != nil { 143 return err 144 } 145 146 span, _ := katautils.Trace(ctx, "events") 147 defer span.Finish() 148 149 containerID := context.Args().First() 150 if containerID == "" { 151 return fmt.Errorf("container id cannot be empty") 152 } 153 154 kataLog = kataLog.WithField("container", containerID) 155 setExternalLoggers(ctx, kataLog) 156 span.SetTag("container", containerID) 157 158 duration := context.Duration("interval") 159 if duration <= 0 { 160 return fmt.Errorf("duration interval must be greater than 0") 161 } 162 163 status, sandboxID, err := getExistingContainerInfo(ctx, containerID) 164 if err != nil { 165 return err 166 } 167 168 containerID = status.ID 169 170 kataLog = kataLog.WithFields(logrus.Fields{ 171 "container": containerID, 172 "sandbox": sandboxID, 173 }) 174 175 setExternalLoggers(ctx, kataLog) 176 span.SetTag("container", containerID) 177 span.SetTag("sandbox", sandboxID) 178 179 if status.State.State == types.StateStopped { 180 return fmt.Errorf("container with id %s is not running", status.ID) 181 } 182 183 var ( 184 events = make(chan *event, 1024) 185 group = &sync.WaitGroup{} 186 ) 187 group.Add(1) 188 189 go func() { 190 defer group.Done() 191 enc := json.NewEncoder(os.Stdout) 192 for e := range events { 193 if err := enc.Encode(e); err != nil { 194 logrus.Error(err) 195 } 196 } 197 }() 198 199 if context.Bool("stats") { 200 s, err := vci.StatsContainer(ctx, sandboxID, containerID) 201 if err != nil { 202 return err 203 } 204 events <- &event{Type: "stats", ID: status.ID, Data: convertVirtcontainerStats(&s)} 205 close(events) 206 group.Wait() 207 return nil 208 } 209 210 go func() { 211 for range time.Tick(context.Duration("interval")) { 212 s, err := vci.StatsContainer(ctx, sandboxID, containerID) 213 if err != nil { 214 logrus.Error(err) 215 continue 216 } 217 events <- &event{Type: "stats", ID: status.ID, Data: convertVirtcontainerStats(&s)} 218 } 219 }() 220 221 group.Wait() 222 return nil 223 }, 224 } 225 226 func convertVirtcontainerStats(containerStats *vc.ContainerStats) *stats { 227 cg := containerStats.CgroupStats 228 if cg == nil { 229 return nil 230 } 231 var s stats 232 s.Pids.Current = cg.PidsStats.Current 233 s.Pids.Limit = cg.PidsStats.Limit 234 235 s.CPU.Usage.Kernel = cg.CPUStats.CPUUsage.UsageInKernelmode 236 s.CPU.Usage.User = cg.CPUStats.CPUUsage.UsageInUsermode 237 s.CPU.Usage.Total = cg.CPUStats.CPUUsage.TotalUsage 238 s.CPU.Usage.Percpu = cg.CPUStats.CPUUsage.PercpuUsage 239 s.CPU.Throttling.Periods = cg.CPUStats.ThrottlingData.Periods 240 s.CPU.Throttling.ThrottledPeriods = cg.CPUStats.ThrottlingData.ThrottledPeriods 241 s.CPU.Throttling.ThrottledTime = cg.CPUStats.ThrottlingData.ThrottledTime 242 243 s.Memory.Cache = cg.MemoryStats.Cache 244 s.Memory.Kernel = convertMemoryEntry(cg.MemoryStats.KernelUsage) 245 s.Memory.KernelTCP = convertMemoryEntry(cg.MemoryStats.KernelTCPUsage) 246 s.Memory.Swap = convertMemoryEntry(cg.MemoryStats.SwapUsage) 247 s.Memory.Usage = convertMemoryEntry(cg.MemoryStats.Usage) 248 s.Memory.Raw = cg.MemoryStats.Stats 249 250 s.Blkio.IoServiceBytesRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceBytesRecursive) 251 s.Blkio.IoServicedRecursive = convertBlkioEntry(cg.BlkioStats.IoServicedRecursive) 252 s.Blkio.IoQueuedRecursive = convertBlkioEntry(cg.BlkioStats.IoQueuedRecursive) 253 s.Blkio.IoServiceTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceTimeRecursive) 254 s.Blkio.IoWaitTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoWaitTimeRecursive) 255 s.Blkio.IoMergedRecursive = convertBlkioEntry(cg.BlkioStats.IoMergedRecursive) 256 s.Blkio.IoTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoTimeRecursive) 257 s.Blkio.SectorsRecursive = convertBlkioEntry(cg.BlkioStats.SectorsRecursive) 258 259 s.Hugetlb = make(map[string]hugetlb) 260 for k, v := range cg.HugetlbStats { 261 s.Hugetlb[k] = convertHugtlb(v) 262 } 263 264 return &s 265 } 266 267 func convertHugtlb(c vc.HugetlbStats) hugetlb { 268 return hugetlb{ 269 Usage: c.Usage, 270 Max: c.MaxUsage, 271 Failcnt: c.Failcnt, 272 } 273 } 274 275 func convertMemoryEntry(c vc.MemoryData) memoryEntry { 276 return memoryEntry{ 277 Limit: c.Limit, 278 Usage: c.Usage, 279 Max: c.MaxUsage, 280 Failcnt: c.Failcnt, 281 } 282 } 283 284 func convertBlkioEntry(c []vc.BlkioStatEntry) []blkioEntry { 285 var out []blkioEntry 286 for _, e := range c { 287 out = append(out, blkioEntry{ 288 Major: e.Major, 289 Minor: e.Minor, 290 Op: e.Op, 291 Value: e.Value, 292 }) 293 } 294 return out 295 }