1
0
Fork 0
telegraf/internal/snmp/translator_gosmi.go
Daniel Baumann 4978089aab
Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-24 07:26:29 +02:00

240 lines
6.2 KiB
Go

package snmp
import (
"errors"
"fmt"
"strings"
"github.com/sleepinggenius2/gosmi"
"github.com/sleepinggenius2/gosmi/models"
"github.com/sleepinggenius2/gosmi/types"
"github.com/influxdata/telegraf"
)
var errCannotFormatUnkownType = errors.New("cannot format value, unknown type")
type gosmiTranslator struct {
}
func NewGosmiTranslator(paths []string, log telegraf.Logger) (*gosmiTranslator, error) {
err := LoadMibsFromPath(paths, log, &GosmiMibLoader{})
if err == nil {
return &gosmiTranslator{}, nil
}
return nil, err
}
//nolint:revive //function-result-limit conditionally 5 return results allowed
func (g *gosmiTranslator) SnmpTranslate(oid string) (mibName string, oidNum string, oidText string, conversion string, err error) {
mibName, oidNum, oidText, conversion, _, err = snmpTranslateCall(oid)
return mibName, oidNum, oidText, conversion, err
}
// snmpTable resolves the given OID as a table, providing information about the
// table and fields within.
//
//nolint:revive //Too many return variable but necessary
func (g *gosmiTranslator) SnmpTable(oid string) (
mibName string, oidNum string, oidText string,
fields []Field,
err error) {
mibName, oidNum, oidText, _, node, err := snmpTranslateCall(oid)
if err != nil {
return "", "", "", nil, fmt.Errorf("translating: %w", err)
}
mibPrefix := mibName + "::"
col, tagOids := getIndex(mibPrefix, node)
for _, c := range col {
_, isTag := tagOids[mibPrefix+c]
fields = append(fields, Field{Name: c, Oid: mibPrefix + c, IsTag: isTag})
}
return mibName, oidNum, oidText, fields, nil
}
func (*gosmiTranslator) SnmpFormatEnum(oid string, value interface{}, full bool) (string, error) {
if value == nil {
return "", nil
}
//nolint:dogsled // only need to get the node
_, _, _, _, node, err := snmpTranslateCall(oid)
if err != nil {
return "", err
}
if node.Type == nil {
return "", errCannotFormatUnkownType
}
var v models.Value
if full {
v = node.FormatValue(value, models.FormatEnumName, models.FormatEnumValue)
} else {
v = node.FormatValue(value, models.FormatEnumName)
}
return v.String(), nil
}
func (*gosmiTranslator) SnmpFormatDisplayHint(oid string, value interface{}) (string, error) {
if value == nil {
return "", nil
}
//nolint:dogsled // only need to get the node
_, _, _, _, node, err := snmpTranslateCall(oid)
if err != nil {
return "", err
}
if node.Type == nil {
return "", errCannotFormatUnkownType
}
return node.FormatValue(value).String(), nil
}
func getIndex(mibPrefix string, node gosmi.SmiNode) (col []string, tagOids map[string]struct{}) {
// first attempt to get the table's tags
// mimcks grabbing INDEX {} that is returned from snmptranslate -Td MibName
indices := node.GetIndex()
tagOids = make(map[string]struct{}, len(indices))
for _, index := range indices {
tagOids[mibPrefix+index.Name] = struct{}{}
}
// grabs all columns from the table
// mimmicks grabbing everything returned from snmptable -Ch -Cl -c public 127.0.0.1 oidFullName
_, col = node.GetColumns()
return col, tagOids
}
//nolint:revive //Too many return variable but necessary
func snmpTranslateCall(oid string) (mibName string, oidNum string, oidText string, conversion string, node gosmi.SmiNode, err error) {
var out gosmi.SmiNode
var end string
if strings.ContainsAny(oid, "::") {
// split given oid
// for example RFC1213-MIB::sysUpTime.0
s := strings.SplitN(oid, "::", 2)
// moduleName becomes RFC1213
moduleName := s[0]
module, err := gosmi.GetModule(moduleName)
if err != nil {
return oid, oid, oid, "", gosmi.SmiNode{}, err
}
if s[1] == "" {
return "", oid, oid, "", gosmi.SmiNode{}, fmt.Errorf("cannot parse %v", oid)
}
// node becomes sysUpTime.0
node := s[1]
if strings.ContainsAny(node, ".") {
s = strings.SplitN(node, ".", 2)
// node becomes sysUpTime
node = s[0]
end = "." + s[1]
}
out, err = module.GetNode(node)
if err != nil {
return oid, oid, oid, "", out, err
}
if oidNum = out.RenderNumeric(); oidNum == "" {
return oid, oid, oid, "", out, fmt.Errorf("cannot translate %v into a numeric OID, please ensure all imported MIBs are in the path", oid)
}
oidNum = "." + oidNum + end
} else if strings.ContainsAny(oid, "abcdefghijklnmopqrstuvwxyz") {
//handle mixed oid ex. .iso.2.3
s := strings.Split(oid, ".")
for i := range s {
if strings.ContainsAny(s[i], "abcdefghijklmnopqrstuvwxyz") {
out, err = gosmi.GetNode(s[i])
if err != nil {
return oid, oid, oid, "", out, err
}
s[i] = out.RenderNumeric()
}
}
oidNum = strings.Join(s, ".")
out, err = gosmi.GetNodeByOID(types.OidMustFromString(oidNum))
if err != nil {
return oid, oid, oid, "", out, err
}
} else {
out, err = gosmi.GetNodeByOID(types.OidMustFromString(oid))
oidNum = oid
// ensure modules are loaded or node will be empty (might not error)
//nolint:nilerr // do not return the err as the oid is numeric and telegraf can continue
if err != nil || out.Name == "iso" {
return oid, oid, oid, "", out, nil
}
}
tc := out.GetSubtree()
for i := range tc {
// case where the mib doesn't have a conversion so Type struct will be nil
// prevents seg fault
if tc[i].Type == nil {
break
}
if tc[i].Type.Format != "" {
conversion = "displayhint"
} else {
switch tc[i].Type.Name {
case "InetAddress", "IPSIpAddress":
conversion = "ipaddr"
}
}
}
oidText = out.RenderQualified()
i := strings.Index(oidText, "::")
if i == -1 {
return "", oid, oid, "", out, errors.New("not found")
}
mibName = oidText[:i]
oidText = oidText[i+2:] + end
return mibName, oidNum, oidText, conversion, out, nil
}
// The following is for snmp_trap
type MibEntry struct {
MibName string
OidText string
}
func TrapLookup(oid string) (e MibEntry, err error) {
var givenOid types.Oid
if givenOid, err = types.OidFromString(oid); err != nil {
return e, fmt.Errorf("could not convert OID %s: %w", oid, err)
}
// Get node name
var node gosmi.SmiNode
if node, err = gosmi.GetNodeByOID(givenOid); err != nil {
return e, err
}
e.OidText = node.Name
// Add not found OID part
if !givenOid.Equals(node.Oid) {
e.OidText += "." + givenOid[len(node.Oid):].String()
}
// Get module name
module := node.GetModule()
if module.Name != "<well-known>" {
e.MibName = module.Name
}
return e, nil
}