From 978dee6974d5b9df2e9fcd523045c1747b184cd7 Mon Sep 17 00:00:00 2001 From: Nikos Kakavas Date: Sun, 13 Feb 2022 21:30:36 +0200 Subject: [PATCH] Introduce parsing for `/proc//net/snmp`, `/proc//net/snmp6` and `/proc//net/netstat`. Signed-off-by: Nikos Kakavas --- .gitignore | 2 +- fixtures.ttar | 119 +++++++++++++++++++++++ go.mod | 1 + go.sum | 2 + proc_netstat.go | 223 +++++++++++++++++++++++++++++++++++++++++++ proc_netstat_test.go | 36 +++++++ proc_snmp.go | 188 ++++++++++++++++++++++++++++++++++++ proc_snmp6.go | 200 ++++++++++++++++++++++++++++++++++++++ proc_snmp6_test.go | 35 +++++++ proc_snmp_test.go | 52 ++++++++++ 10 files changed, 857 insertions(+), 1 deletion(-) create mode 100644 proc_netstat.go create mode 100644 proc_netstat_test.go create mode 100644 proc_snmp.go create mode 100644 proc_snmp6.go create mode 100644 proc_snmp6_test.go create mode 100644 proc_snmp_test.go diff --git a/.gitignore b/.gitignore index 25e3659ab..b4d888d7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/fixtures/ +/fixtures/ \ No newline at end of file diff --git a/fixtures.ttar b/fixtures.ttar index 0c4f6a33d..0fe1e08c8 100644 --- a/fixtures.ttar +++ b/fixtures.ttar @@ -156,6 +156,125 @@ 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/26231/net/netstat +Lines: 4 +TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed EmbryonicRsts PruneCalled RcvPruned OfoPruned OutOfWindowIcmps LockDroppedIcmps ArpFilter TW TWRecycled TWKilled PAWSActive PAWSEstab DelayedACKs DelayedACKLocked DelayedACKLost ListenOverflows ListenDrops TCPHPHits TCPPureAcks TCPHPAcks TCPRenoRecovery TCPSackRecovery TCPSACKReneging TCPSACKReorder TCPRenoReorder TCPTSReorder TCPFullUndo TCPPartialUndo TCPDSACKUndo TCPLossUndo TCPLostRetransmit TCPRenoFailures TCPSackFailures TCPLossFailures TCPFastRetrans TCPSlowStartRetrans TCPTimeouts TCPLossProbes TCPLossProbeRecovery TCPRenoRecoveryFail TCPSackRecoveryFail TCPRcvCollapsed TCPDSACKOldSent TCPDSACKOfoSent TCPDSACKRecv TCPDSACKOfoRecv TCPAbortOnData TCPAbortOnClose TCPAbortOnMemory TCPAbortOnTimeout TCPAbortOnLinger TCPAbortFailed TCPMemoryPressures TCPMemoryPressuresChrono TCPSACKDiscard TCPDSACKIgnoredOld TCPDSACKIgnoredNoUndo TCPSpuriousRTOs TCPMD5NotFound TCPMD5Unexpected TCPMD5Failure TCPSackShifted TCPSackMerged TCPSackShiftFallback TCPBacklogDrop PFMemallocDrop TCPMinTTLDrop TCPDeferAcceptDrop IPReversePathFilter TCPTimeWaitOverflow TCPReqQFullDoCookies TCPReqQFullDrop TCPRetransFail TCPRcvCoalesce TCPOFOQueue TCPOFODrop TCPOFOMerge TCPChallengeACK TCPSYNChallenge TCPFastOpenActive TCPFastOpenActiveFail TCPFastOpenPassive TCPFastOpenPassiveFail TCPFastOpenListenOverflow TCPFastOpenCookieReqd TCPFastOpenBlackhole TCPSpuriousRtxHostQueues BusyPollRxPackets TCPAutoCorking TCPFromZeroWindowAdv TCPToZeroWindowAdv TCPWantZeroWindowAdv TCPSynRetrans TCPOrigDataSent TCPHystartTrainDetect TCPHystartTrainCwnd TCPHystartDelayDetect TCPHystartDelayCwnd TCPACKSkippedSynRecv TCPACKSkippedPAWS TCPACKSkippedSeq TCPACKSkippedFinWait2 TCPACKSkippedTimeWait TCPACKSkippedChallenge TCPWinProbe TCPKeepAlive TCPMTUPFail TCPMTUPSuccess TCPWqueueTooBig +TcpExt: 0 0 0 1 0 0 0 0 0 0 83 0 0 0 3640 287 1 7460 0 0 134193 1335 829 0 4 0 1 0 0 0 0 1 19 0 0 0 0 3 0 32 100 4 0 0 0 7460 2421 49 1 62 6 0 23 0 7 0 0 0 0 19 2 0 0 0 0 0 6 0 0 0 0 3 0 0 0 0 92425 65515 0 2421 4 4 0 0 0 0 0 0 0 0 0 10 0 0 0 16 2221 0 0 2 45 0 0 3 0 0 0 0 456 0 0 0 +IpExt: InNoRoutes InTruncatedPkts InMcastPkts OutMcastPkts InBcastPkts OutBcastPkts InOctets OutOctets InMcastOctets OutMcastOctets InBcastOctets OutBcastOctets InCsumErrors InNoECTPkts InECT1Pkts InECT0Pkts InCEPkts ReasmOverlaps +IpExt: 0 0 208 214 118 111 190585481 7512674 26093 25903 14546 13628 0 134215 0 0 0 0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/26231/net/snmp6 +Lines: 92 +Ip6InReceives 92166 +Ip6InHdrErrors 0 +Ip6InTooBigErrors 0 +Ip6InNoRoutes 0 +Ip6InAddrErrors 0 +Ip6InUnknownProtos 0 +Ip6InTruncatedPkts 0 +Ip6InDiscards 0 +Ip6InDelivers 92053 +Ip6OutForwDatagrams 0 +Ip6OutRequests 57502 +Ip6OutDiscards 0 +Ip6OutNoRoutes 169 +Ip6ReasmTimeout 0 +Ip6ReasmReqds 0 +Ip6ReasmOKs 0 +Ip6ReasmFails 0 +Ip6FragOKs 0 +Ip6FragFails 0 +Ip6FragCreates 0 +Ip6InMcastPkts 381 +Ip6OutMcastPkts 148 +Ip6InOctets 113479132 +Ip6OutOctets 9842685 +Ip6InMcastOctets 65971 +Ip6OutMcastOctets 19394 +Ip6InBcastOctets 0 +Ip6OutBcastOctets 0 +Ip6InNoECTPkts 92166 +Ip6InECT1Pkts 0 +Ip6InECT0Pkts 0 +Ip6InCEPkts 0 +Icmp6InMsgs 142 +Icmp6InErrors 0 +Icmp6OutMsgs 58 +Icmp6OutErrors 0 +Icmp6InCsumErrors 0 +Icmp6InDestUnreachs 2 +Icmp6InPktTooBigs 0 +Icmp6InTimeExcds 0 +Icmp6InParmProblems 0 +Icmp6InEchos 0 +Icmp6InEchoReplies 0 +Icmp6InGroupMembQueries 0 +Icmp6InGroupMembResponses 0 +Icmp6InGroupMembReductions 0 +Icmp6InRouterSolicits 0 +Icmp6InRouterAdvertisements 111 +Icmp6InNeighborSolicits 26 +Icmp6InNeighborAdvertisements 1 +Icmp6InRedirects 0 +Icmp6InMLDv2Reports 2 +Icmp6OutDestUnreachs 0 +Icmp6OutPktTooBigs 0 +Icmp6OutTimeExcds 0 +Icmp6OutParmProblems 0 +Icmp6OutEchos 0 +Icmp6OutEchoReplies 0 +Icmp6OutGroupMembQueries 0 +Icmp6OutGroupMembResponses 0 +Icmp6OutGroupMembReductions 0 +Icmp6OutRouterSolicits 2 +Icmp6OutRouterAdvertisements 0 +Icmp6OutNeighborSolicits 5 +Icmp6OutNeighborAdvertisements 26 +Icmp6OutRedirects 0 +Icmp6OutMLDv2Reports 25 +Icmp6InType1 2 +Icmp6InType134 111 +Icmp6InType135 26 +Icmp6InType136 1 +Icmp6InType143 2 +Icmp6OutType133 2 +Icmp6OutType135 5 +Icmp6OutType136 26 +Icmp6OutType143 25 +Udp6InDatagrams 2016 +Udp6NoPorts 0 +Udp6InErrors 0 +Udp6OutDatagrams 1546 +Udp6RcvbufErrors 0 +Udp6SndbufErrors 0 +Udp6InCsumErrors 0 +Udp6IgnoredMulti 12 +UdpLite6InDatagrams 0 +UdpLite6NoPorts 0 +UdpLite6InErrors 0 +UdpLite6OutDatagrams 0 +UdpLite6RcvbufErrors 0 +UdpLite6SndbufErrors 0 +UdpLite6InCsumErrors 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_netstat.go b/proc_netstat.go new file mode 100644 index 000000000..418f1f514 --- /dev/null +++ b/proc_netstat.go @@ -0,0 +1,223 @@ +// 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" + "bytes" + "fmt" + "io" + "strconv" + "strings" + + "github.com/mitchellh/mapstructure" + "github.com/prometheus/procfs/internal/util" +) + +// ProcNetstat models the content of /proc//net/netstat. +type ProcNetstat struct { + // The process ID. + PID int + TcpExt + IpExt +} + +type TcpExt struct { + SyncookiesSent float64 + SyncookiesRecv float64 + SyncookiesFailed float64 + EmbryonicRsts float64 + PruneCalled float64 + RcvPruned float64 + OfoPruned float64 + OutOfWindowIcmps float64 + LockDroppedIcmps float64 + ArpFilter float64 + TW float64 + TWRecycled float64 + TWKilled float64 + PAWSActive float64 + PAWSEstab float64 + DelayedACKs float64 + DelayedACKLocked float64 + DelayedACKLost float64 + ListenOverflows float64 + ListenDrops float64 + TCPHPHits float64 + TCPPureAcks float64 + TCPHPAcks float64 + TCPRenoRecovery float64 + TCPSackRecovery float64 + TCPSACKReneging float64 + TCPSACKReorder float64 + TCPRenoReorder float64 + TCPTSReorder float64 + TCPFullUndo float64 + TCPPartialUndo float64 + TCPDSACKUndo float64 + TCPLossUndo float64 + TCPLostRetransmit float64 + TCPRenoFailures float64 + TCPSackFailures float64 + TCPLossFailures float64 + TCPFastRetrans float64 + TCPSlowStartRetrans float64 + TCPTimeouts float64 + TCPLossProbes float64 + TCPLossProbeRecovery float64 + TCPRenoRecoveryFail float64 + TCPSackRecoveryFail float64 + TCPRcvCollapsed float64 + TCPDSACKOldSent float64 + TCPDSACKOfoSent float64 + TCPDSACKRecv float64 + TCPDSACKOfoRecv float64 + TCPAbortOnData float64 + TCPAbortOnClose float64 + TCPAbortOnMemory float64 + TCPAbortOnTimeout float64 + TCPAbortOnLinger float64 + TCPAbortFailed float64 + TCPMemoryPressures float64 + TCPMemoryPressuresChrono float64 + TCPSACKDiscard float64 + TCPDSACKIgnoredOld float64 + TCPDSACKIgnoredNoUndo float64 + TCPSpuriousRTOs float64 + TCPMD5NotFound float64 + TCPMD5Unexpected float64 + TCPMD5Failure float64 + TCPSackShifted float64 + TCPSackMerged float64 + TCPSackShiftFallback float64 + TCPBacklogDrop float64 + PFMemallocDrop float64 + TCPMinTTLDrop float64 + TCPDeferAcceptDrop float64 + IPReversePathFilter float64 + TCPTimeWaitOverflow float64 + TCPReqQFullDoCookies float64 + TCPReqQFullDrop float64 + TCPRetransFail float64 + TCPRcvCoalesce float64 + TCPOFOQueue float64 + TCPOFODrop float64 + TCPOFOMerge float64 + TCPChallengeACK float64 + TCPSYNChallenge float64 + TCPFastOpenActive float64 + TCPFastOpenActiveFail float64 + TCPFastOpenPassive float64 + TCPFastOpenPassiveFail float64 + TCPFastOpenListenOverflow float64 + TCPFastOpenCookieReqd float64 + TCPFastOpenBlackhole float64 + TCPSpuriousRtxHostQueues float64 + BusyPollRxPackets float64 + TCPAutoCorking float64 + TCPFromZeroWindowAdv float64 + TCPToZeroWindowAdv float64 + TCPWantZeroWindowAdv float64 + TCPSynRetrans float64 + TCPOrigDataSent float64 + TCPHystartTrainDetect float64 + TCPHystartTrainCwnd float64 + TCPHystartDelayDetect float64 + TCPHystartDelayCwnd float64 + TCPACKSkippedSynRecv float64 + TCPACKSkippedPAWS float64 + TCPACKSkippedSeq float64 + TCPACKSkippedFinWait2 float64 + TCPACKSkippedTimeWait float64 + TCPACKSkippedChallenge float64 + TCPWinProbe float64 + TCPKeepAlive float64 + TCPMTUPFail float64 + TCPMTUPSuccess float64 + TCPWqueueTooBig float64 +} + +type IpExt struct { + InNoRoutes float64 + InTruncatedPkts float64 + InMcastPkts float64 + OutMcastPkts float64 + InBcastPkts float64 + OutBcastPkts float64 + InOctets float64 + OutOctets float64 + InMcastOctets float64 + OutMcastOctets float64 + InBcastOctets float64 + OutBcastOctets float64 + InCsumErrors float64 + InNoECTPkts float64 + InECT1Pkts float64 + InECT0Pkts float64 + InCEPkts float64 + ReasmOverlaps float64 +} + +func (p Proc) Netstat() (ProcNetstat, error) { + filename := p.path("net/netstat") + procNetstat := ProcNetstat{PID: p.PID} + + data, err := util.ReadFileNoStat(filename) + if err != nil { + return procNetstat, err + } + + netStats, err := parseNetstat(bytes.NewReader(data), filename) + if err != nil { + return procNetstat, err + } + + mapStructureErr := mapstructure.Decode(netStats, &procNetstat) + if mapStructureErr != nil { + return procNetstat, mapStructureErr + } + + return procNetstat, nil +} + +// parseNetstat parses the metrics from proc//net/netstat file +// and returns a map contains those metrics (e.g. {"TcpExt": {"SyncookiesSent": 0}}). +func parseNetstat(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_netstat_test.go b/proc_netstat_test.go new file mode 100644 index 000000000..34fb8d7f1 --- /dev/null +++ b/proc_netstat_test.go @@ -0,0 +1,36 @@ +package procfs + +import "testing" + +func TestProcNetstat(t *testing.T) { + p, err := getProcFixtures(t).Proc(26231) + if err != nil { + t.Fatal(err) + } + + procNetstat, err := p.Netstat() + if err != nil { + t.Fatal(err) + } + + for _, test := range []struct { + name string + want float64 + have float64 + }{ + {name: "pid", want: 26231, have: float64(procNetstat.PID)}, + {name: "TcpExt:SyncookiesSent", want: 0, have: procNetstat.TcpExt.SyncookiesSent}, + {name: "TcpExt:EmbryonicRsts", want: 1, have: procNetstat.TcpExt.EmbryonicRsts}, + {name: "TcpExt:TW", want: 83, have: procNetstat.TcpExt.TW}, + {name: "TcpExt:PAWSEstab", want: 3640, have: procNetstat.TcpExt.PAWSEstab}, + + {name: "IpExt:InNoRoutes", want: 0, have: procNetstat.IpExt.InNoRoutes}, + {name: "TcpExt:InMcastPkts", want: 208, have: procNetstat.IpExt.InMcastPkts}, + {name: "TcpExt:OutMcastPkts", want: 214, have: procNetstat.IpExt.OutMcastPkts}, + } { + if test.want != test.have { + t.Errorf("want %s %f, have %f", test.name, test.want, test.have) + } + } + +} diff --git a/proc_snmp.go b/proc_snmp.go new file mode 100644 index 000000000..ef78321d2 --- /dev/null +++ b/proc_snmp.go @@ -0,0 +1,188 @@ +// 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" + "bytes" + "fmt" + "io" + "strconv" + "strings" + + "github.com/mitchellh/mapstructure" + "github.com/prometheus/procfs/internal/util" +) + +// 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} + + data, err := util.ReadFileNoStat(filename) + if err != nil { + return procSnmp, err + } + + netStats, err := parseSnmp(bytes.NewReader(data), 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 parseSnmp(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_snmp6.go b/proc_snmp6.go new file mode 100644 index 000000000..54d3a3d90 --- /dev/null +++ b/proc_snmp6.go @@ -0,0 +1,200 @@ +// 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" + "bytes" + "errors" + "io" + "os" + "strconv" + "strings" + + "github.com/mitchellh/mapstructure" + "github.com/prometheus/procfs/internal/util" +) + +// ProcSnmp6 models the content of /proc//net/snmp6. +type ProcSnmp6 struct { + // The process ID. + PID int + Ip6 + Icmp6 + Udp6 + Udp6Lite +} + +type Ip6 struct { + InReceives float64 + InHdrErrors float64 + InTooBigErrors float64 + InNoRoutes float64 + InAddrErrors float64 + InUnknownProtos float64 + InTruncatedPkts float64 + InDiscards float64 + InDelivers float64 + OutForwDatagrams float64 + OutRequests float64 + OutDiscards float64 + OutNoRoutes float64 + ReasmTimeout float64 + ReasmReqds float64 + ReasmOKs float64 + ReasmFails float64 + FragOKs float64 + FragFails float64 + FragCreates float64 + InMcastPkts float64 + OutMcastPkts float64 + InOctets float64 + OutOctets float64 + InMcastOctets float64 + OutMcastOctets float64 + InBcastOctets float64 + OutBcastOctets float64 + InNoECTPkts float64 + InECT1Pkts float64 + InECT0Pkts float64 + InCEPkts float64 +} + +type Icmp6 struct { + InMsgs float64 + InErrors float64 + OutMsgs float64 + OutErrors float64 + InCsumErrors float64 + InDestUnreachs float64 + InPktTooBigs float64 + InTimeExcds float64 + InParmProblems float64 + InEchos float64 + InEchoReplies float64 + InGroupMembQueries float64 + InGroupMembResponses float64 + InGroupMembReductions float64 + InRouterSolicits float64 + InRouterAdvertisements float64 + InNeighborSolicits float64 + InNeighborAdvertisements float64 + InRedirects float64 + InMLDv2Reports float64 + OutDestUnreachs float64 + OutPktTooBigs float64 + OutTimeExcds float64 + OutParmProblems float64 + OutEchos float64 + OutEchoReplies float64 + OutGroupMembQueries float64 + OutGroupMembResponses float64 + OutGroupMembReductions float64 + OutRouterSolicits float64 + OutRouterAdvertisements float64 + OutNeighborSolicits float64 + OutNeighborAdvertisements float64 + OutRedirects float64 + OutMLDv2Reports float64 + InType1 float64 + InType134 float64 + InType135 float64 + InType136 float64 + InType143 float64 + OutType133 float64 + OutType135 float64 + OutType136 float64 + OutType143 float64 +} + +type Udp6 struct { + InDatagrams float64 + NoPorts float64 + InErrors float64 + OutDatagrams float64 + RcvbufErrors float64 + SndbufErrors float64 + InCsumErrors float64 + IgnoredMulti float64 +} + +type Udp6Lite struct { + InDatagrams float64 + NoPorts float64 + InErrors float64 + OutDatagrams float64 + RcvbufErrors float64 + SndbufErrors float64 + InCsumErrors float64 +} + +func (p Proc) Snmp6() (ProcSnmp6, error) { + filename := p.path("net/snmp6") + procSnmp6 := ProcSnmp6{PID: p.PID} + + data, err := util.ReadFileNoStat(filename) + if err != nil { + // On systems with IPv6 disabled, this file won't exist. + // Do nothing. + if errors.Is(err, os.ErrNotExist) { + return procSnmp6, nil + } + + return procSnmp6, err + } + + netStats, err := parseSNMP6Stats(bytes.NewReader(data)) + if err != nil { + return procSnmp6, err + } + + mapStructureErr := mapstructure.Decode(netStats, &procSnmp6) + if mapStructureErr != nil { + return procSnmp6, mapStructureErr + } + + return procSnmp6, nil + +} + +// parseSnmp6 parses the metrics from proc//net/snmp6 file +// and returns a map contains those metrics. +func parseSNMP6Stats(r io.Reader) (map[string]map[string]float64, error) { + var ( + netStats = map[string]map[string]float64{} + scanner = bufio.NewScanner(r) + ) + + for scanner.Scan() { + stat := strings.Fields(scanner.Text()) + if len(stat) < 2 { + continue + } + // Expect to have "6" in metric name, skip line otherwise + if sixIndex := strings.Index(stat[0], "6"); sixIndex != -1 { + protocol := stat[0][:sixIndex+1] + name := stat[0][sixIndex+1:] + if _, present := netStats[protocol]; !present { + netStats[protocol] = map[string]float64{} + } + var err error + netStats[protocol][name], err = strconv.ParseFloat(stat[1], 64) + if err != nil { + return nil, err + } + } + } + + return netStats, scanner.Err() +} diff --git a/proc_snmp6_test.go b/proc_snmp6_test.go new file mode 100644 index 000000000..11d4b53b3 --- /dev/null +++ b/proc_snmp6_test.go @@ -0,0 +1,35 @@ +package procfs + +import "testing" + +func TestProcSnmp6(t *testing.T) { + p, err := getProcFixtures(t).Proc(26231) + if err != nil { + t.Fatal(err) + } + + procSnmp6, err := p.Snmp6() + if err != nil { + t.Fatal(err) + } + + for _, test := range []struct { + name string + want float64 + have float64 + }{ + {name: "pid", want: 26231, have: float64(procSnmp6.PID)}, + {name: "Ip6InReceives", want: 92166, have: procSnmp6.Ip6.InReceives}, + {name: "Ip6InDelivers", want: 92053, have: procSnmp6.Ip6.InDelivers}, + {name: "Ip6OutNoRoutes", want: 169, have: procSnmp6.Ip6.OutNoRoutes}, + {name: "Ip6InOctets", want: 113479132, have: procSnmp6.Ip6.InOctets}, + {name: "Icmp6InMsgs", want: 142, have: procSnmp6.Icmp6.InMsgs}, + {name: "Udp6InDatagrams", want: 2016, have: procSnmp6.Udp6.InDatagrams}, + {name: "UdpLite6InDatagrams", want: 0, have: procSnmp6.Udp6Lite.InDatagrams}, + } { + if test.want != test.have { + t.Errorf("want %s %f, have %f", test.name, test.want, test.have) + } + } + +} 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) + } + } + +}