...

Source file src/github.com/opencontainers/runc/events.go

Documentation: github.com/opencontainers/runc

     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  					// this means an oom event was received, if it is !ok then
    95  					// the channel was closed because the container stopped and
    96  					// the cgroups no longer exist.
    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