4f174aa7 |
package daemon
import (
"encoding/json" |
55268f4e |
"errors"
"runtime" |
340e5233 |
"time" |
855a056a |
|
62c9e62e |
"golang.org/x/net/context"
|
91e197d6 |
"github.com/docker/docker/api/types" |
06d8f504 |
"github.com/docker/docker/api/types/backend" |
91e197d6 |
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/api/types/versions/v1p20" |
fb48bf51 |
"github.com/docker/docker/container" |
ae4ee974 |
"github.com/docker/docker/pkg/ioutils" |
4f174aa7 |
)
|
abd72d40 |
// ContainerStats writes information about the container to the stream
// given in the config object. |
62c9e62e |
func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error { |
f0d55cd0 |
// Engine API version (used for backwards compatibility) |
7534f172 |
apiVersion := config.Version |
55268f4e |
|
d7d512bb |
container, err := daemon.GetContainer(prefixOrName) |
2d5d606f |
if err != nil {
return err
}
|
786a9549 |
// If the container is either not running or restarting and requires no stream, return an empty stats.
if (!container.IsRunning() || container.IsRestarting()) && !config.Stream { |
eb3a7c23 |
return json.NewEncoder(config.OutStream).Encode(&types.StatsJSON{
Name: container.Name,
ID: container.ID}) |
2d5d606f |
}
|
ae4ee974 |
outStream := config.OutStream |
1cbf5a54 |
if config.Stream { |
ae4ee974 |
wf := ioutils.NewWriteFlusher(outStream)
defer wf.Close()
wf.Flush()
outStream = wf |
1cbf5a54 |
}
|
abd72d40 |
var preCPUStats types.CPUStats |
340e5233 |
var preRead time.Time |
d3379946 |
getStatJSON := func(v interface{}) *types.StatsJSON { |
d17ee4b5 |
ss := v.(types.StatsJSON) |
ef915fd0 |
ss.Name = container.Name
ss.ID = container.ID |
abd72d40 |
ss.PreCPUStats = preCPUStats |
340e5233 |
ss.PreRead = preRead |
abd72d40 |
preCPUStats = ss.CPUStats |
340e5233 |
preRead = ss.Read |
d17ee4b5 |
return &ss |
855a056a |
}
|
ae4ee974 |
enc := json.NewEncoder(outStream) |
855a056a |
|
99065544 |
updates := daemon.subscribeToContainerStats(container) |
dc8a3903 |
defer daemon.unsubscribeToContainerStats(container, updates) |
855a056a |
|
1cbf5a54 |
noStreamFirstFrame := true
for {
select {
case v, ok := <-updates:
if !ok {
return nil
}
|
d2c04f84 |
var statsJSON interface{}
statsJSONPost120 := getStatJSON(v) |
7534f172 |
if versions.LessThan(apiVersion, "1.21") { |
340e5233 |
if runtime.GOOS == "windows" {
return errors.New("API versions pre v1.21 do not support stats on Windows")
} |
d3379946 |
var (
rxBytes uint64
rxPackets uint64
rxErrors uint64
rxDropped uint64
txBytes uint64
txPackets uint64
txErrors uint64
txDropped uint64
) |
d2c04f84 |
for _, v := range statsJSONPost120.Networks { |
d3379946 |
rxBytes += v.RxBytes
rxPackets += v.RxPackets
rxErrors += v.RxErrors
rxDropped += v.RxDropped
txBytes += v.TxBytes
txPackets += v.TxPackets
txErrors += v.TxErrors
txDropped += v.TxDropped
} |
d2c04f84 |
statsJSON = &v1p20.StatsJSON{
Stats: statsJSONPost120.Stats, |
d3379946 |
Network: types.NetworkStats{
RxBytes: rxBytes,
RxPackets: rxPackets,
RxErrors: rxErrors,
RxDropped: rxDropped,
TxBytes: txBytes,
TxPackets: txPackets,
TxErrors: txErrors,
TxDropped: txDropped,
},
} |
d2c04f84 |
} else {
statsJSON = statsJSONPost120 |
d3379946 |
}
|
1cbf5a54 |
if !config.Stream && noStreamFirstFrame {
// prime the cpu stats so they aren't 0 in the final output
noStreamFirstFrame = false
continue
}
|
d3379946 |
if err := enc.Encode(statsJSON); err != nil { |
1cbf5a54 |
return err
} |
855a056a |
|
1cbf5a54 |
if !config.Stream {
return nil
} |
62c9e62e |
case <-ctx.Done(): |
1cbf5a54 |
return nil |
4f174aa7 |
}
}
} |
fb48bf51 |
func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} { |
835971c6 |
return daemon.statsCollector.Collect(c) |
fb48bf51 |
}
func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) { |
835971c6 |
daemon.statsCollector.Unsubscribe(c, ch) |
fb48bf51 |
} |
bfa0885c |
// 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
}
|
340e5233 |
// We already have the network stats on Windows directly from HCS.
if !container.Config.NetworkDisabled && runtime.GOOS != "windows" { |
7bb9c539 |
if stats.Networks, err = daemon.getNetworkStats(container); err != nil {
return nil, err
} |
bfa0885c |
}
return stats, nil
} |