github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/daemon/stats.go (about)

     1  package daemon
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"runtime"
     8  
     9  	"golang.org/x/net/context"
    10  
    11  	"github.com/docker/docker/api/types/backend"
    12  	"github.com/docker/docker/container"
    13  	"github.com/docker/docker/pkg/ioutils"
    14  	"github.com/docker/engine-api/types"
    15  	"github.com/docker/engine-api/types/versions"
    16  	"github.com/docker/engine-api/types/versions/v1p20"
    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  	if runtime.GOOS == "windows" {
    23  		return errors.New("Windows does not support stats")
    24  	}
    25  	// Remote API version (used for backwards compatibility)
    26  	apiVersion := config.Version
    27  
    28  	container, err := daemon.GetContainer(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  	outStream := config.OutStream
    39  	if config.Stream {
    40  		wf := ioutils.NewWriteFlusher(outStream)
    41  		defer wf.Close()
    42  		wf.Flush()
    43  		outStream = wf
    44  	}
    45  
    46  	var preCPUStats types.CPUStats
    47  	getStatJSON := func(v interface{}) *types.StatsJSON {
    48  		ss := v.(types.StatsJSON)
    49  		ss.PreCPUStats = preCPUStats
    50  		preCPUStats = ss.CPUStats
    51  		return &ss
    52  	}
    53  
    54  	enc := json.NewEncoder(outStream)
    55  
    56  	updates := daemon.subscribeToContainerStats(container)
    57  	defer daemon.unsubscribeToContainerStats(container, updates)
    58  
    59  	noStreamFirstFrame := true
    60  	for {
    61  		select {
    62  		case v, ok := <-updates:
    63  			if !ok {
    64  				return nil
    65  			}
    66  
    67  			var statsJSON interface{}
    68  			statsJSONPost120 := getStatJSON(v)
    69  			if versions.LessThan(apiVersion, "1.21") {
    70  				var (
    71  					rxBytes   uint64
    72  					rxPackets uint64
    73  					rxErrors  uint64
    74  					rxDropped uint64
    75  					txBytes   uint64
    76  					txPackets uint64
    77  					txErrors  uint64
    78  					txDropped uint64
    79  				)
    80  				for _, v := range statsJSONPost120.Networks {
    81  					rxBytes += v.RxBytes
    82  					rxPackets += v.RxPackets
    83  					rxErrors += v.RxErrors
    84  					rxDropped += v.RxDropped
    85  					txBytes += v.TxBytes
    86  					txPackets += v.TxPackets
    87  					txErrors += v.TxErrors
    88  					txDropped += v.TxDropped
    89  				}
    90  				statsJSON = &v1p20.StatsJSON{
    91  					Stats: statsJSONPost120.Stats,
    92  					Network: types.NetworkStats{
    93  						RxBytes:   rxBytes,
    94  						RxPackets: rxPackets,
    95  						RxErrors:  rxErrors,
    96  						RxDropped: rxDropped,
    97  						TxBytes:   txBytes,
    98  						TxPackets: txPackets,
    99  						TxErrors:  txErrors,
   100  						TxDropped: txDropped,
   101  					},
   102  				}
   103  			} else {
   104  				statsJSON = statsJSONPost120
   105  			}
   106  
   107  			if !config.Stream && noStreamFirstFrame {
   108  				// prime the cpu stats so they aren't 0 in the final output
   109  				noStreamFirstFrame = false
   110  				continue
   111  			}
   112  
   113  			if err := enc.Encode(statsJSON); err != nil {
   114  				return err
   115  			}
   116  
   117  			if !config.Stream {
   118  				return nil
   119  			}
   120  		case <-ctx.Done():
   121  			return nil
   122  		}
   123  	}
   124  }
   125  
   126  func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} {
   127  	return daemon.statsCollector.collect(c)
   128  }
   129  
   130  func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) {
   131  	daemon.statsCollector.unsubscribe(c, ch)
   132  }
   133  
   134  // GetContainerStats collects all the stats published by a container
   135  func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.StatsJSON, error) {
   136  	stats, err := daemon.stats(container)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	if stats.Networks, err = daemon.getNetworkStats(container); err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	return stats, nil
   146  }
   147  
   148  // Resolve Network SandboxID in case the container reuse another container's network stack
   149  func (daemon *Daemon) getNetworkSandboxID(c *container.Container) (string, error) {
   150  	curr := c
   151  	for curr.HostConfig.NetworkMode.IsContainer() {
   152  		containerID := curr.HostConfig.NetworkMode.ConnectedContainer()
   153  		connected, err := daemon.GetContainer(containerID)
   154  		if err != nil {
   155  			return "", fmt.Errorf("Could not get container for %s", containerID)
   156  		}
   157  		curr = connected
   158  	}
   159  	return curr.NetworkSettings.SandboxID, nil
   160  }
   161  
   162  func (daemon *Daemon) getNetworkStats(c *container.Container) (map[string]types.NetworkStats, error) {
   163  	sandboxID, err := daemon.getNetworkSandboxID(c)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	sb, err := daemon.netController.SandboxByID(sandboxID)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	lnstats, err := sb.Statistics()
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	stats := make(map[string]types.NetworkStats)
   179  	// Convert libnetwork nw stats into engine-api stats
   180  	for ifName, ifStats := range lnstats {
   181  		stats[ifName] = types.NetworkStats{
   182  			RxBytes:   ifStats.RxBytes,
   183  			RxPackets: ifStats.RxPackets,
   184  			RxErrors:  ifStats.RxErrors,
   185  			RxDropped: ifStats.RxDropped,
   186  			TxBytes:   ifStats.TxBytes,
   187  			TxPackets: ifStats.TxPackets,
   188  			TxErrors:  ifStats.TxErrors,
   189  			TxDropped: ifStats.TxDropped,
   190  		}
   191  	}
   192  
   193  	return stats, nil
   194  }