Use gosmi for SNMP traps (#9343)
Use gosmi for snmp_trap plugin Co-authored-by: Logan McNaughton <logan@bacoosta.com> Co-authored-by: reimda <reimda@users.noreply.github.com>
This commit is contained in:
parent
daec1040c6
commit
28fbdd8fba
|
|
@ -19,6 +19,7 @@ following works:
|
||||||
- github.com/Shopify/sarama [MIT License](https://github.com/Shopify/sarama/blob/master/LICENSE)
|
- github.com/Shopify/sarama [MIT License](https://github.com/Shopify/sarama/blob/master/LICENSE)
|
||||||
- github.com/StackExchange/wmi [MIT License](https://github.com/StackExchange/wmi/blob/master/LICENSE)
|
- github.com/StackExchange/wmi [MIT License](https://github.com/StackExchange/wmi/blob/master/LICENSE)
|
||||||
- github.com/aerospike/aerospike-client-go [Apache License 2.0](https://github.com/aerospike/aerospike-client-go/blob/master/LICENSE)
|
- github.com/aerospike/aerospike-client-go [Apache License 2.0](https://github.com/aerospike/aerospike-client-go/blob/master/LICENSE)
|
||||||
|
- github.com/alecthomas/participle [MIT License](https://github.com/alecthomas/participle/blob/master/COPYING)
|
||||||
- github.com/alecthomas/units [MIT License](https://github.com/alecthomas/units/blob/master/COPYING)
|
- github.com/alecthomas/units [MIT License](https://github.com/alecthomas/units/blob/master/COPYING)
|
||||||
- github.com/aliyun/alibaba-cloud-sdk-go [Apache License 2.0](https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/LICENSE)
|
- github.com/aliyun/alibaba-cloud-sdk-go [Apache License 2.0](https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/LICENSE)
|
||||||
- github.com/amir/raidman [The Unlicense](https://github.com/amir/raidman/blob/master/UNLICENSE)
|
- github.com/amir/raidman [The Unlicense](https://github.com/amir/raidman/blob/master/UNLICENSE)
|
||||||
|
|
@ -188,6 +189,7 @@ following works:
|
||||||
- github.com/signalfx/golib [Apache License 2.0](https://github.com/signalfx/golib/blob/master/LICENSE)
|
- github.com/signalfx/golib [Apache License 2.0](https://github.com/signalfx/golib/blob/master/LICENSE)
|
||||||
- github.com/signalfx/sapm-proto [Apache License 2.0](https://github.com/signalfx/sapm-proto/blob/master/LICENSE)
|
- github.com/signalfx/sapm-proto [Apache License 2.0](https://github.com/signalfx/sapm-proto/blob/master/LICENSE)
|
||||||
- github.com/sirupsen/logrus [MIT License](https://github.com/sirupsen/logrus/blob/master/LICENSE)
|
- github.com/sirupsen/logrus [MIT License](https://github.com/sirupsen/logrus/blob/master/LICENSE)
|
||||||
|
- github.com/sleepinggenius2/gosmi [MIT License](https://github.com/sleepinggenius2/gosmi/blob/master/LICENSE)
|
||||||
- github.com/snowflakedb/gosnowflake [Apache License 2.0](https://github.com/snowflakedb/gosnowflake/blob/master/LICENSE)
|
- github.com/snowflakedb/gosnowflake [Apache License 2.0](https://github.com/snowflakedb/gosnowflake/blob/master/LICENSE)
|
||||||
- github.com/streadway/amqp [BSD 2-Clause "Simplified" License](https://github.com/streadway/amqp/blob/master/LICENSE)
|
- github.com/streadway/amqp [BSD 2-Clause "Simplified" License](https://github.com/streadway/amqp/blob/master/LICENSE)
|
||||||
- github.com/stretchr/objx [MIT License](https://github.com/stretchr/objx/blob/master/LICENSE)
|
- github.com/stretchr/objx [MIT License](https://github.com/stretchr/objx/blob/master/LICENSE)
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -112,6 +112,7 @@ require (
|
||||||
github.com/shopspring/decimal v0.0.0-20200105231215-408a2507e114 // indirect
|
github.com/shopspring/decimal v0.0.0-20200105231215-408a2507e114 // indirect
|
||||||
github.com/signalfx/golib/v3 v3.3.0
|
github.com/signalfx/golib/v3 v3.3.0
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.7.0
|
||||||
|
github.com/sleepinggenius2/gosmi v0.4.3
|
||||||
github.com/snowflakedb/gosnowflake v1.5.0
|
github.com/snowflakedb/gosnowflake v1.5.0
|
||||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271
|
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
|
|
|
||||||
8
go.sum
8
go.sum
|
|
@ -164,6 +164,12 @@ github.com/aerospike/aerospike-client-go v1.27.0/go.mod h1:zj8LBEnWBDOVEIJt8LvaR
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||||
|
github.com/alecthomas/go-thrift v0.0.0-20170109061633-7914173639b2/go.mod h1:CxCgO+NdpMdi9SsTlGbc0W+/UNxO3I0AabOEJZ3w61w=
|
||||||
|
github.com/alecthomas/kong v0.2.1/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
|
||||||
|
github.com/alecthomas/participle v0.4.1 h1:P2PJWzwrSpuCWXKnzqvw0b0phSfH1kJo4p2HvLynVsI=
|
||||||
|
github.com/alecthomas/participle v0.4.1/go.mod h1:T8u4bQOSMwrkTWOSyt8/jSFPEnRtd0FKFMjVfYBlqPs=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20210301060118-828286944d6a/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
|
@ -1370,6 +1376,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/sleepinggenius2/gosmi v0.4.3 h1:99Zwzy1Cvgsh396sw07oR2G4ab88ILGZFMxSlGWnR6o=
|
||||||
|
github.com/sleepinggenius2/gosmi v0.4.3/go.mod h1:l8OniPmd3bJzw0MXP2/qh7AhP/e+bTY2CNivIhsnDT0=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,10 @@ information.
|
||||||
## 1024. See README.md for details
|
## 1024. See README.md for details
|
||||||
##
|
##
|
||||||
# service_address = "udp://:162"
|
# service_address = "udp://:162"
|
||||||
|
##
|
||||||
|
## Path to mib files
|
||||||
|
# path = ["/usr/share/snmp/mibs"]
|
||||||
|
##
|
||||||
## Timeout running snmptranslate command
|
## Timeout running snmptranslate command
|
||||||
# timeout = "5s"
|
# timeout = "5s"
|
||||||
## Snmp version
|
## Snmp version
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,25 @@
|
||||||
package snmp_trap
|
package snmp_trap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os/exec"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/config"
|
"github.com/influxdata/telegraf/config"
|
||||||
"github.com/influxdata/telegraf/internal"
|
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
"github.com/sleepinggenius2/gosmi"
|
||||||
|
"github.com/sleepinggenius2/gosmi/types"
|
||||||
|
|
||||||
"github.com/gosnmp/gosnmp"
|
"github.com/gosnmp/gosnmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultTimeout = config.Duration(time.Second * 5)
|
var defaultTimeout = config.Duration(time.Second * 5)
|
||||||
|
|
||||||
type execer func(config.Duration, string, ...string) ([]byte, error)
|
|
||||||
|
|
||||||
type mibEntry struct {
|
type mibEntry struct {
|
||||||
mibName string
|
mibName string
|
||||||
oidText string
|
oidText string
|
||||||
|
|
@ -32,6 +29,7 @@ type SnmpTrap struct {
|
||||||
ServiceAddress string `toml:"service_address"`
|
ServiceAddress string `toml:"service_address"`
|
||||||
Timeout config.Duration `toml:"timeout"`
|
Timeout config.Duration `toml:"timeout"`
|
||||||
Version string `toml:"version"`
|
Version string `toml:"version"`
|
||||||
|
Path []string `toml:"path"`
|
||||||
|
|
||||||
// Settings for version 3
|
// Settings for version 3
|
||||||
// Values: "noAuthNoPriv", "authNoPriv", "authPriv"
|
// Values: "noAuthNoPriv", "authNoPriv", "authPriv"
|
||||||
|
|
@ -44,19 +42,15 @@ type SnmpTrap struct {
|
||||||
PrivProtocol string `toml:"priv_protocol"`
|
PrivProtocol string `toml:"priv_protocol"`
|
||||||
PrivPassword string `toml:"priv_password"`
|
PrivPassword string `toml:"priv_password"`
|
||||||
|
|
||||||
acc telegraf.Accumulator
|
acc telegraf.Accumulator
|
||||||
listener *gosnmp.TrapListener
|
listener *gosnmp.TrapListener
|
||||||
timeFunc func() time.Time
|
timeFunc func() time.Time
|
||||||
errCh chan error
|
lookupFunc func(string) (mibEntry, error)
|
||||||
|
errCh chan error
|
||||||
|
|
||||||
makeHandlerWrapper func(gosnmp.TrapHandlerFunc) gosnmp.TrapHandlerFunc
|
makeHandlerWrapper func(gosnmp.TrapHandlerFunc) gosnmp.TrapHandlerFunc
|
||||||
|
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
cacheLock sync.Mutex
|
|
||||||
cache map[string]mibEntry
|
|
||||||
|
|
||||||
execCmd execer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
|
|
@ -68,6 +62,10 @@ var sampleConfig = `
|
||||||
## 1024. See README.md for details
|
## 1024. See README.md for details
|
||||||
##
|
##
|
||||||
# service_address = "udp://:162"
|
# service_address = "udp://:162"
|
||||||
|
##
|
||||||
|
## Path to mib files
|
||||||
|
# path = ["/usr/share/snmp/mibs"]
|
||||||
|
##
|
||||||
## Timeout running snmptranslate command
|
## Timeout running snmptranslate command
|
||||||
# timeout = "5s"
|
# timeout = "5s"
|
||||||
## Snmp version, defaults to 2c
|
## Snmp version, defaults to 2c
|
||||||
|
|
@ -104,6 +102,7 @@ func init() {
|
||||||
inputs.Add("snmp_trap", func() telegraf.Input {
|
inputs.Add("snmp_trap", func() telegraf.Input {
|
||||||
return &SnmpTrap{
|
return &SnmpTrap{
|
||||||
timeFunc: time.Now,
|
timeFunc: time.Now,
|
||||||
|
lookupFunc: lookup,
|
||||||
ServiceAddress: "udp://:162",
|
ServiceAddress: "udp://:162",
|
||||||
Timeout: defaultTimeout,
|
Timeout: defaultTimeout,
|
||||||
Version: "2c",
|
Version: "2c",
|
||||||
|
|
@ -111,20 +110,50 @@ func init() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func realExecCmd(timeout config.Duration, arg0 string, args ...string) ([]byte, error) {
|
func (s *SnmpTrap) Init() error {
|
||||||
cmd := exec.Command(arg0, args...)
|
// must init, append path for each directory, load module for every file
|
||||||
var out bytes.Buffer
|
// or gosmi will fail without saying why
|
||||||
cmd.Stdout = &out
|
gosmi.Init()
|
||||||
err := internal.RunTimeout(cmd, time.Duration(timeout))
|
err := s.getMibsPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
s.Log.Errorf("Could not get path %v", err)
|
||||||
}
|
}
|
||||||
return out.Bytes(), nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SnmpTrap) Init() error {
|
func (s *SnmpTrap) getMibsPath() error {
|
||||||
s.cache = map[string]mibEntry{}
|
var folders []string
|
||||||
s.execCmd = realExecCmd
|
for _, mibPath := range s.Path {
|
||||||
|
gosmi.AppendPath(mibPath)
|
||||||
|
folders = append(folders, mibPath)
|
||||||
|
err := filepath.Walk(mibPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if info.Mode()&os.ModeSymlink != 0 {
|
||||||
|
s, _ := os.Readlink(path)
|
||||||
|
folders = append(folders, s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Filepath could not be walked %v", err)
|
||||||
|
}
|
||||||
|
for _, folder := range folders {
|
||||||
|
err := filepath.Walk(folder, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if info.IsDir() {
|
||||||
|
gosmi.AppendPath(path)
|
||||||
|
} else if info.Mode()&os.ModeSymlink == 0 {
|
||||||
|
_, err := gosmi.LoadModule(info.Name())
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Module could not be loaded %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Filepath could not be walked %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
folders = []string{}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,6 +277,7 @@ func (s *SnmpTrap) Start(acc telegraf.Accumulator) error {
|
||||||
|
|
||||||
func (s *SnmpTrap) Stop() {
|
func (s *SnmpTrap) Stop() {
|
||||||
s.listener.Close()
|
s.listener.Close()
|
||||||
|
defer gosmi.Exit()
|
||||||
err := <-s.errCh
|
err := <-s.errCh
|
||||||
if nil != err {
|
if nil != err {
|
||||||
s.Log.Errorf("Error stopping trap listener %v", err)
|
s.Log.Errorf("Error stopping trap listener %v", err)
|
||||||
|
|
@ -281,7 +311,7 @@ func makeTrapHandler(s *SnmpTrap) gosnmp.TrapHandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if trapOid != "" {
|
if trapOid != "" {
|
||||||
e, err := s.lookup(trapOid)
|
e, err := s.lookupFunc(trapOid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Error resolving V1 OID, oid=%s, source=%s: %v", trapOid, tags["source"], err)
|
s.Log.Errorf("Error resolving V1 OID, oid=%s, source=%s: %v", trapOid, tags["source"], err)
|
||||||
return
|
return
|
||||||
|
|
@ -319,7 +349,7 @@ func makeTrapHandler(s *SnmpTrap) gosnmp.TrapHandlerFunc {
|
||||||
|
|
||||||
var e mibEntry
|
var e mibEntry
|
||||||
var err error
|
var err error
|
||||||
e, err = s.lookup(val)
|
e, err = s.lookupFunc(val)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
s.Log.Errorf("Error resolving value OID, oid=%s, source=%s: %v", val, tags["source"], err)
|
s.Log.Errorf("Error resolving value OID, oid=%s, source=%s: %v", val, tags["source"], err)
|
||||||
return
|
return
|
||||||
|
|
@ -337,7 +367,7 @@ func makeTrapHandler(s *SnmpTrap) gosnmp.TrapHandlerFunc {
|
||||||
value = v.Value
|
value = v.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := s.lookup(v.Name)
|
e, err := s.lookupFunc(v.Name)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
s.Log.Errorf("Error resolving OID oid=%s, source=%s: %v", v.Name, tags["source"], err)
|
s.Log.Errorf("Error resolving OID oid=%s, source=%s: %v", v.Name, tags["source"], err)
|
||||||
return
|
return
|
||||||
|
|
@ -366,48 +396,16 @@ func makeTrapHandler(s *SnmpTrap) gosnmp.TrapHandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SnmpTrap) lookup(oid string) (e mibEntry, err error) {
|
func lookup(oid string) (e mibEntry, err error) {
|
||||||
s.cacheLock.Lock()
|
var node gosmi.SmiNode
|
||||||
defer s.cacheLock.Unlock()
|
node, err = gosmi.GetNodeByOID(types.OidMustFromString(oid))
|
||||||
var ok bool
|
|
||||||
if e, ok = s.cache[oid]; !ok {
|
|
||||||
// cache miss. exec snmptranslate
|
|
||||||
e, err = s.snmptranslate(oid)
|
|
||||||
if err == nil {
|
|
||||||
s.cache[oid] = e
|
|
||||||
}
|
|
||||||
return e, err
|
|
||||||
}
|
|
||||||
return e, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SnmpTrap) clear() {
|
|
||||||
s.cacheLock.Lock()
|
|
||||||
defer s.cacheLock.Unlock()
|
|
||||||
s.cache = map[string]mibEntry{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SnmpTrap) load(oid string, e mibEntry) {
|
|
||||||
s.cacheLock.Lock()
|
|
||||||
defer s.cacheLock.Unlock()
|
|
||||||
s.cache[oid] = e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SnmpTrap) snmptranslate(oid string) (e mibEntry, err error) {
|
|
||||||
var out []byte
|
|
||||||
out, err = s.execCmd(s.Timeout, "snmptranslate", "-Td", "-Ob", "-m", "all", oid)
|
|
||||||
|
|
||||||
|
// ensure modules are loaded or node will be empty (might not error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return e, err
|
return e, err
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(bytes.NewBuffer(out))
|
e.oidText = node.RenderQualified()
|
||||||
ok := scanner.Scan()
|
|
||||||
if err = scanner.Err(); !ok && err != nil {
|
|
||||||
return e, err
|
|
||||||
}
|
|
||||||
|
|
||||||
e.oidText = scanner.Text()
|
|
||||||
|
|
||||||
i := strings.Index(e.oidText, "::")
|
i := strings.Index(e.oidText, "::")
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package snmp_trap
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -11,35 +12,11 @@ import (
|
||||||
"github.com/gosnmp/gosnmp"
|
"github.com/gosnmp/gosnmp"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/config"
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLoad(t *testing.T) {
|
|
||||||
s := &SnmpTrap{}
|
|
||||||
require.Nil(t, s.Init())
|
|
||||||
|
|
||||||
defer s.clear()
|
|
||||||
s.load(
|
|
||||||
".1.3.6.1.6.3.1.1.5.1",
|
|
||||||
mibEntry{
|
|
||||||
"SNMPv2-MIB",
|
|
||||||
"coldStart",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
e, err := s.lookup(".1.3.6.1.6.3.1.1.5.1")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "SNMPv2-MIB", e.mibName)
|
|
||||||
require.Equal(t, "coldStart", e.oidText)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fakeExecCmd(_ config.Duration, x string, y ...string) ([]byte, error) {
|
|
||||||
return nil, fmt.Errorf("mock " + x + " " + strings.Join(y, " "))
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMsgFlagsV3(secLevel string) gosnmp.SnmpV3MsgFlags {
|
func newMsgFlagsV3(secLevel string) gosnmp.SnmpV3MsgFlags {
|
||||||
var msgFlags gosnmp.SnmpV3MsgFlags
|
var msgFlags gosnmp.SnmpV3MsgFlags
|
||||||
switch strings.ToLower(secLevel) {
|
switch strings.ToLower(secLevel) {
|
||||||
|
|
@ -1284,6 +1261,15 @@ func TestReceiveTrap(t *testing.T) {
|
||||||
timeFunc: func() time.Time {
|
timeFunc: func() time.Time {
|
||||||
return fakeTime
|
return fakeTime
|
||||||
},
|
},
|
||||||
|
lookupFunc: func(input string) (mibEntry, error) {
|
||||||
|
for _, entry := range tt.entries {
|
||||||
|
if input == entry.oid {
|
||||||
|
return mibEntry{entry.e.mibName, entry.e.oidText}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mibEntry{}, fmt.Errorf("Unexpected oid")
|
||||||
|
},
|
||||||
|
//if cold start be answer otherwise err
|
||||||
Log: testutil.Logger{},
|
Log: testutil.Logger{},
|
||||||
Version: tt.version.String(),
|
Version: tt.version.String(),
|
||||||
SecName: tt.secName,
|
SecName: tt.secName,
|
||||||
|
|
@ -1293,19 +1279,13 @@ func TestReceiveTrap(t *testing.T) {
|
||||||
PrivProtocol: tt.privProto,
|
PrivProtocol: tt.privProto,
|
||||||
PrivPassword: tt.privPass,
|
PrivPassword: tt.privPass,
|
||||||
}
|
}
|
||||||
require.Nil(t, s.Init())
|
|
||||||
// Don't look up oid with snmptranslate.
|
require.NoError(t, s.Init())
|
||||||
s.execCmd = fakeExecCmd
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
require.Nil(t, s.Start(&acc))
|
require.Nil(t, s.Start(&acc))
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
// Preload the cache with the oids we'll use in this test
|
|
||||||
// so snmptranslate and mibs don't need to be installed.
|
|
||||||
for _, entry := range tt.entries {
|
|
||||||
s.load(entry.oid, entry.e)
|
|
||||||
}
|
|
||||||
|
|
||||||
var goSNMP gosnmp.GoSNMP
|
var goSNMP gosnmp.GoSNMP
|
||||||
if tt.version == gosnmp.Version3 {
|
if tt.version == gosnmp.Version3 {
|
||||||
msgFlags := newMsgFlagsV3(tt.secLevel)
|
msgFlags := newMsgFlagsV3(tt.secLevel)
|
||||||
|
|
@ -1331,4 +1311,98 @@ func TestReceiveTrap(t *testing.T) {
|
||||||
testutil.SortMetrics())
|
testutil.SortMetrics())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGosmiSingleMib(t *testing.T) {
|
||||||
|
// We would prefer to specify port 0 and let the network
|
||||||
|
// stack choose an unused port for us but TrapListener
|
||||||
|
// doesn't have a way to return the autoselected port.
|
||||||
|
// Instead, we'll use an unusual port and hope it's
|
||||||
|
// unused.
|
||||||
|
const port = 12399
|
||||||
|
|
||||||
|
// Hook into the trap handler so the test knows when the
|
||||||
|
// trap has been received
|
||||||
|
received := make(chan int)
|
||||||
|
wrap := func(f gosnmp.TrapHandlerFunc) gosnmp.TrapHandlerFunc {
|
||||||
|
return func(p *gosnmp.SnmpPacket, a *net.UDPAddr) {
|
||||||
|
f(p, a)
|
||||||
|
received <- 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeTime := time.Unix(456456456, 456)
|
||||||
|
now := uint32(123123123)
|
||||||
|
|
||||||
|
testDataPath, err := filepath.Abs("./testdata")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
trap := gosnmp.SnmpTrap{
|
||||||
|
Variables: []gosnmp.SnmpPDU{
|
||||||
|
{
|
||||||
|
Name: ".1.3.6.1.2.1.1.3.0",
|
||||||
|
Type: gosnmp.TimeTicks,
|
||||||
|
Value: now,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: ".1.3.6.1.6.3.1.1.4.1.0", // SNMPv2-MIB::snmpTrapOID.0
|
||||||
|
Type: gosnmp.ObjectIdentifier,
|
||||||
|
Value: ".1.3.6.1.6.3.1.1.5.1", // coldStart
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"snmp_trap", // name
|
||||||
|
map[string]string{ // tags
|
||||||
|
"oid": ".1.3.6.1.6.3.1.1.5.1",
|
||||||
|
"name": "coldStart",
|
||||||
|
"mib": "SNMPv2-MIB",
|
||||||
|
"version": "2c",
|
||||||
|
"source": "127.0.0.1",
|
||||||
|
"community": "public",
|
||||||
|
},
|
||||||
|
map[string]interface{}{ // fields
|
||||||
|
"sysUpTimeInstance": now,
|
||||||
|
},
|
||||||
|
fakeTime,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the service input plugin
|
||||||
|
s := &SnmpTrap{
|
||||||
|
ServiceAddress: "udp://:" + strconv.Itoa(port),
|
||||||
|
makeHandlerWrapper: wrap,
|
||||||
|
timeFunc: func() time.Time {
|
||||||
|
return fakeTime
|
||||||
|
},
|
||||||
|
lookupFunc: lookup,
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
Version: "2c",
|
||||||
|
Path: []string{testDataPath},
|
||||||
|
}
|
||||||
|
require.NoError(t, s.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.Nil(t, s.Start(&acc))
|
||||||
|
defer s.Stop()
|
||||||
|
|
||||||
|
goSNMP := newGoSNMP(gosnmp.Version2c, port)
|
||||||
|
|
||||||
|
// Send the trap
|
||||||
|
sendTrap(t, goSNMP, trap)
|
||||||
|
|
||||||
|
// Wait for trap to be received
|
||||||
|
select {
|
||||||
|
case <-received:
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
t.Fatal("timed out waiting for trap to be received")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify plugin output
|
||||||
|
testutil.RequireMetricsEqual(t,
|
||||||
|
metrics, acc.GetTelegrafMetrics(),
|
||||||
|
testutil.SortMetrics())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
SNMPv2-MIB DEFINITIONS ::= BEGIN
|
||||||
|
|
||||||
|
IMPORTS
|
||||||
|
NOTIFICATION-TYPE, NOTIFICATION-GROUP
|
||||||
|
FROM test2;
|
||||||
|
|
||||||
|
|
||||||
|
snmpMIB MODULE-IDENTITY
|
||||||
|
LAST-UPDATED "2021060900Z"
|
||||||
|
ORGANIZATION "testing"
|
||||||
|
CONTACT-INFO
|
||||||
|
"EMail: testing@emai.com"
|
||||||
|
DESCRIPTION
|
||||||
|
"MIB module for testing snmp_trap plugin
|
||||||
|
for telegraf
|
||||||
|
"
|
||||||
|
::={ coldStart 1 }
|
||||||
|
|
||||||
|
snmpMIBObjects OBJECT IDENTIFIER ::= { snmpMIB 1 }
|
||||||
|
|
||||||
|
system OBJECT IDENTIFIER ::= { sysUpTimeInstance 1 }
|
||||||
|
|
||||||
|
coldStart NOTIFICATION-TYPE
|
||||||
|
STATUS current
|
||||||
|
DESCRIPTION
|
||||||
|
"A coldStart trap signifies that the SNMP entity,
|
||||||
|
supporting a notification originator application, is
|
||||||
|
reinitializing itself and that its configuration may
|
||||||
|
have been altered."
|
||||||
|
::= { snmpTraps 1 }
|
||||||
|
|
||||||
|
snmpBasicNotificationsGroup NOTIFICATION-GROUP
|
||||||
|
NOTIFICATIONS { coldStart, authenticationFailure }
|
||||||
|
STATUS current
|
||||||
|
DESCRIPTION
|
||||||
|
"The basic notifications implemented by an SNMP entity
|
||||||
|
supporting command responder applications."
|
||||||
|
::= { snmpMIBGroups 7 }
|
||||||
|
|
||||||
|
END
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
SNMPv2-MIB DEFINITIONS ::= BEGIN
|
||||||
|
|
||||||
|
org OBJECT IDENTIFIER ::= { iso 3 } -- "iso" = 1
|
||||||
|
dod OBJECT IDENTIFIER ::= { org 6 }
|
||||||
|
internet OBJECT IDENTIFIER ::= { dod 1 }
|
||||||
|
|
||||||
|
directory OBJECT IDENTIFIER ::= { internet 1 }
|
||||||
|
|
||||||
|
mgmt OBJECT IDENTIFIER ::= { internet 2 }
|
||||||
|
sysUpTimeInstance OBJECT IDENTIFIER ::= { mgmt 1 }
|
||||||
|
transmission OBJECT IDENTIFIER ::= { sysUpTimeInstance 10 }
|
||||||
|
|
||||||
|
experimental OBJECT IDENTIFIER ::= { internet 3 }
|
||||||
|
|
||||||
|
private OBJECT IDENTIFIER ::= { internet 4 }
|
||||||
|
enterprises OBJECT IDENTIFIER ::= { private 1 }
|
||||||
|
|
||||||
|
security OBJECT IDENTIFIER ::= { internet 5 }
|
||||||
|
|
||||||
|
snmpV2 OBJECT IDENTIFIER ::= { internet 6 }
|
||||||
|
|
||||||
|
-- transport domains
|
||||||
|
snmpDomains OBJECT IDENTIFIER ::= { snmpV2 1 }
|
||||||
|
|
||||||
|
-- transport proxies
|
||||||
|
snmpProxys OBJECT IDENTIFIER ::= { snmpV2 2 }
|
||||||
|
|
||||||
|
-- module identities
|
||||||
|
coldStart OBJECT IDENTIFIER ::= { snmpV2 3 }
|
||||||
|
|
||||||
|
NOTIFICATION-TYPE MACRO ::=
|
||||||
|
BEGIN
|
||||||
|
TYPE NOTATION ::=
|
||||||
|
ObjectsPart
|
||||||
|
"STATUS" Status
|
||||||
|
"DESCRIPTION" Text
|
||||||
|
ReferPart
|
||||||
|
|
||||||
|
VALUE NOTATION ::=
|
||||||
|
value(VALUE NotificationName)
|
||||||
|
|
||||||
|
ObjectsPart ::=
|
||||||
|
"OBJECTS" "{" Objects "}"
|
||||||
|
| empty
|
||||||
|
Objects ::=
|
||||||
|
Object
|
||||||
|
|
||||||
|
| Objects "," Object
|
||||||
|
Object ::=
|
||||||
|
value(ObjectName)
|
||||||
|
|
||||||
|
Status ::=
|
||||||
|
"current"
|
||||||
|
| "deprecated"
|
||||||
|
| "obsolete"
|
||||||
|
|
||||||
|
ReferPart ::=
|
||||||
|
"REFERENCE" Text
|
||||||
|
| empty
|
||||||
|
|
||||||
|
-- a character string as defined in section 3.1.1
|
||||||
|
Text ::= value(IA5String)
|
||||||
|
END
|
||||||
|
|
||||||
|
NOTIFICATION-GROUP MACRO ::=
|
||||||
|
BEGIN
|
||||||
|
TYPE NOTATION ::=
|
||||||
|
NotificationsPart
|
||||||
|
"STATUS" Status
|
||||||
|
"DESCRIPTION" Text
|
||||||
|
ReferPart
|
||||||
|
|
||||||
|
VALUE NOTATION ::=
|
||||||
|
value(VALUE OBJECT IDENTIFIER)
|
||||||
|
|
||||||
|
NotificationsPart ::=
|
||||||
|
"NOTIFICATIONS" "{" Notifications "}"
|
||||||
|
Notifications ::=
|
||||||
|
Notification
|
||||||
|
| Notifications "," Notification
|
||||||
|
Notification ::=
|
||||||
|
value(NotificationName)
|
||||||
|
|
||||||
|
Status ::=
|
||||||
|
"current"
|
||||||
|
| "deprecated"
|
||||||
|
| "obsolete"
|
||||||
|
|
||||||
|
ReferPart ::=
|
||||||
|
"REFERENCE" Text
|
||||||
|
| empty
|
||||||
|
|
||||||
|
-- a character string as defined in [2]
|
||||||
|
Text ::= value(IA5String)
|
||||||
|
END
|
||||||
|
|
||||||
|
END
|
||||||
Loading…
Reference in New Issue