1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
package abi
import (
"context"
"fmt"
"github.com/containers/common/pkg/cgroups"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/utils"
"github.com/docker/go-units"
"github.com/pkg/errors"
)
// PodStats implements printing stats about pods.
func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) {
// Cgroups v2 check for rootless.
if rootless.IsRootless() {
unified, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, err
}
if !unified {
return nil, errors.New("pod stats is not supported in rootless mode without cgroups v2")
}
}
// Get the (running) pods and convert them to the entities format.
pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
return nil, errors.Wrap(err, "unable to get list of pods")
}
return ic.podsToStatsReport(pods)
}
// podsToStatsReport converts a slice of pods into a corresponding slice of stats reports.
func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.PodStatsReport, error) {
reports := []*entities.PodStatsReport{}
for i := range pods { // Access by index to prevent potential loop-variable leaks.
podStats, err := pods[i].GetPodStats(nil)
if err != nil {
return nil, err
}
podID := pods[i].ID()[:12]
for j := range podStats {
r := entities.PodStatsReport{
CPU: floatToPercentString(podStats[j].CPU),
MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit),
MemUsageBytes: combineBytesValues(podStats[j].MemUsage, podStats[j].MemLimit),
Mem: floatToPercentString(podStats[j].MemPerc),
NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput),
BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput),
PIDS: pidsToString(podStats[j].PIDs),
CID: podStats[j].ContainerID[:12],
Name: podStats[j].Name,
Pod: podID,
}
reports = append(reports, &r)
}
}
return reports, nil
}
func combineHumanValues(a, b uint64) string {
if a == 0 && b == 0 {
return "-- / --"
}
return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b)))
}
func combineBytesValues(a, b uint64) string {
if a == 0 && b == 0 {
return "-- / --"
}
return fmt.Sprintf("%s / %s", units.BytesSize(float64(a)), units.BytesSize(float64(b)))
}
func floatToPercentString(f float64) string {
strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
if err != nil || strippedFloat == 0 {
// If things go bazinga, return a safe value
return "--"
}
return fmt.Sprintf("%.2f", strippedFloat) + "%"
}
func pidsToString(pid uint64) string {
if pid == 0 {
// If things go bazinga, return a safe value
return "--"
}
return fmt.Sprintf("%d", pid)
}
|