Skip to content

Commit 4fc52ed

Browse files
Merge pull request #21534 from mheon/stats_per_interface
Send container stats over API on a per-interface basis
2 parents 246831b + 6e0decb commit 4fc52ed

File tree

11 files changed

+107
-104
lines changed

11 files changed

+107
-104
lines changed

cmd/podman/containers/stats.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,15 @@ func (s *containerStats) MemPerc() string {
215215
}
216216

217217
func (s *containerStats) NetIO() string {
218-
return combineHumanValues(s.NetInput, s.NetOutput)
218+
var netInput uint64
219+
var netOutput uint64
220+
221+
for _, net := range s.Network {
222+
netInput += net.RxBytes
223+
netOutput += net.TxBytes
224+
}
225+
226+
return combineHumanValues(netInput, netOutput)
219227
}
220228

221229
func (s *containerStats) BlockIO() string {

docs/source/markdown/podman-stats.1.md.in

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,8 @@ Valid placeholders for the Go template are listed below:
5050
| .MemUsage | Memory usage |
5151
| .MemUsageBytes | Memory usage (IEC) |
5252
| .Name | Container Name |
53-
| .NetInput | Network Input |
5453
| .NetIO | Network IO |
55-
| .NetOutput | Network Output |
54+
| .Network | Network I/O, separated by network interface |
5655
| .PerCPU | CPU time consumed by all tasks [1] |
5756
| .PIDs | Number of PIDs |
5857
| .PIDS | Number of PIDs (yes, we know this is a dup) |

libpod/define/containerstate.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,23 @@ type ContainerStats struct {
141141
MemUsage uint64
142142
MemLimit uint64
143143
MemPerc float64
144-
NetInput uint64
145-
NetOutput uint64
146-
BlockInput uint64
147-
BlockOutput uint64
148-
PIDs uint64
149-
UpTime time.Duration
150-
Duration uint64
144+
// Map of interface name to network statistics for that interface.
145+
Network map[string]ContainerNetworkStats
146+
BlockInput uint64
147+
BlockOutput uint64
148+
PIDs uint64
149+
UpTime time.Duration
150+
Duration uint64
151+
}
152+
153+
// Statistics for an individual container network interface
154+
type ContainerNetworkStats struct {
155+
RxBytes uint64
156+
RxDropped uint64
157+
RxErrors uint64
158+
RxPackets uint64
159+
TxBytes uint64
160+
TxDropped uint64
161+
TxErrors uint64
162+
TxPackets uint64
151163
}

libpod/networking_freebsd.go

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/containers/buildah/pkg/jail"
1515
"github.com/containers/common/libnetwork/types"
16+
"github.com/containers/podman/v4/libpod/define"
1617
"github.com/containers/storage/pkg/lockfile"
1718
"github.com/sirupsen/logrus"
1819
)
@@ -45,33 +46,6 @@ type NetstatAddress struct {
4546
Collisions uint64 `json:"collisions"`
4647
}
4748

