diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index 93ea3e77935bd..49e7918aa895a 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -35,6 +35,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/net_response" _ "github.com/influxdata/telegraf/plugins/inputs/nginx" _ "github.com/influxdata/telegraf/plugins/inputs/nsq" + _ "github.com/influxdata/telegraf/plugins/inputs/nstat" _ "github.com/influxdata/telegraf/plugins/inputs/ntpq" _ "github.com/influxdata/telegraf/plugins/inputs/passenger" _ "github.com/influxdata/telegraf/plugins/inputs/phpfpm" diff --git a/plugins/inputs/nstat/README.md b/plugins/inputs/nstat/README.md new file mode 100644 index 0000000000000..da2dbe60807e1 --- /dev/null +++ b/plugins/inputs/nstat/README.md @@ -0,0 +1,342 @@ +## Nstat input plugin + +Plugin collects network metrics from ```/proc/net/netstat```, ```/proc/net/snmp``` and ```/proc/net/snmp6``` files + +### Configuration +The plugin firstly tries to read file paths from config values +if it is empty, then it reads from env variables. +* ```PROC_NET_NETSTAT``` +* ```PROC_NET_SNMP``` +* ```PROC_NET_SNMP6``` + +If these variables are also not set, +then it tries to read the proc root from env - ```PROC_ROOT```, +and sets ```/proc``` as a root path if ```PROC_ROOT``` is also empty. + +Then appends default file paths: +* ```/net/netstat``` +* ```/net/snmp``` +* ```/net/snmp6``` + +So if nothing is given, no paths in config and in env vars, the plugin takes the default paths. +* ```/proc/net/netstat``` +* ```/proc/net/snmp``` +* ```/proc/net/snmp6``` + +The sample config file +```toml +[[inputs.nstat]] + ## file paths + ## e.g: /proc/net/netstat, /proc/net/snmp, /proc/net/snmp6 + # proc_net_netstat = "" + # proc_net_snmp = "" + # proc_net_snmp6 = "" + ## dump metrics with 0 values too + # dump_zeros = true +``` + +### Measurements & Fields + +- nstat + - Icmp6InCsumErrors + - Icmp6InDestUnreachs + - Icmp6InEchoReplies + - Icmp6InEchos + - Icmp6InErrors + - Icmp6InGroupMembQueries + - Icmp6InGroupMembReductions + - Icmp6InGroupMembResponses + - Icmp6InMLDv2Reports + - Icmp6InMsgs + - Icmp6InNeighborAdvertisements + - Icmp6InNeighborSolicits + - Icmp6InParmProblems + - Icmp6InPktTooBigs + - Icmp6InRedirects + - Icmp6InRouterAdvertisements + - Icmp6InRouterSolicits + - Icmp6InTimeExcds + - Icmp6OutDestUnreachs + - Icmp6OutEchoReplies + - Icmp6OutEchos + - Icmp6OutErrors + - Icmp6OutGroupMembQueries + - Icmp6OutGroupMembReductions + - Icmp6OutGroupMembResponses + - Icmp6OutMLDv2Reports + - Icmp6OutMsgs + - Icmp6OutNeighborAdvertisements + - Icmp6OutNeighborSolicits + - Icmp6OutParmProblems + - Icmp6OutPktTooBigs + - Icmp6OutRedirects + - Icmp6OutRouterAdvertisements + - Icmp6OutRouterSolicits + - Icmp6OutTimeExcds + - Icmp6OutType133 + - Icmp6OutType135 + - Icmp6OutType143 + - IcmpInAddrMaskReps + - IcmpInAddrMasks + - IcmpInCsumErrors + - IcmpInDestUnreachs + - IcmpInEchoReps + - IcmpInEchos + - IcmpInErrors + - IcmpInMsgs + - IcmpInParmProbs + - IcmpInRedirects + - IcmpInSrcQuenchs + - IcmpInTimeExcds + - IcmpInTimestampReps + - IcmpInTimestamps + - IcmpMsgInType3 + - IcmpMsgOutType3 + - IcmpOutAddrMaskReps + - IcmpOutAddrMasks + - IcmpOutDestUnreachs + - IcmpOutEchoReps + - IcmpOutEchos + - IcmpOutErrors + - IcmpOutMsgs + - IcmpOutParmProbs + - IcmpOutRedirects + - IcmpOutSrcQuenchs + - IcmpOutTimeExcds + - IcmpOutTimestampReps + - IcmpOutTimestamps + - Ip6FragCreates + - Ip6FragFails + - Ip6FragOKs + - Ip6InAddrErrors + - Ip6InBcastOctets + - Ip6InCEPkts + - Ip6InDelivers + - Ip6InDiscards + - Ip6InECT0Pkts + - Ip6InECT1Pkts + - Ip6InHdrErrors + - Ip6InMcastOctets + - Ip6InMcastPkts + - Ip6InNoECTPkts + - Ip6InNoRoutes + - Ip6InOctets + - Ip6InReceives + - Ip6InTooBigErrors + - Ip6InTruncatedPkts + - Ip6InUnknownProtos + - Ip6OutBcastOctets + - Ip6OutDiscards + - Ip6OutForwDatagrams + - Ip6OutMcastOctets + - Ip6OutMcastPkts + - Ip6OutNoRoutes + - Ip6OutOctets + - Ip6OutRequests + - Ip6ReasmFails + - Ip6ReasmOKs + - Ip6ReasmReqds + - Ip6ReasmTimeout + - IpDefaultTTL + - IpExtInBcastOctets + - IpExtInBcastPkts + - IpExtInCEPkts + - IpExtInCsumErrors + - IpExtInECT0Pkts + - IpExtInECT1Pkts + - IpExtInMcastOctets + - IpExtInMcastPkts + - IpExtInNoECTPkts + - IpExtInNoRoutes + - IpExtInOctets + - IpExtInTruncatedPkts + - IpExtOutBcastOctets + - IpExtOutBcastPkts + - IpExtOutMcastOctets + - IpExtOutMcastPkts + - IpExtOutOctets + - IpForwDatagrams + - IpForwarding + - IpFragCreates + - IpFragFails + - IpFragOKs + - IpInAddrErrors + - IpInDelivers + - IpInDiscards + - IpInHdrErrors + - IpInReceives + - IpInUnknownProtos + - IpOutDiscards + - IpOutNoRoutes + - IpOutRequests + - IpReasmFails + - IpReasmOKs + - IpReasmReqds + - IpReasmTimeout + - TcpActiveOpens + - TcpAttemptFails + - TcpCurrEstab + - TcpEstabResets + - TcpExtArpFilter + - TcpExtBusyPollRxPackets + - TcpExtDelayedACKLocked + - TcpExtDelayedACKLost + - TcpExtDelayedACKs + - TcpExtEmbryonicRsts + - TcpExtIPReversePathFilter + - TcpExtListenDrops + - TcpExtListenOverflows + - TcpExtLockDroppedIcmps + - TcpExtOfoPruned + - TcpExtOutOfWindowIcmps + - TcpExtPAWSActive + - TcpExtPAWSEstab + - TcpExtPAWSPassive + - TcpExtPruneCalled + - TcpExtRcvPruned + - TcpExtSyncookiesFailed + - TcpExtSyncookiesRecv + - TcpExtSyncookiesSent + - TcpExtTCPACKSkippedChallenge + - TcpExtTCPACKSkippedFinWait2 + - TcpExtTCPACKSkippedPAWS + - TcpExtTCPACKSkippedSeq + - TcpExtTCPACKSkippedSynRecv + - TcpExtTCPACKSkippedTimeWait + - TcpExtTCPAbortFailed + - TcpExtTCPAbortOnClose + - TcpExtTCPAbortOnData + - TcpExtTCPAbortOnLinger + - TcpExtTCPAbortOnMemory + - TcpExtTCPAbortOnTimeout + - TcpExtTCPAutoCorking + - TcpExtTCPBacklogDrop + - TcpExtTCPChallengeACK + - TcpExtTCPDSACKIgnoredNoUndo + - TcpExtTCPDSACKIgnoredOld + - TcpExtTCPDSACKOfoRecv + - TcpExtTCPDSACKOfoSent + - TcpExtTCPDSACKOldSent + - TcpExtTCPDSACKRecv + - TcpExtTCPDSACKUndo + - TcpExtTCPDeferAcceptDrop + - TcpExtTCPDirectCopyFromBacklog + - TcpExtTCPDirectCopyFromPrequeue + - TcpExtTCPFACKReorder + - TcpExtTCPFastOpenActive + - TcpExtTCPFastOpenActiveFail + - TcpExtTCPFastOpenCookieReqd + - TcpExtTCPFastOpenListenOverflow + - TcpExtTCPFastOpenPassive + - TcpExtTCPFastOpenPassiveFail + - TcpExtTCPFastRetrans + - TcpExtTCPForwardRetrans + - TcpExtTCPFromZeroWindowAdv + - TcpExtTCPFullUndo + - TcpExtTCPHPAcks + - TcpExtTCPHPHits + - TcpExtTCPHPHitsToUser + - TcpExtTCPHystartDelayCwnd + - TcpExtTCPHystartDelayDetect + - TcpExtTCPHystartTrainCwnd + - TcpExtTCPHystartTrainDetect + - TcpExtTCPKeepAlive + - TcpExtTCPLossFailures + - TcpExtTCPLossProbeRecovery + - TcpExtTCPLossProbes + - TcpExtTCPLossUndo + - TcpExtTCPLostRetransmit + - TcpExtTCPMD5NotFound + - TcpExtTCPMD5Unexpected + - TcpExtTCPMTUPFail + - TcpExtTCPMTUPSuccess + - TcpExtTCPMemoryPressures + - TcpExtTCPMinTTLDrop + - TcpExtTCPOFODrop + - TcpExtTCPOFOMerge + - TcpExtTCPOFOQueue + - TcpExtTCPOrigDataSent + - TcpExtTCPPartialUndo + - TcpExtTCPPrequeueDropped + - TcpExtTCPPrequeued + - TcpExtTCPPureAcks + - TcpExtTCPRcvCoalesce + - TcpExtTCPRcvCollapsed + - TcpExtTCPRenoFailures + - TcpExtTCPRenoRecovery + - TcpExtTCPRenoRecoveryFail + - TcpExtTCPRenoReorder + - TcpExtTCPReqQFullDoCookies + - TcpExtTCPReqQFullDrop + - TcpExtTCPRetransFail + - TcpExtTCPSACKDiscard + - TcpExtTCPSACKReneging + - TcpExtTCPSACKReorder + - TcpExtTCPSYNChallenge + - TcpExtTCPSackFailures + - TcpExtTCPSackMerged + - TcpExtTCPSackRecovery + - TcpExtTCPSackRecoveryFail + - TcpExtTCPSackShiftFallback + - TcpExtTCPSackShifted + - TcpExtTCPSchedulerFailed + - TcpExtTCPSlowStartRetrans + - TcpExtTCPSpuriousRTOs + - TcpExtTCPSpuriousRtxHostQueues + - TcpExtTCPSynRetrans + - TcpExtTCPTSReorder + - TcpExtTCPTimeWaitOverflow + - TcpExtTCPTimeouts + - TcpExtTCPToZeroWindowAdv + - TcpExtTCPWantZeroWindowAdv + - TcpExtTCPWinProbe + - TcpExtTW + - TcpExtTWKilled + - TcpExtTWRecycled + - TcpInCsumErrors + - TcpInErrs + - TcpInSegs + - TcpMaxConn + - TcpOutRsts + - TcpOutSegs + - TcpPassiveOpens + - TcpRetransSegs + - TcpRtoAlgorithm + - TcpRtoMax + - TcpRtoMin + - Udp6IgnoredMulti + - Udp6InCsumErrors + - Udp6InDatagrams + - Udp6InErrors + - Udp6NoPorts + - Udp6OutDatagrams + - Udp6RcvbufErrors + - Udp6SndbufErrors + - UdpIgnoredMulti + - UdpInCsumErrors + - UdpInDatagrams + - UdpInErrors + - UdpLite6InCsumErrors + - UdpLite6InDatagrams + - UdpLite6InErrors + - UdpLite6NoPorts + - UdpLite6OutDatagrams + - UdpLite6RcvbufErrors + - UdpLite6SndbufErrors + - UdpLiteIgnoredMulti + - UdpLiteInCsumErrors + - UdpLiteInDatagrams + - UdpLiteInErrors + - UdpLiteNoPorts + - UdpLiteOutDatagrams + - UdpLiteRcvbufErrors + - UdpLiteSndbufErrors + - UdpNoPorts + - UdpOutDatagrams + - UdpRcvbufErrors + - UdpSndbufErrors + +### Tags +- All measurements have the following tags + - host (host of the system) + - name (the type of the metric: snmp, snmp6 or netstat) diff --git a/plugins/inputs/nstat/nstat.go b/plugins/inputs/nstat/nstat.go new file mode 100644 index 0000000000000..407128d97ffcf --- /dev/null +++ b/plugins/inputs/nstat/nstat.go @@ -0,0 +1,232 @@ +package nstat + +import ( + "bytes" + "io/ioutil" + "os" + "strconv" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs" +) + +var ( + zeroByte = []byte("0") + newLineByte = []byte("\n") + colonByte = []byte(":") +) + +// default file paths +const ( + NET_NETSTAT = "/net/netstat" + NET_SNMP = "/net/snmp" + NET_SNMP6 = "/net/snmp6" + NET_PROC = "/proc" +) + +// env variable names +const ( + ENV_NETSTAT = "PROC_NET_NETSTAT" + ENV_SNMP = "PROC_NET_SNMP" + ENV_SNMP6 = "PROC_NET_SNMP6" + ENV_ROOT = "PROC_ROOT" +) + +type Nstat struct { + ProcNetNetstat string `toml:"proc_net_netstat"` + ProcNetSNMP string `toml:"proc_net_snmp"` + ProcNetSNMP6 string `toml:"proc_net_snmp6"` + DumpZeros bool `toml:"dump_zeros"` +} + +var sampleConfig = ` + # file paths + # e.g: /proc/net/netstat, /proc/net/snmp, /proc/net/snmp6 + proc_net_netstat = "" + proc_net_snmp = "" + proc_net_snmp6 = "" + # dump metrics with 0 values too + dump_zeros = true +` + +func (ns *Nstat) Description() string { + return "Collect network metrics from '/proc/net/netstat', '/proc/net/snmp' & '/proc/net/snmp6' files" +} + +func (ns *Nstat) SampleConfig() string { + return sampleConfig +} + +func (ns *Nstat) Gather(acc telegraf.Accumulator) error { + // load paths, get from env if config values are empty + ns.loadPaths() + + netstat, err := ioutil.ReadFile(ns.ProcNetNetstat) + if err != nil { + return err + } + + // collect netstat data + err = ns.gatherNetstat(netstat, acc) + if err != nil { + return err + } + + // collect SNMP data + snmp, err := ioutil.ReadFile(ns.ProcNetSNMP) + if err != nil { + return err + } + err = ns.gatherSNMP(snmp, acc) + if err != nil { + return err + } + + // collect SNMP6 data + snmp6, err := ioutil.ReadFile(ns.ProcNetSNMP6) + if err != nil { + return err + } + err = ns.gatherSNMP6(snmp6, acc) + if err != nil { + return err + } + return nil +} + +func (ns *Nstat) gatherNetstat(data []byte, acc telegraf.Accumulator) error { + metrics, err := loadUglyTable(data, ns.DumpZeros) + if err != nil { + return err + } + tags := map[string]string{ + "name": "netstat", + } + acc.AddFields("nstat", metrics, tags) + return nil +} + +func (ns *Nstat) gatherSNMP(data []byte, acc telegraf.Accumulator) error { + metrics, err := loadUglyTable(data, ns.DumpZeros) + if err != nil { + return err + } + tags := map[string]string{ + "name": "snmp", + } + acc.AddFields("nstat", metrics, tags) + return nil +} + +func (ns *Nstat) gatherSNMP6(data []byte, acc telegraf.Accumulator) error { + metrics, err := loadGoodTable(data, ns.DumpZeros) + if err != nil { + return err + } + tags := map[string]string{ + "name": "snmp6", + } + acc.AddFields("nstat", metrics, tags) + return nil +} + +// loadPaths can be used to read paths firstly from config +// if it is empty then try read from env variables +func (ns *Nstat) loadPaths() { + if ns.ProcNetNetstat == "" { + ns.ProcNetNetstat = proc(ENV_NETSTAT, NET_NETSTAT) + } + if ns.ProcNetSNMP == "" { + ns.ProcNetSNMP = proc(ENV_SNMP, NET_SNMP) + } + if ns.ProcNetSNMP6 == "" { + ns.ProcNetSNMP = proc(ENV_SNMP6, NET_SNMP6) + } +} + +// loadGoodTable can be used to parse string heap that +// headers and values are arranged in right order +func loadGoodTable(table []byte, dumpZeros bool) (map[string]interface{}, error) { + entries := map[string]interface{}{} + fields := bytes.Fields(table) + var value int64 + var err error + // iterate over two values each time + // first value is header, second is value + for i := 0; i < len(fields); i = i + 2 { + // counter is zero + if bytes.Equal(fields[i+1], zeroByte) { + if !dumpZeros { + continue + } else { + entries[string(fields[i])] = int64(0) + continue + } + } + // the counter is not zero, so parse it. + value, err = strconv.ParseInt(string(fields[i+1]), 10, 64) + if err == nil { + entries[string(fields[i])] = value + } + } + return entries, nil +} + +// loadUglyTable can be used to parse string heap that +// the headers and values are splitted with a newline +func loadUglyTable(table []byte, dumpZeros bool) (map[string]interface{}, error) { + entries := map[string]interface{}{} + // split the lines by newline + lines := bytes.Split(table, newLineByte) + var value int64 + var err error + // iterate over lines, take 2 lines each time + // first line contains header names + // second line contains values + for i := 0; i < len(lines); i = i + 2 { + if len(lines[i]) == 0 { + continue + } + headers := bytes.Fields(lines[i]) + prefix := bytes.TrimSuffix(headers[0], colonByte) + metrics := bytes.Fields(lines[i+1]) + + for j := 1; j < len(headers); j++ { + // counter is zero + if bytes.Equal(metrics[j], zeroByte) { + if !dumpZeros { + continue + } else { + entries[string(append(prefix, headers[j]...))] = int64(0) + continue + } + } + // the counter is not zero, so parse it. + value, err = strconv.ParseInt(string(metrics[j]), 10, 64) + if err == nil { + entries[string(append(prefix, headers[j]...))] = value + } + } + } + return entries, nil +} + +// proc can be used to read file paths from env +func proc(env, path string) string { + // try to read full file path + if p := os.Getenv(env); p != "" { + return p + } + // try to read root path, or use default root path + root := os.Getenv(ENV_ROOT) + if root == "" { + root = NET_PROC + } + return root + path +} + +func init() { + inputs.Add("nstat", func() telegraf.Input { + return &Nstat{} + }) +} diff --git a/plugins/inputs/nstat/nstat_test.go b/plugins/inputs/nstat/nstat_test.go new file mode 100644 index 0000000000000..7f4c09ce4d4be --- /dev/null +++ b/plugins/inputs/nstat/nstat_test.go @@ -0,0 +1,56 @@ +package nstat + +import "testing" + +func TestLoadUglyTable(t *testing.T) { + uglyStr := `IpExt: InNoRoutes InTruncatedPkts InMcastPkts InCEPkts + IpExt: 332 433718 0 2660494435` + parsed := map[string]interface{}{ + "IpExtInNoRoutes": int64(332), + "IpExtInTruncatedPkts": int64(433718), + "IpExtInMcastPkts": int64(0), + "IpExtInCEPkts": int64(2660494435), + } + + got, err := loadUglyTable([]byte(uglyStr), true) + if err != nil { + t.Fatal(err) + } + + if len(got) == 0 { + t.Fatalf("want %+v, got %+v", parsed, got) + } + + for key := range parsed { + if parsed[key].(int64) != got[key].(int64) { + t.Fatalf("want %+v, got %+v", parsed[key], got[key]) + } + } +} + +func TestLoadGoodTable(t *testing.T) { + goodStr := `Ip6InReceives 11707 + Ip6InTooBigErrors 0 + Ip6InDelivers 62 + Ip6InMcastOctets 1242966` + + parsed := map[string]interface{}{ + "Ip6InReceives": int64(11707), + "Ip6InTooBigErrors": int64(0), + "Ip6InDelivers": int64(62), + "Ip6InMcastOctets": int64(1242966), + } + got, err := loadGoodTable([]byte(goodStr), true) + if err != nil { + t.Fatal(err) + } + if len(got) == 0 { + t.Fatalf("want %+v, got %+v", parsed, got) + } + + for key := range parsed { + if parsed[key].(int64) != got[key].(int64) { + t.Fatalf("want %+v, got %+v", parsed[key], got[key]) + } + } +}