123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- package daemon
- import (
- "encoding/json"
- "errors"
- "fmt"
- "runtime"
- "time"
- "golang.org/x/net/context"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/api/types/backend"
- "github.com/docker/docker/api/types/versions"
- "github.com/docker/docker/api/types/versions/v1p20"
- "github.com/docker/docker/container"
- "github.com/docker/docker/pkg/ioutils"
- )
- // ContainerStats writes information about the container to the stream
- // given in the config object.
- func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error {
- if runtime.GOOS == "solaris" {
- return fmt.Errorf("%+v does not support stats", runtime.GOOS)
- }
- // Engine API version (used for backwards compatibility)
- apiVersion := config.Version
- container, err := daemon.GetContainer(prefixOrName)
- if err != nil {
- return err
- }
- // If the container is either not running or restarting and requires no stream, return an empty stats.
- if (!container.IsRunning() || container.IsRestarting()) && !config.Stream {
- return json.NewEncoder(config.OutStream).Encode(&types.StatsJSON{
- Name: container.Name,
- ID: container.ID})
- }
- outStream := config.OutStream
- if config.Stream {
- wf := ioutils.NewWriteFlusher(outStream)
- defer wf.Close()
- wf.Flush()
- outStream = wf
- }
- var preCPUStats types.CPUStats
- var preRead time.Time
- getStatJSON := func(v interface{}) *types.StatsJSON {
- ss := v.(types.StatsJSON)
- ss.Name = container.Name
- ss.ID = container.ID
- ss.PreCPUStats = preCPUStats
- ss.PreRead = preRead
- preCPUStats = ss.CPUStats
- preRead = ss.Read
- return &ss
- }
- enc := json.NewEncoder(outStream)
- updates := daemon.subscribeToContainerStats(container)
- defer daemon.unsubscribeToContainerStats(container, updates)
- noStreamFirstFrame := true
- for {
- select {
- case v, ok := <-updates:
- if !ok {
- return nil
- }
- var statsJSON interface{}
- statsJSONPost120 := getStatJSON(v)
- if versions.LessThan(apiVersion, "1.21") {
- if runtime.GOOS == "windows" {
- return errors.New("API versions pre v1.21 do not support stats on Windows")
- }
- var (
- rxBytes uint64
- rxPackets uint64
- rxErrors uint64
- rxDropped uint64
- txBytes uint64
- txPackets uint64
- txErrors uint64
- txDropped uint64
- )
- for _, v := range statsJSONPost120.Networks {
- rxBytes += v.RxBytes
- rxPackets += v.RxPackets
- rxErrors += v.RxErrors
- rxDropped += v.RxDropped
- txBytes += v.TxBytes
- txPackets += v.TxPackets
- txErrors += v.TxErrors
- txDropped += v.TxDropped
- }
- statsJSON = &v1p20.StatsJSON{
- Stats: statsJSONPost120.Stats,
- Network: types.NetworkStats{
- RxBytes: rxBytes,
- RxPackets: rxPackets,
- RxErrors: rxErrors,
- RxDropped: rxDropped,
- TxBytes: txBytes,
- TxPackets: txPackets,
- TxErrors: txErrors,
- TxDropped: txDropped,
- },
- }
- } else {
- statsJSON = statsJSONPost120
- }
- if !config.Stream && noStreamFirstFrame {
- // prime the cpu stats so they aren't 0 in the final output
- noStreamFirstFrame = false
- continue
- }
- if err := enc.Encode(statsJSON); err != nil {
- return err
- }
- if !config.Stream {
- return nil
- }
- case <-ctx.Done():
- return nil
- }
- }
- }
- func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} {
- return daemon.statsCollector.Collect(c)
- }
- func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) {
- daemon.statsCollector.Unsubscribe(c, ch)
- }
- // GetContainerStats collects all the stats published by a container
- func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.StatsJSON, error) {
- stats, err := daemon.stats(container)
- if err != nil {
- return nil, err
- }
- // We already have the network stats on Windows directly from HCS.
- if !container.Config.NetworkDisabled && runtime.GOOS != "windows" {
- if stats.Networks, err = daemon.getNetworkStats(container); err != nil {
- return nil, err
- }
- }
- return stats, nil
- }
|