48-
// copied from github.com/vishvanada/netlink which does not build on freebsd
49-
type LinkStatistics64 struct {
50-
RxPackets uint64
51-
TxPackets uint64
52-
RxBytes uint64
53-
TxBytes uint64
54-
RxErrors uint64
55-
TxErrors uint64
56-
RxDropped uint64
57-
TxDropped uint64
58-
Multicast uint64
59-
Collisions uint64
60-
RxLengthErrors uint64
61-
RxOverErrors uint64
62-
RxCrcErrors uint64
63-
RxFrameErrors uint64
64-
RxFifoErrors uint64
65-
RxMissedErrors uint64
66-
TxAbortedErrors uint64
67-
TxCarrierErrors uint64
68-
TxFifoErrors uint64
69-
TxHeartbeatErrors uint64
70-
TxWindowErrors uint64
71-
RxCompressed uint64
72-
TxCompressed uint64
73-
}
74-
7549
type RootlessNetNS struct {
7650
dir string
7751
Lock *lockfile.LockFile
@@ -223,7 +197,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
223197

224198
// TODO (5.0): return the statistics per network interface
225199
// This would allow better compat with docker.
226-
func getContainerNetIO(ctr *Container) (*LinkStatistics64, error) {
200+
func getContainerNetIO(ctr *Container) (map[string]define.ContainerNetworkStats, error) {
227201
if ctr.state.NetNS == "" {
228202
// If NetNS is nil, it was set as none, and no netNS
229203
// was set up this is a valid state and thus return no
@@ -249,8 +223,9 @@ func getContainerNetIO(ctr *Container) (*LinkStatistics64, error) {
249223
return nil, err
250224
}
251225

226+
res := make(map[string]define.ContainerNetworkStats)
227+
252228
// Sum all the interface stats - in practice only Tx/TxBytes are needed
253-
res := &LinkStatistics64{}
254229
for _, ifaddr := range stats.Statistics.Interface {
255230
// Each interface has two records, one for link-layer which has
256231
// an MTU field and one for IP which doesn't. We only want the
@@ -260,14 +235,16 @@ func getContainerNetIO(ctr *Container) (*LinkStatistics64, error) {
260235
// if we move to per-interface stats in future, this can be
261236
// reported separately.
262237
if ifaddr.Mtu > 0 {
263-
res.RxPackets += ifaddr.ReceivedPackets
264-
res.TxPackets += ifaddr.SentPackets
265-
res.RxBytes += ifaddr.ReceivedBytes
266-
res.TxBytes += ifaddr.SentBytes
267-
res.RxErrors += ifaddr.ReceivedErrors
268-
res.TxErrors += ifaddr.SentErrors
269-
res.RxDropped += ifaddr.DroppedPackets
270-
res.Collisions += ifaddr.Collisions
238+
linkStats := define.ContainerNetworkStats{
239+
RxPackets: ifaddr.ReceivedPackets,
240+
TxPackets: ifaddr.SentPackets,
241+
RxBytes: ifaddr.ReceivedBytes,
242+
TxBytes: ifaddr.SentBytes,
243+
RxErrors: ifaddr.ReceivedErrors,
244+
TxErrors: ifaddr.SentErrors,
245+
RxDropped: ifaddr.DroppedPackets,
246+
}
247+
res[ifaddr.Name] = linkStats
271248
}
272249
}
273250

libpod/networking_linux.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/containers/common/libnetwork/types"
1414
netUtil "github.com/containers/common/libnetwork/util"
1515
"github.com/containers/common/pkg/netns"
16+
"github.com/containers/podman/v4/libpod/define"
1617
"github.com/containers/podman/v4/pkg/rootless"
1718
"github.com/opencontainers/runtime-spec/specs-go"
1819
"github.com/sirupsen/logrus"
@@ -186,10 +187,9 @@ func getContainerNetNS(ctr *Container) (string, *Container, error) {
186187
return "", nil, nil
187188
}
188189

189-
// TODO (5.0): return the statistics per network interface
190-
// This would allow better compat with docker.
191-
func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
192-
var netStats *netlink.LinkStatistics
190+
// Returns a map of interface name to statistics for that interface.
191+
func getContainerNetIO(ctr *Container) (map[string]define.ContainerNetworkStats, error) {
192+
perNetworkStats := make(map[string]define.ContainerNetworkStats)
193193

194194
netNSPath, otherCtr, netPathErr := getContainerNetNS(ctr)
195195
if netPathErr != nil {
@@ -222,21 +222,26 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
222222
if err != nil {
223223
return err
224224
}
225-
if netStats == nil {
226-
netStats = link.Attrs().Statistics
227-
continue
228-
}
229-
// Currently only Tx/RxBytes are used.
230-
// In the future we should return all stats per interface so that
231-
// api users have a better options.
232225
stats := link.Attrs().Statistics
233-
netStats.TxBytes += stats.TxBytes
234-
netStats.RxBytes += stats.RxBytes
226+
if stats != nil {
227+
newStats := define.ContainerNetworkStats{
228+
RxBytes: stats.RxBytes,
229+
RxDropped: stats.RxDropped,
230+
RxErrors: stats.RxErrors,
231+
RxPackets: stats.RxPackets,
232+
TxBytes: stats.TxBytes,
233+
TxDropped: stats.TxDropped,
234+
TxErrors: stats.TxErrors,
235+
TxPackets: stats.TxPackets,
236+
}
237+
238+
perNetworkStats[dev] = newStats
239+
}
235240
}
236241
}
237242
return nil
238243
})
239-
return netStats, err
244+
return perNetworkStats, err
240245
}
241246

242247
// joinedNetworkNSPath returns netns path and bool if netns was set

libpod/stats_common.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
4141
}
4242
}
4343

