github.com/openshift/moby-moby@v1.13.2-0.20170601211448-f5ec1e2936dc/daemon/stats.go (about)

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