Skip to content

Commit f436cbb

Browse files
authored
Add NetStat to parse /proc/net/stat/... (#316)
* Add NetStat to parse /proc/net/stat/... Signed-off-by: Aleksei Zakharov <[email protected]>
1 parent e979fa4 commit f436cbb

File tree

3 files changed

+199
-0
lines changed

3 files changed

+199
-0
lines changed

fixtures.ttar

+17
Original file line numberDiff line numberDiff line change
@@ -2209,6 +2209,23 @@ Lines: 1
22092209
00015c73 00020e76 F0000769 00000000
22102210
Mode: 644
22112211
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2212+
Directory: fixtures/proc/net/stat
2213+
Mode: 755
2214+
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2215+
Path: fixtures/proc/net/stat/arp_cache
2216+
Lines: 3
2217+
entries allocs destroys hash_grows lookups hits res_failed rcv_probes_mcast rcv_probes_ucast periodic_gc_runs forced_gc_runs unresolved_discards table_fulls
2218+
00000014 00000001 00000002 00000003 00000004 00000005 00000006 00000007 00000008 00000009 0000000a 0000000b 0000000c
2219+
00000014 0000000d 0000000e 0000000f 00000010 00000011 00000012 00000013 00000014 00000015 00000016 00000017 00000018
2220+
Mode: 644
2221+
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2222+
Path: fixtures/proc/net/stat/ndisc_cache
2223+
Lines: 3
2224+
entries allocs destroys hash_grows lookups hits res_failed rcv_probes_mcast rcv_probes_ucast periodic_gc_runs forced_gc_runs unresolved_discards table_fulls
2225+
00000024 000000f0 000000f1 000000f2 000000f3 000000f4 000000f5 000000f6 000000f7 000000f8 000000f9 000000fa 000000fb
2226+
00000024 000000fc 000000fd 000000fe 000000ff 00000100 00000101 00000102 00000103 00000104 00000105 00000106 00000107
2227+
Mode: 644
2228+
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
22122229
Path: fixtures/proc/net/tcp
22132230
Lines: 4
22142231
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode

netstat.go

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2020 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package procfs
15+
16+
import (
17+
"bufio"
18+
"os"
19+
"path/filepath"
20+
"strconv"
21+
"strings"
22+
)
23+
24+
// NetStat contains statistics for all the counters from one file
25+
type NetStat struct {
26+
Filename string
27+
Stats map[string][]uint64
28+
}
29+
30+
// NetStat retrieves stats from /proc/net/stat/
31+
func (fs FS) NetStat() ([]NetStat, error) {
32+
statFiles, err := filepath.Glob(fs.proc.Path("net/stat/*"))
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
var netStatsTotal []NetStat
38+
39+
for _, filePath := range statFiles {
40+
file, err := os.Open(filePath)
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
netStatFile := NetStat{
46+
Filename: filepath.Base(filePath),
47+
Stats: make(map[string][]uint64),
48+
}
49+
scanner := bufio.NewScanner(file)
50+
scanner.Scan()
51+
// First string is always a header for stats
52+
var headers []string
53+
headers = append(headers, strings.Fields(scanner.Text())...)
54+
55+
// Other strings represent per-CPU counters
56+
for scanner.Scan() {
57+
for num, counter := range strings.Fields(scanner.Text()) {
58+
value, err := strconv.ParseUint(counter, 16, 32)
59+
if err != nil {
60+
return nil, err
61+
}
62+
netStatFile.Stats[headers[num]] = append(netStatFile.Stats[headers[num]], value)
63+
}
64+
}
65+
netStatsTotal = append(netStatsTotal, netStatFile)
66+
}
67+
return netStatsTotal, nil
68+
}

netstat_test.go

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2020 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package procfs
15+
16+
import (
17+
"testing"
18+
)
19+
20+
func TestNetStat(t *testing.T) {
21+
const (
22+
filesCount = 2
23+
CPUsCount = 2
24+
arpCacheMetricsCount = 13
25+
ndiscCacheMetricsCount = 13
26+
)
27+
28+
fs, err := NewFS(procTestFixtures)
29+
if err != nil {
30+
t.Fatalf("failed to open procfs: %v", err)
31+
}
32+
33+
netStats, err := fs.NetStat()
34+
if err != nil {
35+
t.Fatalf("NetStat() error: %s", err)
36+
}
37+
38+
if len(netStats) != filesCount {
39+
t.Fatalf("unexpected number of files parsed %d, expected %d", len(netStats), filesCount)
40+
}
41+
42+
expectedStats := [2]NetStat{
43+
{
44+
Filename: "arp_cache",
45+
Stats: make(map[string][]uint64),
46+
},
47+
{
48+
Filename: "ndisc_cache",
49+
Stats: make(map[string][]uint64),
50+
},
51+
}
52+
53+
for _, expected := range expectedStats {
54+
if expected.Filename == "arp_cache" {
55+
expected.Stats["entries"] = []uint64{20, 20}
56+
expected.Stats["allocs"] = []uint64{1, 13}
57+
expected.Stats["destroys"] = []uint64{2, 14}
58+
expected.Stats["hash_grows"] = []uint64{3, 15}
59+
expected.Stats["lookups"] = []uint64{4, 16}
60+
expected.Stats["hits"] = []uint64{5, 17}
61+
expected.Stats["res_failed"] = []uint64{6, 18}
62+
expected.Stats["rcv_probes_mcast"] = []uint64{7, 19}
63+
expected.Stats["rcv_probes_ucast"] = []uint64{8, 20}
64+
expected.Stats["periodic_gc_runs"] = []uint64{9, 21}
65+
expected.Stats["forced_gc_runs"] = []uint64{10, 22}
66+
expected.Stats["unresolved_discards"] = []uint64{11, 23}
67+
expected.Stats["table_fulls"] = []uint64{12, 24}
68+
}
69+
if expected.Filename == "ndisc_cache" {
70+
expected.Stats["entries"] = []uint64{36, 36}
71+
expected.Stats["allocs"] = []uint64{240, 252}
72+
expected.Stats["destroys"] = []uint64{241, 253}
73+
expected.Stats["hash_grows"] = []uint64{242, 254}
74+
expected.Stats["lookups"] = []uint64{243, 255}
75+
expected.Stats["hits"] = []uint64{244, 256}
76+
expected.Stats["res_failed"] = []uint64{245, 257}
77+
expected.Stats["rcv_probes_mcast"] = []uint64{246, 258}
78+
expected.Stats["rcv_probes_ucast"] = []uint64{247, 259}
79+
expected.Stats["periodic_gc_runs"] = []uint64{248, 260}
80+
expected.Stats["forced_gc_runs"] = []uint64{249, 261}
81+
expected.Stats["unresolved_discards"] = []uint64{250, 262}
82+
expected.Stats["table_fulls"] = []uint64{251, 263}
83+
}
84+
}
85+
86+
for _, netStatFile := range netStats {
87+
if netStatFile.Filename == "arp_cache" && len(netStatFile.Stats) != arpCacheMetricsCount {
88+
t.Fatalf("unexpected arp_cache metrics count %d, expected %d", len(netStatFile.Stats), arpCacheMetricsCount)
89+
}
90+
if netStatFile.Filename == "ndisc_cache" && len(netStatFile.Stats) != ndiscCacheMetricsCount {
91+
t.Fatalf("unexpected ndisc_cache metrics count %d, expected %d", len(netStatFile.Stats), ndiscCacheMetricsCount)
92+
}
93+
for _, expected := range expectedStats {
94+
for header, stats := range netStatFile.Stats {
95+
if header == "" {
96+
t.Fatalf("Found empty metric name")
97+
}
98+
if len(stats) != CPUsCount {
99+
t.Fatalf("NetStat() parsed %d lines with metrics, expected %d", len(stats), CPUsCount)
100+
}
101+
if netStatFile.Filename == expected.Filename {
102+
if expected.Stats[header] == nil {
103+
t.Fatalf("unexpected metric header: %s", header)
104+
}
105+
for cpu, value := range netStatFile.Stats[header] {
106+
if expected.Stats[header][cpu] != value {
107+
t.Fatalf("unexpected value for %s for cpu %d in %s: %d, expected %d", header, cpu, netStatFile.Filename, value, expected.Stats[header][cpu])
108+
}
109+
}
110+
}
111+
}
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)