44+
netStats, err := getContainerNetIO(c)
45+
if err != nil {
46+
return nil, err
47+
}
48+
stats.Network = netStats
49+
4450
if err := c.getPlatformContainerStats(stats, previousStats); err != nil {
4551
return nil, err
4652
}

libpod/stats_freebsd.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,6 @@ func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, prev
8080
stats.MemLimit = c.getMemLimit()
8181
stats.SystemNano = now
8282

83-
netStats, err := getContainerNetIO(c)
84-
if err != nil {
85-
return err
86-
}
87-
88-
// Handle case where the container is not in a network namespace
89-
if netStats != nil {
90-
stats.NetInput = netStats.RxBytes
91-
stats.NetOutput = netStats.TxBytes
92-
} else {
93-
stats.NetInput = 0
94-
stats.NetOutput = 0
95-
}
96-
9783
return nil
9884
}
9985

libpod/stats_linux.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@ func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, prev
3939
return fmt.Errorf("unable to obtain cgroup stats: %w", err)
4040
}
4141
conState := c.state.State
42-
netStats, err := getContainerNetIO(c)
43-
if err != nil {
44-
return err
45-
}
4642

4743
// If the current total usage in the cgroup is less than what was previously
4844
// recorded then it means the container was restarted and runs in a new cgroup
@@ -69,14 +65,6 @@ func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, prev
6965
stats.CPUSystemNano = cgroupStats.CpuStats.CpuUsage.UsageInKernelmode
7066
stats.SystemNano = now
7167
stats.PerCPU = cgroupStats.CpuStats.CpuUsage.PercpuUsage
72-
// Handle case where the container is not in a network namespace
73-
if netStats != nil {
74-
stats.NetInput = netStats.RxBytes
75-
stats.NetOutput = netStats.TxBytes
76-
} else {
77-
stats.NetInput = 0
78-
stats.NetOutput = 0
79-
}
8068

8169
return nil
8270
}

pkg/api/handlers/compat/containers_stats_linux.go

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -119,23 +119,20 @@ streamLabel: // A label to flatten the scope
119119
return
120120
}
121121

122-
// FIXME: network inspection does not yet work entirely
123122
net := make(map[string]docker.NetworkStats)
124-
networkName := inspect.NetworkSettings.EndpointID
125-
if networkName == "" {
126-
networkName = "network"
127-
}
128-
net[networkName] = docker.NetworkStats{
129-
RxBytes: stats.NetInput,
130-
RxPackets: 0,
131-
RxErrors: 0,
132-
RxDropped: 0,
133-
TxBytes: stats.NetOutput,
134-
TxPackets: 0,
135-
TxErrors: 0,
136-
TxDropped: 0,
137-
EndpointID: inspect.NetworkSettings.EndpointID,
138-
InstanceID: "",
123+
for netName, netStats := range stats.Network {
124+
net[netName] = docker.NetworkStats{
125+
RxBytes: netStats.RxBytes,
126+
RxPackets: netStats.RxPackets,
127+
RxErrors: netStats.RxErrors,
128+
RxDropped: netStats.RxDropped,
129+
TxBytes: netStats.TxBytes,
130+
TxPackets: netStats.TxPackets,
131+
TxErrors: netStats.TxErrors,
132+
TxDropped: netStats.TxDropped,
133+
EndpointID: inspect.NetworkSettings.EndpointID,
134+
InstanceID: "",
135+
}
139136
}
140137

141138
resources := ctnr.LinuxResources()

pkg/domain/infra/abi/pods_stats.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,19 @@ func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.Po
4444
}
4545
podID := pods[i].ID()[:12]
4646
for j := range podStats {
47+
var podNetInput uint64
48+
var podNetOutput uint64
49+
for _, stats := range podStats[j].Network {
50+
podNetInput += stats.RxBytes
51+
podNetOutput += stats.TxBytes
52+
}
53+
4754
r := entities.PodStatsReport{
4855
CPU: floatToPercentString(podStats[j].CPU),
4956
MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit),
5057
MemUsageBytes: combineBytesValues(podStats[j].MemUsage, podStats[j].MemLimit),
5158
Mem: floatToPercentString(podStats[j].MemPerc),
52-
NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput),
59+
NetIO: combineHumanValues(podNetInput, podNetOutput),
5360
BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput),
5461
PIDS: pidsToString(podStats[j].PIDs),
5562
CID: podStats[j].ContainerID[:12],

0 commit comments

Comments
 (0)