github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/daemon/stats.go (about)

     1  package daemon
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"runtime"
     7  	"time"
     8  
     9  	"golang.org/x/net/context"
    10  
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/api/types/backend"
    13  	"github.com/docker/docker/api/types/versions"
    14  	"github.com/docker/docker/api/types/versions/v1p20"
    15  	"github.com/docker/docker/container"
    16  	"github.com/docker/docker/pkg/ioutils"
    17  )
    18  
    19  // ContainerStats writes information about the container to the stream
    20  // given in the config object.
    21  func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error {
    22  	// Engine API version (used for backwards compatibility)
    23  	apiVersion := config.Version
    24  
    25  	container, err := daemon.GetContainer(prefixOrName)
    26  	if err != nil {
    27  		return err
    28  	}
    29  
    30  	// If the container is either not running or restarting and requires no stream, return an empty stats.
    31  	if (!container.IsRunning() || container.IsRestarting()) && !config.Stream {
    32  		return json.NewEncoder(config.OutStream).Encode(&types.StatsJSON{
    33  			Name: container.Name,
    34  			ID:   container.ID})
    35  	}
    36  
    37  	outStream := config.OutStream
    38  	if config.Stream {
    39  		wf := ioutils.NewWriteFlusher(outStream)
    40  		defer wf.Close()
    41  		wf.Flush()
    42  		outStream = wf
    43  	}
    44  
    45  	var preCPUStats types.CPUStats
    46  	var preRead time.Time
    47  	getStatJSON := func(v interface{}) *types.StatsJSON {
    48  		ss := v.(types.StatsJSON)
    49  		ss.Name = container.Name
    50  		ss.ID = container.ID
    51  		ss.PreCPUStats = preCPUStats
    52  		ss.PreRead = preRead
    53  		preCPUStats = ss.CPUStats
    54  		preRead = ss.Read
    55  		return &ss
    56  	}
    57  
    58  	enc := json.NewEncoder(outStream)
    59  
    60  	updates := daemon.subscribeToContainerStats(container)
    61  	defer daemon.unsubscribeToContainerStats(container, updates)
    62  
    63  	noStreamFirstFrame := true
    64  	for {
    65  		select {
    66  		case v, ok := <-updates:
    67  			if !ok {
    68  				return nil
    69  			}
    70  
    71  			var statsJSON interface{}
    72  			statsJSONPost120 := getStatJSON(v)
    73  			if versions.LessThan(apiVersion, "1.21") {
    74  				if runtime.GOOS == "windows" {
    75  					return errors.New("API versions pre v1.21 do not support stats on Windows")
    76  				}
    77  				var (
    78  					rxBytes   uint64
    79  					rxPackets uint64
    80  					rxErrors  uint64
    81  					rxDropped uint64
    82  					txBytes   uint64
    83  					txPackets uint64
    84  					txErrors  uint64
    85  					txDropped uint64
    86  				)
    87  				for _, v := range statsJSONPost120.Networks {
    88  					rxBytes += v.RxBytes
    89  					rxPackets += v.RxPackets
    90  					rxErrors += v.RxErrors
    91  					rxDropped += v.RxDropped
    92  					txBytes += v.TxBytes
    93  					txPackets += v.TxPackets
    94  					txErrors += v.TxErrors
    95  					txDropped += v.TxDropped
    96  				}
    97  				statsJSON = &v1p20.StatsJSON{
    98  					Stats: statsJSONPost120.Stats,
    99  					Network: types.NetworkStats{
   100  						RxBytes:   rxBytes,
   101  						RxPackets: rxPackets,
   102  						RxErrors:  rxErrors,
   103  						RxDropped: rxDropped,
   104  						TxBytes:   txBytes,
   105  						TxPackets: txPackets,
   106  						TxErrors:  txErrors,
   107  						TxDropped: txDropped,
   108  					},
   109  				}
   110  			} else {
   111  				statsJSON = statsJSONPost120
   112  			}
   113  
   114  			if !config.Stream && noStreamFirstFrame {
   115  				// prime the cpu stats so they aren't 0 in the final output
   116  				noStreamFirstFrame = false
   117  				continue
   118  			}
   119  
   120  			if err := enc.Encode(statsJSON); err != nil {
   121  				return err
   122  			}
   123  
   124  			if !config.Stream {
   125  				return nil
   126  			}
   127  		case <-ctx.Done():
   128  			return nil
   129  		}
   130  	}
   131  }
   132  
   133  func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} {
   134  	return daemon.statsCollector.Collect(c)
   135  }
   136  
   137  func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) {
   138  	daemon.statsCollector.Unsubscribe(c, ch)
   139  }
   140  
   141  // GetContainerStats collects all the stats published by a container
   142  func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.StatsJSON, error) {
   143  	stats, err := daemon.stats(container)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	// We already have the network stats on Windows directly from HCS.
   149  	if !container.Config.NetworkDisabled && runtime.GOOS != "windows" {
   150  		if stats.Networks, err = daemon.getNetworkStats(container); err != nil {
   151  			return nil, err
   152  		}
   153  	}
   154  
   155  	return stats, nil
   156  }