github.com/gondor/docker@v1.9.0-rc1/daemon/stats.go (about)

     1  package daemon
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  
     7  	"github.com/docker/docker/api/types"
     8  	"github.com/docker/docker/api/types/versions/v1p20"
     9  	"github.com/docker/docker/daemon/execdriver"
    10  	"github.com/docker/docker/pkg/version"
    11  	lntypes "github.com/docker/libnetwork/types"
    12  	"github.com/opencontainers/runc/libcontainer"
    13  )
    14  
    15  // ContainerStatsConfig holds information for configuring the runtime
    16  // behavior of a daemon.ContainerStats() call.
    17  type ContainerStatsConfig struct {
    18  	Stream    bool
    19  	OutStream io.Writer
    20  	Stop      <-chan bool
    21  	Version   version.Version
    22  }
    23  
    24  // ContainerStats writes information about the container to the stream
    25  // given in the config object.
    26  func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStatsConfig) error {
    27  
    28  	container, err := daemon.Get(prefixOrName)
    29  	if err != nil {
    30  		return err
    31  	}
    32  
    33  	// If the container is not running and requires no stream, return an empty stats.
    34  	if !container.IsRunning() && !config.Stream {
    35  		return json.NewEncoder(config.OutStream).Encode(&types.Stats{})
    36  	}
    37  
    38  	updates, err := daemon.subscribeToContainerStats(container)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	if config.Stream {
    44  		// Write an empty chunk of data.
    45  		// This is to ensure that the HTTP status code is sent immediately,
    46  		// even if the container has not yet produced any data.
    47  		config.OutStream.Write(nil)
    48  	}
    49  
    50  	var preCPUStats types.CPUStats
    51  	getStatJSON := func(v interface{}) *types.StatsJSON {
    52  		update := v.(*execdriver.ResourceStats)
    53  		// Retrieve the nw statistics from libnetwork and inject them in the Stats
    54  		if nwStats, err := daemon.getNetworkStats(container); err == nil {
    55  			update.Stats.Interfaces = nwStats
    56  		}
    57  		ss := convertStatsToAPITypes(update.Stats)
    58  		ss.PreCPUStats = preCPUStats
    59  		ss.MemoryStats.Limit = uint64(update.MemoryLimit)
    60  		ss.Read = update.Read
    61  		ss.CPUStats.SystemUsage = update.SystemUsage
    62  		preCPUStats = ss.CPUStats
    63  		return ss
    64  	}
    65  
    66  	enc := json.NewEncoder(config.OutStream)
    67  
    68  	defer daemon.unsubscribeToContainerStats(container, updates)
    69  
    70  	noStreamFirstFrame := true
    71  	for {
    72  		select {
    73  		case v, ok := <-updates:
    74  			if !ok {
    75  				return nil
    76  			}
    77  
    78  			statsJSON := getStatJSON(v)
    79  			if config.Version.LessThan("1.21") {
    80  				var (
    81  					rxBytes   uint64
    82  					rxPackets uint64
    83  					rxErrors  uint64
    84  					rxDropped uint64
    85  					txBytes   uint64
    86  					txPackets uint64
    87  					txErrors  uint64
    88  					txDropped uint64
    89  				)
    90  				for _, v := range statsJSON.Networks {
    91  					rxBytes += v.RxBytes
    92  					rxPackets += v.RxPackets
    93  					rxErrors += v.RxErrors
    94  					rxDropped += v.RxDropped
    95  					txBytes += v.TxBytes
    96  					txPackets += v.TxPackets
    97  					txErrors += v.TxErrors
    98  					txDropped += v.TxDropped
    99  				}
   100  				statsJSONPre121 := &v1p20.StatsJSON{
   101  					Stats: statsJSON.Stats,
   102  					Network: types.NetworkStats{
   103  						RxBytes:   rxBytes,
   104  						RxPackets: rxPackets,
   105  						RxErrors:  rxErrors,
   106  						RxDropped: rxDropped,
   107  						TxBytes:   txBytes,
   108  						TxPackets: txPackets,
   109  						TxErrors:  txErrors,
   110  						TxDropped: txDropped,
   111  					},
   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(statsJSONPre121); err != nil {
   121  					return err
   122  				}
   123  
   124  				if !config.Stream {
   125  					return nil
   126  				}
   127  			}
   128  
   129  			if !config.Stream && noStreamFirstFrame {
   130  				// prime the cpu stats so they aren't 0 in the final output
   131  				noStreamFirstFrame = false
   132  				continue
   133  			}
   134  
   135  			if err := enc.Encode(statsJSON); err != nil {
   136  				return err
   137  			}
   138  
   139  			if !config.Stream {
   140  				return nil
   141  			}
   142  		case <-config.Stop:
   143  			return nil
   144  		}
   145  	}
   146  }
   147  
   148  func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) {
   149  	var list []*libcontainer.NetworkInterface
   150  
   151  	sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
   152  	if err != nil {
   153  		return list, err
   154  	}
   155  
   156  	stats, err := sb.Statistics()
   157  	if err != nil {
   158  		return list, err
   159  	}
   160  
   161  	// Convert libnetwork nw stats into libcontainer nw stats
   162  	for ifName, ifStats := range stats {
   163  		list = append(list, convertLnNetworkStats(ifName, ifStats))
   164  	}
   165  
   166  	return list, nil
   167  }
   168  
   169  func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface {
   170  	n := &libcontainer.NetworkInterface{Name: name}
   171  	n.RxBytes = stats.RxBytes
   172  	n.RxPackets = stats.RxPackets
   173  	n.RxErrors = stats.RxErrors
   174  	n.RxDropped = stats.RxDropped
   175  	n.TxBytes = stats.TxBytes
   176  	n.TxPackets = stats.TxPackets
   177  	n.TxErrors = stats.TxErrors
   178  	n.TxDropped = stats.TxDropped
   179  	return n
   180  }