feat(inputs.snmp): convert enum values (#11872)
This commit is contained in:
parent
8ec12c06b8
commit
25154e50fd
|
|
@ -147,6 +147,10 @@ option operate similar to the `snmpget` utility.
|
|||
## or hextoint:BigEndian:uint32. Valid options for the Endian are:
|
||||
## BigEndian and LittleEndian. For the bit size: uint16, uint32
|
||||
## and uint64.
|
||||
## enum(1): Convert the value according to its syntax in the MIB (full).
|
||||
## (Only supported with gosmi translator)
|
||||
## enum: Convert the value according to its syntax in the MIB.
|
||||
## (Only supported with gosmi translator)
|
||||
##
|
||||
# conversion = ""
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/sleepinggenius2/gosmi"
|
||||
"github.com/sleepinggenius2/gosmi/models"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/internal/snmp"
|
||||
|
|
@ -122,3 +123,21 @@ func (g *gosmiTranslator) SnmpTableCall(oid string) (mibName string, oidNum stri
|
|||
|
||||
return mibName, oidNum, oidText, fields, nil
|
||||
}
|
||||
|
||||
func (g *gosmiTranslator) SnmpFormatEnum(oid string, value interface{}, full bool) (string, error) {
|
||||
//nolint:dogsled // only need to get the node
|
||||
_, _, _, _, node, err := g.SnmpTranslateFull(oid)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var v models.Value
|
||||
if full {
|
||||
v = node.FormatValue(value, models.FormatEnumName, models.FormatEnumValue)
|
||||
} else {
|
||||
v = node.FormatValue(value, models.FormatEnumName)
|
||||
}
|
||||
|
||||
return v.Formatted, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gosnmp/gosnmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
|
@ -580,10 +581,12 @@ func TestFieldConvertGosmi(t *testing.T) {
|
|||
{[]byte{0x00, 0x09, 0x3E, 0xE3, 0xF6, 0xD5, 0x3B, 0x60}, "hextoint:LittleEndian:uint64", uint64(6934371307618175232)},
|
||||
{[]byte{0x00, 0x09, 0x3E, 0xE3}, "hextoint:LittleEndian:uint32", uint32(3812493568)},
|
||||
{[]byte{0x00, 0x09}, "hextoint:LittleEndian:uint16", uint16(2304)},
|
||||
{3, "enum", "testing"},
|
||||
{3, "enum(1)", "testing(3)"},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
act, err := fieldConvert(tc.conv, tc.input)
|
||||
act, err := fieldConvert(getGosmiTr(t), tc.conv, gosnmp.SnmpPDU{Name: ".1.3.6.1.2.1.2.2.1.8", Value: tc.input})
|
||||
assert.NoError(t, err, "input=%T(%v) conv=%s expected=%T(%v)", tc.input, tc.input, tc.conv, tc.expected, tc.expected)
|
||||
assert.EqualValues(t, tc.expected, act, "input=%T(%v) conv=%s expected=%T(%v)", tc.input, tc.input, tc.conv, tc.expected, tc.expected)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package snmp
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log" //nolint:depguard // Allow exceptional but valid use of log here.
|
||||
"os/exec"
|
||||
|
|
@ -256,3 +257,7 @@ func snmpTranslateCall(oid string) (mibName string, oidNum string, oidText strin
|
|||
|
||||
return mibName, oidNum, oidText, conversion, nil
|
||||
}
|
||||
|
||||
func (n *netsnmpTranslator) SnmpFormatEnum(_ string, _ interface{}, _ bool) (string, error) {
|
||||
return "", errors.New("not implemented in netsnmp translator")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ type Translator interface {
|
|||
fields []Field,
|
||||
err error,
|
||||
)
|
||||
|
||||
SnmpFormatEnum(oid string, value interface{}, full bool) (
|
||||
formatted string,
|
||||
err error,
|
||||
)
|
||||
}
|
||||
|
||||
// Snmp holds the configuration for the plugin.
|
||||
|
|
@ -214,6 +219,7 @@ type Field struct {
|
|||
// "int" will conver the value into an integer.
|
||||
// "hwaddr" will convert a 6-byte string to a MAC address.
|
||||
// "ipaddr" will convert the value to an IPv4 or IPv6 address.
|
||||
// "enum"/"enum(1)" will convert the value according to its syntax. (Only supported with gosmi translator)
|
||||
Conversion string
|
||||
// Translate tells if the value of the field should be snmptranslated
|
||||
Translate bool
|
||||
|
|
@ -424,7 +430,7 @@ func (t Table) Build(gs snmpConnection, walk bool, tr Translator) (*RTable, erro
|
|||
}
|
||||
} else if pkt != nil && len(pkt.Variables) > 0 && pkt.Variables[0].Type != gosnmp.NoSuchObject && pkt.Variables[0].Type != gosnmp.NoSuchInstance {
|
||||
ent := pkt.Variables[0]
|
||||
fv, err := fieldConvert(f.Conversion, ent.Value)
|
||||
fv, err := fieldConvert(tr, f.Conversion, ent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting %q (OID %s) for field %s: %w", ent.Value, ent.Name, f.Name, err)
|
||||
}
|
||||
|
|
@ -468,7 +474,7 @@ func (t Table) Build(gs snmpConnection, walk bool, tr Translator) (*RTable, erro
|
|||
}
|
||||
}
|
||||
|
||||
fv, err := fieldConvert(f.Conversion, ent.Value)
|
||||
fv, err := fieldConvert(tr, f.Conversion, ent)
|
||||
if err != nil {
|
||||
return &walkError{
|
||||
msg: fmt.Sprintf("converting %q (OID %s) for field %s", ent.Value, ent.Name, f.Name),
|
||||
|
|
@ -599,16 +605,17 @@ func (s *Snmp) getConnection(idx int) (snmpConnection, error) {
|
|||
}
|
||||
|
||||
// fieldConvert converts from any type according to the conv specification
|
||||
func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
||||
func fieldConvert(tr Translator, conv string, ent gosnmp.SnmpPDU) (v interface{}, err error) {
|
||||
if conv == "" {
|
||||
if bs, ok := v.([]byte); ok {
|
||||
if bs, ok := ent.Value.([]byte); ok {
|
||||
return string(bs), nil
|
||||
}
|
||||
return v, nil
|
||||
return ent.Value, nil
|
||||
}
|
||||
|
||||
var d int
|
||||
if _, err := fmt.Sscanf(conv, "float(%d)", &d); err == nil || conv == "float" {
|
||||
v = ent.Value
|
||||
switch vt := v.(type) {
|
||||
case float32:
|
||||
v = float64(vt) / math.Pow10(d)
|
||||
|
|
@ -645,6 +652,7 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
|||
}
|
||||
|
||||
if conv == "int" {
|
||||
v = ent.Value
|
||||
switch vt := v.(type) {
|
||||
case float32:
|
||||
v = int64(vt)
|
||||
|
|
@ -679,7 +687,7 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
|||
}
|
||||
|
||||
if conv == "hwaddr" {
|
||||
switch vt := v.(type) {
|
||||
switch vt := ent.Value.(type) {
|
||||
case string:
|
||||
v = net.HardwareAddr(vt).String()
|
||||
case []byte:
|
||||
|
|
@ -695,9 +703,9 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
|||
endian := split[1]
|
||||
bit := split[2]
|
||||
|
||||
bv, ok := v.([]byte)
|
||||
bv, ok := ent.Value.([]byte)
|
||||
if !ok {
|
||||
return v, nil
|
||||
return ent.Value, nil
|
||||
}
|
||||
|
||||
switch endian {
|
||||
|
|
@ -733,7 +741,7 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
|||
if conv == "ipaddr" {
|
||||
var ipbs []byte
|
||||
|
||||
switch vt := v.(type) {
|
||||
switch vt := ent.Value.(type) {
|
||||
case string:
|
||||
ipbs = []byte(vt)
|
||||
case []byte:
|
||||
|
|
@ -752,6 +760,14 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
|||
return v, nil
|
||||
}
|
||||
|
||||
if conv == "enum" {
|
||||
return tr.SnmpFormatEnum(ent.Name, ent.Value, false)
|
||||
}
|
||||
|
||||
if conv == "enum(1)" {
|
||||
return tr.SnmpFormatEnum(ent.Name, ent.Value, true)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid conversion type '%s'", conv)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -901,7 +901,7 @@ func TestFieldConvert(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
act, err := fieldConvert(tc.conv, tc.input)
|
||||
act, err := fieldConvert(NewNetsnmpTranslator(), tc.conv, gosnmp.SnmpPDU{Value: tc.input})
|
||||
if !assert.NoError(t, err, "input=%T(%v) conv=%s expected=%T(%v)", tc.input, tc.input, tc.conv, tc.expected, tc.expected) {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue