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  }