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:
|
## or hextoint:BigEndian:uint32. Valid options for the Endian are:
|
||||||
## BigEndian and LittleEndian. For the bit size: uint16, uint32
|
## BigEndian and LittleEndian. For the bit size: uint16, uint32
|
||||||
## and uint64.
|
## 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 = ""
|
# conversion = ""
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/sleepinggenius2/gosmi"
|
"github.com/sleepinggenius2/gosmi"
|
||||||
|
"github.com/sleepinggenius2/gosmi/models"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/internal/snmp"
|
"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
|
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"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gosnmp/gosnmp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"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, 0xF6, 0xD5, 0x3B, 0x60}, "hextoint:LittleEndian:uint64", uint64(6934371307618175232)},
|
||||||
{[]byte{0x00, 0x09, 0x3E, 0xE3}, "hextoint:LittleEndian:uint32", uint32(3812493568)},
|
{[]byte{0x00, 0x09, 0x3E, 0xE3}, "hextoint:LittleEndian:uint32", uint32(3812493568)},
|
||||||
{[]byte{0x00, 0x09}, "hextoint:LittleEndian:uint16", uint16(2304)},
|
{[]byte{0x00, 0x09}, "hextoint:LittleEndian:uint16", uint16(2304)},
|
||||||
|
{3, "enum", "testing"},
|
||||||
|
{3, "enum(1)", "testing(3)"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testTable {
|
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.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)
|
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 (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log" //nolint:depguard // Allow exceptional but valid use of log here.
|
"log" //nolint:depguard // Allow exceptional but valid use of log here.
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -256,3 +257,7 @@ func snmpTranslateCall(oid string) (mibName string, oidNum string, oidText strin
|
||||||
|
|
||||||
return mibName, oidNum, oidText, conversion, nil
|
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,
|
fields []Field,
|
||||||
err error,
|
err error,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SnmpFormatEnum(oid string, value interface{}, full bool) (
|
||||||
|
formatted string,
|
||||||
|
err error,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snmp holds the configuration for the plugin.
|
// Snmp holds the configuration for the plugin.
|
||||||
|
|
@ -214,6 +219,7 @@ type Field struct {
|
||||||
// "int" will conver the value into an integer.
|
// "int" will conver the value into an integer.
|
||||||
// "hwaddr" will convert a 6-byte string to a MAC address.
|
// "hwaddr" will convert a 6-byte string to a MAC address.
|
||||||
// "ipaddr" will convert the value to an IPv4 or IPv6 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
|
Conversion string
|
||||||
// Translate tells if the value of the field should be snmptranslated
|
// Translate tells if the value of the field should be snmptranslated
|
||||||
Translate bool
|
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 {
|
} else if pkt != nil && len(pkt.Variables) > 0 && pkt.Variables[0].Type != gosnmp.NoSuchObject && pkt.Variables[0].Type != gosnmp.NoSuchInstance {
|
||||||
ent := pkt.Variables[0]
|
ent := pkt.Variables[0]
|
||||||
fv, err := fieldConvert(f.Conversion, ent.Value)
|
fv, err := fieldConvert(tr, f.Conversion, ent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("converting %q (OID %s) for field %s: %w", ent.Value, ent.Name, f.Name, err)
|
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 {
|
if err != nil {
|
||||||
return &walkError{
|
return &walkError{
|
||||||
msg: fmt.Sprintf("converting %q (OID %s) for field %s", ent.Value, ent.Name, f.Name),
|
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
|
// 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 conv == "" {
|
||||||
if bs, ok := v.([]byte); ok {
|
if bs, ok := ent.Value.([]byte); ok {
|
||||||
return string(bs), nil
|
return string(bs), nil
|
||||||
}
|
}
|
||||||
return v, nil
|
return ent.Value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var d int
|
var d int
|
||||||
if _, err := fmt.Sscanf(conv, "float(%d)", &d); err == nil || conv == "float" {
|
if _, err := fmt.Sscanf(conv, "float(%d)", &d); err == nil || conv == "float" {
|
||||||
|
v = ent.Value
|
||||||
switch vt := v.(type) {
|
switch vt := v.(type) {
|
||||||
case float32:
|
case float32:
|
||||||
v = float64(vt) / math.Pow10(d)
|
v = float64(vt) / math.Pow10(d)
|
||||||
|
|
@ -645,6 +652,7 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if conv == "int" {
|
if conv == "int" {
|
||||||
|
v = ent.Value
|
||||||
switch vt := v.(type) {
|
switch vt := v.(type) {
|
||||||
case float32:
|
case float32:
|
||||||
v = int64(vt)
|
v = int64(vt)
|
||||||
|
|
@ -679,7 +687,7 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if conv == "hwaddr" {
|
if conv == "hwaddr" {
|
||||||
switch vt := v.(type) {
|
switch vt := ent.Value.(type) {
|
||||||
case string:
|
case string:
|
||||||
v = net.HardwareAddr(vt).String()
|
v = net.HardwareAddr(vt).String()
|
||||||
case []byte:
|
case []byte:
|
||||||
|
|
@ -695,9 +703,9 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
||||||
endian := split[1]
|
endian := split[1]
|
||||||
bit := split[2]
|
bit := split[2]
|
||||||
|
|
||||||
bv, ok := v.([]byte)
|
bv, ok := ent.Value.([]byte)
|
||||||
if !ok {
|
if !ok {
|
||||||
return v, nil
|
return ent.Value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch endian {
|
switch endian {
|
||||||
|
|
@ -733,7 +741,7 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
||||||
if conv == "ipaddr" {
|
if conv == "ipaddr" {
|
||||||
var ipbs []byte
|
var ipbs []byte
|
||||||
|
|
||||||
switch vt := v.(type) {
|
switch vt := ent.Value.(type) {
|
||||||
case string:
|
case string:
|
||||||
ipbs = []byte(vt)
|
ipbs = []byte(vt)
|
||||||
case []byte:
|
case []byte:
|
||||||
|
|
@ -752,6 +760,14 @@ func fieldConvert(conv string, v interface{}) (interface{}, error) {
|
||||||
return v, nil
|
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)
|
return nil, fmt.Errorf("invalid conversion type '%s'", conv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -901,7 +901,7 @@ func TestFieldConvert(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testTable {
|
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) {
|
if !assert.NoError(t, err, "input=%T(%v) conv=%s expected=%T(%v)", tc.input, tc.input, tc.conv, tc.expected, tc.expected) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue