2015-01-07 21:02:08 -05:00
|
|
|
package daemon
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
|
|
|
|
"github.com/docker/docker/api/stats"
|
2015-01-19 18:29:42 -05:00
|
|
|
"github.com/docker/docker/daemon/execdriver"
|
2015-01-07 21:02:08 -05:00
|
|
|
"github.com/docker/docker/engine"
|
2015-01-19 18:29:42 -05:00
|
|
|
"github.com/docker/libcontainer"
|
|
|
|
"github.com/docker/libcontainer/cgroups"
|
2015-01-07 21:02:08 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func (daemon *Daemon) ContainerStats(job *engine.Job) engine.Status {
|
2015-01-19 18:29:42 -05:00
|
|
|
updates, err := daemon.SubscribeToContainerStats(job.Args[0])
|
2015-01-07 21:02:08 -05:00
|
|
|
if err != nil {
|
|
|
|
return job.Error(err)
|
|
|
|
}
|
|
|
|
enc := json.NewEncoder(job.Stdout)
|
2015-01-19 18:29:42 -05:00
|
|
|
for v := range updates {
|
|
|
|
update := v.(*execdriver.ResourceStats)
|
|
|
|
ss := convertToAPITypes(update.ContainerStats)
|
2015-01-07 21:02:08 -05:00
|
|
|
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
|
2015-01-19 18:29:42 -05:00
|
|
|
daemon.UnsubscribeToContainerStats(job.Args[0], updates)
|
2015-01-07 21:02:08 -05:00
|
|
|
return job.Error(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return engine.StatusOK
|
|
|
|
}
|
2015-01-19 18:29:42 -05:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|