Browse code

Add docker stats --no-stream show cpu usage

Signed-off-by: Lei Jitang <leijitang@huawei.com>

Lei Jitang authored on 2015/05/31 02:25:51
Showing 5 changed files
... ...
@@ -46,7 +46,6 @@ func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
46 46
 	var (
47 47
 		previousCPU    uint64
48 48
 		previousSystem uint64
49
-		start          = true
50 49
 		dec            = json.NewDecoder(stream)
51 50
 		u              = make(chan error, 1)
52 51
 	)
... ...
@@ -61,10 +60,9 @@ func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
61 61
 				memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
62 62
 				cpuPercent = 0.0
63 63
 			)
64
-			if !start {
65
-				cpuPercent = calculateCPUPercent(previousCPU, previousSystem, v)
66
-			}
67
-			start = false
64
+			previousCPU = v.PreCpuStats.CpuUsage.TotalUsage
65
+			previousSystem = v.PreCpuStats.SystemUsage
66
+			cpuPercent = calculateCPUPercent(previousCPU, previousSystem, v)
68 67
 			s.mu.Lock()
69 68
 			s.CPUPercentage = cpuPercent
70 69
 			s.Memory = float64(v.MemoryStats.Usage)
... ...
@@ -73,8 +71,6 @@ func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
73 73
 			s.NetworkRx = float64(v.Network.RxBytes)
74 74
 			s.NetworkTx = float64(v.Network.TxBytes)
75 75
 			s.mu.Unlock()
76
-			previousCPU = v.CpuStats.CpuUsage.TotalUsage
77
-			previousSystem = v.CpuStats.SystemUsage
78 76
 			u <- nil
79 77
 			if !streamStats {
80 78
 				return
... ...
@@ -151,7 +147,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
151 151
 	}
152 152
 	// do a quick pause so that any failed connections for containers that do not exist are able to be
153 153
 	// evicted before we display the initial or default values.
154
-	time.Sleep(500 * time.Millisecond)
154
+	time.Sleep(1500 * time.Millisecond)
155 155
 	var errs []string
156 156
 	for _, c := range cStats {
157 157
 		c.mu.Lock()
... ...
@@ -84,6 +84,7 @@ type Network struct {
84 84
 type Stats struct {
85 85
 	Read        time.Time   `json:"read"`
86 86
 	Network     Network     `json:"network,omitempty"`
87
+	PreCpuStats CpuStats    `json:"precpu_stats,omitempty"`
87 88
 	CpuStats    CpuStats    `json:"cpu_stats,omitempty"`
88 89
 	MemoryStats MemoryStats `json:"memory_stats,omitempty"`
89 90
 	BlkioStats  BlkioStats  `json:"blkio_stats,omitempty"`
... ...
@@ -2,9 +2,9 @@ package daemon
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
-	"io"
6
-
5
+	"github.com/docker/docker/api/types"
7 6
 	"github.com/docker/docker/daemon/execdriver"
7
+	"io"
8 8
 )
9 9
 
10 10
 func (daemon *Daemon) ContainerStats(name string, stream bool, out io.Writer) error {
... ...
@@ -12,13 +12,23 @@ func (daemon *Daemon) ContainerStats(name string, stream bool, out io.Writer) er
12 12
 	if err != nil {
13 13
 		return err
14 14
 	}
15
+	var pre_cpu_stats types.CpuStats
16
+	for first_v := range updates {
17
+		first_update := first_v.(*execdriver.ResourceStats)
18
+		first_stats := convertToAPITypes(first_update.Stats)
19
+		pre_cpu_stats = first_stats.CpuStats
20
+		pre_cpu_stats.SystemUsage = first_update.SystemUsage
21
+		break
22
+	}
15 23
 	enc := json.NewEncoder(out)
16 24
 	for v := range updates {
17 25
 		update := v.(*execdriver.ResourceStats)
18 26
 		ss := convertToAPITypes(update.Stats)
27
+		ss.PreCpuStats = pre_cpu_stats
19 28
 		ss.MemoryStats.Limit = uint64(update.MemoryLimit)
20 29
 		ss.Read = update.Read
21 30
 		ss.CpuStats.SystemUsage = update.SystemUsage
31
+		pre_cpu_stats = ss.CpuStats
22 32
 		if err := enc.Encode(ss); err != nil {
23 33
 			// TODO: handle the specific broken pipe
24 34
 			daemon.UnsubscribeToContainerStats(name, updates)
25 35
new file mode 100644
... ...
@@ -0,0 +1,48 @@
0
+package main
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"github.com/docker/docker/api/types"
6
+	"github.com/go-check/check"
7
+	"strings"
8
+	"time"
9
+)
10
+
11
+func (s *DockerSuite) TestCliStatsNoStreamGetCpu(c *check.C) {
12
+	out, _ := dockerCmd(c, "run", "-d", "--cpu-quota=2000", "busybox", "/bin/sh", "-c", "while true;do echo 'Hello';done")
13
+
14
+	id := strings.TrimSpace(out)
15
+	if err := waitRun(id); err != nil {
16
+		c.Fatal(err)
17
+	}
18
+	ch := make(chan error)
19
+	var v *types.Stats
20
+	go func() {
21
+		_, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=1", id), nil, "")
22
+		if err != nil {
23
+			ch <- err
24
+		}
25
+		dec := json.NewDecoder(body)
26
+		if err := dec.Decode(&v); err != nil {
27
+			ch <- err
28
+		}
29
+		ch <- nil
30
+	}()
31
+	select {
32
+	case e := <-ch:
33
+		if e == nil {
34
+			var cpuPercent = 0.0
35
+			cpuDelta := float64(v.CpuStats.CpuUsage.TotalUsage - v.PreCpuStats.CpuUsage.TotalUsage)
36
+			systemDelta := float64(v.CpuStats.SystemUsage - v.PreCpuStats.SystemUsage)
37
+			cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
38
+			if cpuPercent < 1.8 || cpuPercent > 2.2 {
39
+				c.Fatal("docker stats with no-stream get cpu usage failed")
40
+			}
41
+
42
+		}
43
+	case <-time.After(4 * time.Second):
44
+		c.Fatal("docker stats with no-stream timeout")
45
+	}
46
+
47
+}
... ...
@@ -29,7 +29,7 @@ func (s *DockerSuite) TestCliStatsNoStream(c *check.C) {
29 29
 		if err != nil {
30 30
 			c.Fatalf("Error running stats: %v", err)
31 31
 		}
32
-	case <-time.After(2 * time.Second):
32
+	case <-time.After(3 * time.Second):
33 33
 		statsCmd.Process.Kill()
34 34
 		c.Fatalf("stats did not return immediately when not streaming")
35 35
 	}