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