diff --git a/.gitignore b/.gitignore index 25e3659ab..2ff8075aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /fixtures/ +.idea diff --git a/fixtures.ttar b/fixtures.ttar index 0c4f6a33d..8889ea3b9 100644 --- a/fixtures.ttar +++ b/fixtures.ttar @@ -156,6 +156,22 @@ Inter-| Receive | Transmit eth0: 438 5 0 0 0 0 0 0 648 8 0 0 0 0 0 0 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/26231/net/snmp +Lines: 12 +Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates +Ip: 2 64 594223 0 1 0 0 0 593186 547253 20 231 0 0 0 0 0 0 0 +Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps +Icmp: 45 1 0 45 0 0 0 0 0 0 0 0 0 0 50 0 50 0 0 0 0 0 0 0 0 0 0 +IcmpMsg: InType3 OutType3 +IcmpMsg: 45 50 +Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors +Tcp: 1 200 120000 -1 1103 9 8 51 15 653161 594855 348 98 1038 0 +Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti +Udp: 10179 50 0 9846 0 0 0 58 +UdpLite: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti +UdpLite: 0 0 0 0 0 0 0 0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/proc/26231/ns Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/go.mod b/go.mod index ba6681f52..ff171bb43 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( github.com/google/go-cmp v0.5.4 + github.com/mitchellh/mapstructure v1.4.3 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c ) diff --git a/go.sum b/go.sum index 7ceaf56b7..ea3f41b44 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= diff --git a/proc_snmp.go b/proc_snmp.go new file mode 100644 index 000000000..513a141c0 --- /dev/null +++ b/proc_snmp.go @@ -0,0 +1,187 @@ +// Copyright 2022 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package procfs + +import ( + "bufio" + "fmt" + "github.com/mitchellh/mapstructure" + "io" + "os" + "strconv" + "strings" +) + +// ProcSnmp models the content of /proc//net/snmp. +type ProcSnmp struct { + // The process ID. + PID int + Ip + Icmp + IcmpMsg + Tcp + Udp + UdpLite +} + +type Ip struct { + Forwarding float64 + DefaultTTL float64 + InReceives float64 + InHdrErrors float64 + InAddrErrors float64 + ForwDatagrams float64 + InUnknownProtos float64 + InDiscards float64 + InDelivers float64 + OutRequests float64 + OutDiscards float64 + OutNoRoutes float64 + ReasmTimeout float64 + ReasmReqds float64 + ReasmOKs float64 + ReasmFails float64 + FragOKs float64 + FragFails float64 + FragCreates float64 +} + +type Icmp struct { + InMsgs float64 + InErrors float64 + InCsumErrors float64 + InDestUnreachs float64 + InTimeExcds float64 + InParmProbs float64 + InSrcQuenchs float64 + InRedirects float64 + InEchos float64 + InEchoReps float64 + InTimestamps float64 + InTimestampReps float64 + InAddrMasks float64 + InAddrMaskReps float64 + OutMsgs float64 + OutErrors float64 + OutDestUnreachs float64 + OutTimeExcds float64 + OutParmProbs float64 + OutSrcQuenchs float64 + OutRedirects float64 + OutEchos float64 + OutEchoReps float64 + OutTimestamps float64 + OutTimestampReps float64 + OutAddrMasks float64 + OutAddrMaskReps float64 +} + +type IcmpMsg struct { + InType3 float64 + OutType3 float64 +} + +type Tcp struct { + RtoAlgorithm float64 + RtoMin float64 + RtoMax float64 + MaxConn float64 + ActiveOpens float64 + PassiveOpens float64 + AttemptFails float64 + EstabResets float64 + CurrEstab float64 + InSegs float64 + OutSegs float64 + RetransSegs float64 + InErrs float64 + OutRsts float64 + InCsumErrors float64 +} + +type Udp struct { + InDatagrams float64 + NoPorts float64 + InErrors float64 + OutDatagrams float64 + RcvbufErrors float64 + SndbufErrors float64 + InCsumErrors float64 + IgnoredMulti float64 +} + +type UdpLite struct { + InDatagrams float64 + NoPorts float64 + InErrors float64 + OutDatagrams float64 + RcvbufErrors float64 + SndbufErrors float64 + InCsumErrors float64 + IgnoredMulti float64 +} + +func (p Proc) Snmp() (ProcSnmp, error) { + filename := p.path("net/snmp") + procSnmp := ProcSnmp{PID: p.PID} + + file, err := os.Open(filename) + if err != nil { + return procSnmp, err + } + defer file.Close() + + netStats, err := parseNetStats(file, filename) + if err != nil { + return procSnmp, err + } + + mapStructureErr := mapstructure.Decode(netStats, &procSnmp) + if mapStructureErr != nil { + return procSnmp, mapStructureErr + } + + return procSnmp, nil +} + +// parseSnmp parses the metrics from proc//net/snmp file +// and returns a map contains those metrics (e.g. {"Ip": {"Forwarding": 2}} +func parseNetStats(r io.Reader, fileName string) (map[string]map[string]float64, error) { + var ( + netStats = map[string]map[string]float64{} + scanner = bufio.NewScanner(r) + ) + + for scanner.Scan() { + nameParts := strings.Split(scanner.Text(), " ") + scanner.Scan() + valueParts := strings.Split(scanner.Text(), " ") + // Remove trailing :. + protocol := nameParts[0][:len(nameParts[0])-1] + netStats[protocol] = map[string]float64{} + if len(nameParts) != len(valueParts) { + return nil, fmt.Errorf("mismatch field count mismatch in %s: %s", + fileName, protocol) + } + for i := 1; i < len(nameParts); i++ { + var err error + netStats[protocol][nameParts[i]], err = strconv.ParseFloat(valueParts[i], 64) + if err != nil { + return nil, err + } + } + } + + return netStats, scanner.Err() +} diff --git a/proc_snmp_test.go b/proc_snmp_test.go new file mode 100644 index 000000000..73c8a69f4 --- /dev/null +++ b/proc_snmp_test.go @@ -0,0 +1,52 @@ +// Copyright 2022 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package procfs + +import "testing" + +func TestProcSnmp(t *testing.T) { + p, err := getProcFixtures(t).Proc(26231) + if err != nil { + t.Fatal(err) + } + + procSnmp, err := p.Snmp() + if err != nil { + t.Fatal(err) + } + + for _, test := range []struct { + name string + want float64 + have float64 + }{ + {name: "pid", want: 26231, have: float64(procSnmp.PID)}, + {name: "IP:Forwarding", want: 2, have: procSnmp.Ip.Forwarding}, + {name: "IP:DefaultTTL", want: 64, have: procSnmp.Ip.DefaultTTL}, + {name: "Icmp:InMsgs", want: 45, have: procSnmp.Icmp.InMsgs}, + {name: "IcmpMsg:InType3", want: 45, have: procSnmp.IcmpMsg.InType3}, + {name: "IcmpMsg:OutType3", want: 50, have: procSnmp.IcmpMsg.OutType3}, + {name: "TCP:RtoAlgorithm", want: 1, have: procSnmp.Tcp.RtoAlgorithm}, + {name: "TCP:RtoMin", want: 200, have: procSnmp.Tcp.RtoMin}, + {name: "Udp:InDatagrams", want: 10179, have: procSnmp.Udp.InDatagrams}, + {name: "Udp:NoPorts", want: 50, have: procSnmp.Udp.NoPorts}, + {name: "UdpLite:InDatagrams", want: 0, have: procSnmp.UdpLite.NoPorts}, + {name: "UdpLite:NoPorts", want: 0, have: procSnmp.UdpLite.NoPorts}, + } { + if test.want != test.have { + t.Errorf("want %s %f, have %f", test.name, test.want, test.have) + } + } + +}