| package daemon |
| |
| import ( |
| "encoding/json" |
| |
| "github.com/docker/docker/api/stats" |
| "github.com/docker/docker/daemon/execdriver" |
| "github.com/docker/docker/engine" |
| "github.com/docker/libcontainer" |
| "github.com/docker/libcontainer/cgroups" |
| ) |
| |
| func (daemon *Daemon) ContainerStats(job *engine.Job) engine.Status { |
| updates, err := daemon.SubscribeToContainerStats(job.Args[0]) |
| if err != nil { |
| return job.Error(err) |
| } |
| enc := json.NewEncoder(job.Stdout) |
| for v := range updates { |
| update := v.(*execdriver.ResourceStats) |
| ss := convertToAPITypes(update.ContainerStats) |
| ss.MemoryStats.Limit = uint64(update.MemoryLimit) |
| ss.Read = update.Read |
| ss.CpuStats.SystemUsage = update.SystemUsage |
| if err := enc.Encode(ss); err != nil { |
| // TODO: handle the specific broken pipe |
| daemon.UnsubscribeToContainerStats(job.Args[0], updates) |
| return job.Error(err) |
| } |
| } |
| return engine.StatusOK |
| } |
| |
| // convertToAPITypes converts the libcontainer.ContainerStats to the api specific |
| // structs. This is done to preserve API compatibility and versioning. |
| func convertToAPITypes(ls *libcontainer.ContainerStats) *stats.Stats { |
| s := &stats.Stats{} |
| if ls.NetworkStats != nil { |
| s.Network = stats.Network{ |
| RxBytes: ls.NetworkStats.RxBytes, |
| RxPackets: ls.NetworkStats.RxPackets, |
| RxErrors: ls.NetworkStats.RxErrors, |
| RxDropped: ls.NetworkStats.RxDropped, |
| TxBytes: ls.NetworkStats.TxBytes, |
| TxPackets: ls.NetworkStats.TxPackets, |
| TxErrors: ls.NetworkStats.TxErrors, |
| TxDropped: ls.NetworkStats.TxDropped, |
| } |
| } |
| cs := ls.CgroupStats |
| if cs != nil { |
| s.BlkioStats = stats.BlkioStats{ |
| IoServiceBytesRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceBytesRecursive), |
| IoServicedRecursive: copyBlkioEntry(cs.BlkioStats.IoServicedRecursive), |
| IoQueuedRecursive: copyBlkioEntry(cs.BlkioStats.IoQueuedRecursive), |
| IoServiceTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceTimeRecursive), |
| IoWaitTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoWaitTimeRecursive), |
| IoMergedRecursive: copyBlkioEntry(cs.BlkioStats.IoMergedRecursive), |
| IoTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoTimeRecursive), |
| SectorsRecursive: copyBlkioEntry(cs.BlkioStats.SectorsRecursive), |
| } |
| cpu := cs.CpuStats |
| s.CpuStats = stats.CpuStats{ |
| CpuUsage: stats.CpuUsage{ |
| TotalUsage: cpu.CpuUsage.TotalUsage, |
| PercpuUsage: cpu.CpuUsage.PercpuUsage, |
| UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode, |
| UsageInUsermode: cpu.CpuUsage.UsageInUsermode, |
| }, |
| ThrottlingData: stats.ThrottlingData{ |
| Periods: cpu.ThrottlingData.Periods, |
| ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods, |
| ThrottledTime: cpu.ThrottlingData.ThrottledTime, |
| }, |
| } |
| mem := cs.MemoryStats |
| s.MemoryStats = stats.MemoryStats{ |
| Usage: mem.Usage, |
| MaxUsage: mem.MaxUsage, |
| Stats: mem.Stats, |
| Failcnt: mem.Failcnt, |
| } |
| } |
| return s |
| } |
| |
| func copyBlkioEntry(entries []cgroups.BlkioStatEntry) []stats.BlkioStatEntry { |
| out := make([]stats.BlkioStatEntry, len(entries)) |
| for i, re := range entries { |
| out[i] = stats.BlkioStatEntry{ |
| Major: re.Major, |
| Minor: re.Minor, |
| Op: re.Op, |
| Value: re.Value, |
| } |
| } |
| return out |
| } |