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 }