From 57dfc195d3c62d2d3a2ec7f3ef8ca182468ddcb1 Mon Sep 17 00:00:00 2001 From: Thibault Cohen Date: Tue, 19 Jan 2016 23:53:40 -0500 Subject: [PATCH] Add features from https://github.com/influxdata/telegraf/pull/546 --- plugins/inputs/snmp/snmp.go | 94 +++++++---- plugins/inputs/snmp/snmp_test.go | 274 +++++++++++++++++++++++++++---- 2 files changed, 300 insertions(+), 68 deletions(-) diff --git a/plugins/inputs/snmp/snmp.go b/plugins/inputs/snmp/snmp.go index d894cd47cf9d4..ae77920b20277 100644 --- a/plugins/inputs/snmp/snmp.go +++ b/plugins/inputs/snmp/snmp.go @@ -1,7 +1,6 @@ package snmp import ( - "fmt" "io/ioutil" "log" "net" @@ -41,14 +40,18 @@ type Host struct { type Data struct { Name string - // OIds + // OID (could be numbers or name) Oid string // Unit Unit string // SNMP getbulk max repetition MaxRepetition uint8 `toml:"max_repetition"` - // We want to resolve oid to names oids text file - Snmptranslate bool + // SNMP Instance (default 0) + // (only used with GET request and if + // OID is a name from snmptranslate file) + Instance string + // OID (only number) (used for computation) + rawOid string } type Node struct { @@ -63,6 +66,8 @@ var initNode = Node{ subnodes: make(map[string]Node), } +var NameToOid = make(map[string]string) + var sampleConfig = ` # Use 'oids.txt' file to translate oids to names # To generate 'oids.txt' you need to run: @@ -93,25 +98,28 @@ var sampleConfig = ` collect = ["mybulk"] [[inputs.snmp.get]] - name = "sysservices" - oid = ".1.3.6.1.2.1.1.7.0" + name = "ifnumber" + oid = "ifNumber" [[inputs.snmp.get]] - name = "sysdescr" - oid = ".1.3.6.1.2.1.1.1.0" - snmptranslate = true + name = "interface_speed" + oid = "ifSpeed" + instance = 0 [[inputs.snmp.get]] name = "sysuptime" oid = ".1.3.6.1.2.1.1.3.0" - unit = "seconds" - snmptranslate = true + unit = "second" [[inputs.snmp.bulk]] name = "mybulk" max_repetition = 127 oid = ".1.3.6.1.2.1.1" - snmptranslate = true + + [[inputs.snmp.bulk]] + name = "ifoutoctets" + max_repetition = 127 + oid = "ifOutOctets" ` // SampleConfig returns sample configuration message @@ -144,10 +152,10 @@ func fillnode(parentNode Node, oid_name string, ids []string) { } } -func findnodename(node Node, ids []string) string { +func findnodename(node Node, ids []string) (string, string) { // ids = ["1", "3", "6", ...] if len(ids) == 1 { - return node.name + return node.name, ids[0] } id, ids := ids[0], ids[1:] // Get node @@ -159,16 +167,16 @@ func findnodename(node Node, ids []string) string { // Get node name if node.name != "" && len(ids) == 0 && id == "0" { // node with instance 0 - return node.name + return node.name, "0" } else if node.name != "" && len(ids) == 0 && id != "0" { // node with an instance - return node.name + "." + string(id) + return node.name, string(id) } else if node.name != "" && len(ids) > 0 { // node with subinstances - return node.name + "." + strings.Join(ids, ".") + return node.name, strings.Join(ids, ".") } // return an empty node name - return node.name + return node.name, "" } func (s *Snmp) Gather(acc inputs.Accumulator) error { @@ -176,7 +184,8 @@ func (s *Snmp) Gather(acc inputs.Accumulator) error { if s.SnmptranslateFile != "" { data, err := ioutil.ReadFile(s.SnmptranslateFile) if err != nil { - fmt.Print(err) + log.Printf("Reading SNMPtranslate file error: %s", err) + return err } else { for _, line := range strings.Split(string(data), "\n") { oidsRegEx := regexp.MustCompile(`([^\t]*)\t*([^\t]*)`) @@ -185,6 +194,7 @@ func (s *Snmp) Gather(acc inputs.Accumulator) error { oid_name := oids[1] oid := oids[2] fillnode(initNode, oid_name, strings.Split(string(oid), ".")) + NameToOid[oid_name] = oid } } } @@ -209,12 +219,27 @@ func (s *Snmp) Gather(acc inputs.Accumulator) error { // Get GET oids for _, oid := range s.Get { if oid.Name == oid_name { + if val, ok := NameToOid[oid.Oid]; ok { + // TODO should we add the 0 instance ? + if oid.Instance != "" { + oid.rawOid = "." + val + "." + oid.Instance + } else { + oid.rawOid = "." + val + ".0" + } + } else { + oid.rawOid = oid.Oid + } host.getOids = append(host.getOids, oid) } } // Get GETBULK oids for _, oid := range s.Bulk { if oid.Name == oid_name { + if val, ok := NameToOid[oid.Oid]; ok { + oid.rawOid = "." + val + } else { + oid.rawOid = oid.Oid + } host.bulkOids = append(host.bulkOids, oid) } } @@ -239,11 +264,11 @@ func (h *Host) SNMPGet(acc inputs.Accumulator) error { // Prepare OIDs oidsList := make(map[string]Data) for _, oid := range h.getOids { - oidsList[oid.Oid] = oid + oidsList[oid.rawOid] = oid } oidsNameList := make([]string, 0, len(oidsList)) for _, oid := range oidsList { - oidsNameList = append(oidsNameList, oid.Oid) + oidsNameList = append(oidsNameList, oid.rawOid) } // gosnmp.MAX_OIDS == 60 @@ -281,11 +306,11 @@ func (h *Host) SNMPBulk(acc inputs.Accumulator) error { // Prepare OIDs oidsList := make(map[string]Data) for _, oid := range h.bulkOids { - oidsList[oid.Oid] = oid + oidsList[oid.rawOid] = oid } oidsNameList := make([]string, 0, len(oidsList)) for _, oid := range oidsList { - oidsNameList = append(oidsNameList, oid.Oid) + oidsNameList = append(oidsNameList, oid.rawOid) } // TODO Trying to make requests with more than one OID // to reduce the number of requests @@ -379,31 +404,28 @@ func (h *Host) HandleResponse(oids map[string]Data, result *gosnmp.SnmpPacket, a // Get name and instance var oid_name string var instance string - if oid.Snmptranslate { - // Get oidname and instannce from translate file - oidfullname := findnodename(initNode, - strings.Split(string(variable.Name[1:]), ".")) - res := strings.SplitN(string(oidfullname), ".", 2) - oid_name = res[0] - if len(res) > 1 { - instance = res[1] - } - } + // Get oidname and instannce from translate file + oid_name, instance = findnodename(initNode, + strings.Split(string(variable.Name[1:]), ".")) + if instance != "" { tags["instance"] = instance } + // Set name var field_name string if oid_name != "" { + // Set fieldname as oid name from translate file field_name = oid_name - } else if oid.Oid == variable.Name { - field_name = oid.Name } else { - field_name = variable.Name + // Set fieldname as oid name from inputs.snmp.get section + // Because the result oid is equal to inputs.snmp.get section + field_name = oid.Name } tags["host"], _, _ = net.SplitHostPort(h.Address) fields := make(map[string]interface{}) fields[string(field_name)] = variable.Value + acc.AddFields(field_name, fields, tags) case gosnmp.NoSuchObject, gosnmp.NoSuchInstance: // Oid not found diff --git a/plugins/inputs/snmp/snmp_test.go b/plugins/inputs/snmp/snmp_test.go index b98eb43f0599e..392b17e50f1a9 100644 --- a/plugins/inputs/snmp/snmp_test.go +++ b/plugins/inputs/snmp/snmp_test.go @@ -5,14 +5,34 @@ import ( "github.com/influxdb/telegraf/testutil" - "github.com/stretchr/testify/assert" + // "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestSNMPErrorGet(t *testing.T) { +func TestSNMPErrorGet1(t *testing.T) { get1 := Data{ Name: "oid1", - Unit: "second", + Unit: "octets", + Oid: ".1.3.6.1.2.1.2.2.1.16.1", + } + h := Host{ + Collect: []string{"oid1"}, + } + s := Snmp{ + SnmptranslateFile: "bad_oid.txt", + Host: []Host{h}, + Get: []Data{get1}, + } + + var acc testutil.Accumulator + err := s.Gather(&acc) + require.Error(t, err) +} + +func TestSNMPErrorGet2(t *testing.T) { + get1 := Data{ + Name: "oid1", + Unit: "octets", Oid: ".1.3.6.1.2.1.2.2.1.16.1", } h := Host{ @@ -30,12 +50,12 @@ func TestSNMPErrorGet(t *testing.T) { func TestSNMPErrorBulk(t *testing.T) { bulk1 := Data{ - Name: "oid1", - Unit: "second", - Oid: ".1.3.6.1.2.1.2.2.1.16", - Snmptranslate: true, + Name: "oid1", + Unit: "octets", + Oid: ".1.3.6.1.2.1.2.2.1.16", } h := Host{ + Address: "127.0.0.1", Collect: []string{"oid1"}, } s := Snmp{ @@ -48,13 +68,11 @@ func TestSNMPErrorBulk(t *testing.T) { require.Error(t, err) } -func TestSNMPBulk(t *testing.T) { - bulk1 := Data{ - Name: "oid1", - Unit: "second", - Oid: ".1.3.6.1.2.1.2.2.1.16", - MaxRepetition: 2, - Snmptranslate: true, +func TestSNMPGet1(t *testing.T) { + get1 := Data{ + Name: "oid1", + Unit: "octets", + Oid: ".1.3.6.1.2.1.2.2.1.16.1", } h := Host{ Address: "127.0.0.1:31161", @@ -66,29 +84,66 @@ func TestSNMPBulk(t *testing.T) { } s := Snmp{ Host: []Host{h}, - Bulk: []Data{bulk1}, + Get: []Data{get1}, } - expected := map[string]uint{".1.3.6.1.2.1.2.2.1.16.1": 543846, - ".1.3.6.1.2.1.2.2.1.16.2": 26475179, - ".1.3.6.1.2.1.2.2.1.16.3": 108963968, - ".1.3.6.1.2.1.2.2.1.16.36": 12991453, + var acc testutil.Accumulator + err := s.Gather(&acc) + require.NoError(t, err) + + acc.AssertContainsTaggedFields(t, + "oid1", + map[string]interface{}{ + "oid1": uint(543846), + }, + map[string]string{ + "unit": "octets", + "host": "127.0.0.1", + }, + ) +} + +func TestSNMPGet2(t *testing.T) { + get1 := Data{ + Name: "oid1", + Oid: "ifNumber", + } + h := Host{ + Address: "127.0.0.1:31161", + Community: "telegraf", + Version: 2, + Timeout: 2.0, + Retries: 2, + Collect: []string{"oid1"}, + } + s := Snmp{ + SnmptranslateFile: "./testdata/oids.txt", + Host: []Host{h}, + Get: []Data{get1}, } var acc testutil.Accumulator err := s.Gather(&acc) require.NoError(t, err) - for _, p := range acc.Points { - assert.Equal(t, expected[p.Measurement], p.Fields[p.Measurement]) - } + acc.AssertContainsTaggedFields(t, + "ifNumber", + map[string]interface{}{ + "ifNumber": int(4), + }, + map[string]string{ + "instance": "0", + "host": "127.0.0.1", + }, + ) } -func TestSNMPGet(t *testing.T) { +func TestSNMPGet3(t *testing.T) { get1 := Data{ - Name: "oid1", - Unit: "second", - Oid: ".1.3.6.1.2.1.2.2.1.16.1", + Name: "oid1", + Unit: "octets", + Oid: "ifSpeed", + Instance: "1", } h := Host{ Address: "127.0.0.1:31161", @@ -99,17 +154,172 @@ func TestSNMPGet(t *testing.T) { Collect: []string{"oid1"}, } s := Snmp{ - Host: []Host{h}, - Get: []Data{get1}, + SnmptranslateFile: "./testdata/oids.txt", + Host: []Host{h}, + Get: []Data{get1}, } var acc testutil.Accumulator err := s.Gather(&acc) require.NoError(t, err) - for _, p := range acc.Points { - value := p.Fields["oid1"].(uint) - assert.Equal(t, get1.Name, p.Measurement) - assert.Equal(t, uint(543846), value) + acc.AssertContainsTaggedFields(t, + "ifSpeed", + map[string]interface{}{ + "ifSpeed": uint(10000000), + }, + map[string]string{ + "unit": "octets", + "instance": "1", + "host": "127.0.0.1", + }, + ) +} + +func TestSNMPBulk1(t *testing.T) { + bulk1 := Data{ + Name: "oid1", + Unit: "octets", + Oid: ".1.3.6.1.2.1.2.2.1.16", + MaxRepetition: 2, } + h := Host{ + Address: "127.0.0.1:31161", + Community: "telegraf", + Version: 2, + Timeout: 2.0, + Retries: 2, + Collect: []string{"oid1"}, + } + s := Snmp{ + SnmptranslateFile: "./testdata/oids.txt", + Host: []Host{h}, + Bulk: []Data{bulk1}, + } + + var acc testutil.Accumulator + err := s.Gather(&acc) + require.NoError(t, err) + + acc.AssertContainsTaggedFields(t, + "ifOutOctets", + map[string]interface{}{ + "ifOutOctets": uint(543846), + }, + map[string]string{ + "unit": "octets", + "instance": "1", + "host": "127.0.0.1", + }, + ) + + acc.AssertContainsTaggedFields(t, + "ifOutOctets", + map[string]interface{}{ + "ifOutOctets": uint(26475179), + }, + map[string]string{ + "unit": "octets", + "instance": "2", + "host": "127.0.0.1", + }, + ) + + acc.AssertContainsTaggedFields(t, + "ifOutOctets", + map[string]interface{}{ + "ifOutOctets": uint(108963968), + }, + map[string]string{ + "unit": "octets", + "instance": "3", + "host": "127.0.0.1", + }, + ) + + acc.AssertContainsTaggedFields(t, + "ifOutOctets", + map[string]interface{}{ + "ifOutOctets": uint(12991453), + }, + map[string]string{ + "unit": "octets", + "instance": "36", + "host": "127.0.0.1", + }, + ) +} + +func TestSNMPBulk2(t *testing.T) { + bulk1 := Data{ + Name: "oid1", + Unit: "octets", + Oid: "ifOutOctets", + MaxRepetition: 2, + } + h := Host{ + Address: "127.0.0.1:31161", + Community: "telegraf", + Version: 2, + Timeout: 2.0, + Retries: 2, + Collect: []string{"oid1"}, + } + s := Snmp{ + SnmptranslateFile: "./testdata/oids.txt", + Host: []Host{h}, + Bulk: []Data{bulk1}, + } + + var acc testutil.Accumulator + err := s.Gather(&acc) + require.NoError(t, err) + + acc.AssertContainsTaggedFields(t, + "ifOutOctets", + map[string]interface{}{ + "ifOutOctets": uint(543846), + }, + map[string]string{ + "unit": "octets", + "instance": "1", + "host": "127.0.0.1", + }, + ) + + acc.AssertContainsTaggedFields(t, + "ifOutOctets", + map[string]interface{}{ + "ifOutOctets": uint(26475179), + }, + map[string]string{ + "unit": "octets", + "instance": "2", + "host": "127.0.0.1", + }, + ) + + acc.AssertContainsTaggedFields(t, + "ifOutOctets", + map[string]interface{}{ + "ifOutOctets": uint(108963968), + }, + map[string]string{ + "unit": "octets", + "instance": "3", + "host": "127.0.0.1", + }, + ) + + acc.AssertContainsTaggedFields(t, + "ifOutOctets", + map[string]interface{}{ + "ifOutOctets": uint(12991453), + }, + map[string]string{ + "unit": "octets", + "instance": "36", + "host": "127.0.0.1", + }, + ) }