github.com/influxdata/telegraf@v1.30.3/internal/snmp/translator_gosmi.go (about)

     1  package snmp
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/sleepinggenius2/gosmi"
     9  	"github.com/sleepinggenius2/gosmi/models"
    10  	"github.com/sleepinggenius2/gosmi/types"
    11  
    12  	"github.com/influxdata/telegraf"
    13  )
    14  
    15  type gosmiTranslator struct {
    16  }
    17  
    18  func NewGosmiTranslator(paths []string, log telegraf.Logger) (*gosmiTranslator, error) {
    19  	err := LoadMibsFromPath(paths, log, &GosmiMibLoader{})
    20  	if err == nil {
    21  		return &gosmiTranslator{}, nil
    22  	}
    23  	return nil, err
    24  }
    25  
    26  //nolint:revive //function-result-limit conditionally 5 return results allowed
    27  func (g *gosmiTranslator) SnmpTranslate(oid string) (mibName string, oidNum string, oidText string, conversion string, err error) {
    28  	mibName, oidNum, oidText, conversion, _, err = snmpTranslateCall(oid)
    29  	return mibName, oidNum, oidText, conversion, err
    30  }
    31  
    32  // snmpTable resolves the given OID as a table, providing information about the
    33  // table and fields within.
    34  //
    35  //nolint:revive //Too many return variable but necessary
    36  func (g *gosmiTranslator) SnmpTable(oid string) (
    37  	mibName string, oidNum string, oidText string,
    38  	fields []Field,
    39  	err error) {
    40  	mibName, oidNum, oidText, _, node, err := snmpTranslateCall(oid)
    41  	if err != nil {
    42  		return "", "", "", nil, fmt.Errorf("translating: %w", err)
    43  	}
    44  
    45  	mibPrefix := mibName + "::"
    46  
    47  	col, tagOids := getIndex(mibPrefix, node)
    48  	for _, c := range col {
    49  		_, isTag := tagOids[mibPrefix+c]
    50  		fields = append(fields, Field{Name: c, Oid: mibPrefix + c, IsTag: isTag})
    51  	}
    52  
    53  	return mibName, oidNum, oidText, fields, nil
    54  }
    55  
    56  func (g *gosmiTranslator) SnmpFormatEnum(oid string, value interface{}, full bool) (string, error) {
    57  	//nolint:dogsled // only need to get the node
    58  	_, _, _, _, node, err := snmpTranslateCall(oid)
    59  
    60  	if err != nil {
    61  		return "", err
    62  	}
    63  
    64  	var v models.Value
    65  	if full {
    66  		v = node.FormatValue(value, models.FormatEnumName, models.FormatEnumValue)
    67  	} else {
    68  		v = node.FormatValue(value, models.FormatEnumName)
    69  	}
    70  
    71  	return v.Formatted, nil
    72  }
    73  
    74  func getIndex(mibPrefix string, node gosmi.SmiNode) (col []string, tagOids map[string]struct{}) {
    75  	// first attempt to get the table's tags
    76  	tagOids = map[string]struct{}{}
    77  
    78  	// mimcks grabbing INDEX {} that is returned from snmptranslate -Td MibName
    79  	for _, index := range node.GetIndex() {
    80  		tagOids[mibPrefix+index.Name] = struct{}{}
    81  	}
    82  
    83  	// grabs all columns from the table
    84  	// mimmicks grabbing everything returned from snmptable -Ch -Cl -c public 127.0.0.1 oidFullName
    85  	_, col = node.GetColumns()
    86  
    87  	return col, tagOids
    88  }
    89  
    90  //nolint:revive //Too many return variable but necessary
    91  func snmpTranslateCall(oid string) (mibName string, oidNum string, oidText string, conversion string, node gosmi.SmiNode, err error) {
    92  	var out gosmi.SmiNode
    93  	var end string
    94  	if strings.ContainsAny(oid, "::") {
    95  		// split given oid
    96  		// for example RFC1213-MIB::sysUpTime.0
    97  		s := strings.SplitN(oid, "::", 2)
    98  		// moduleName becomes RFC1213
    99  		moduleName := s[0]
   100  		module, err := gosmi.GetModule(moduleName)
   101  		if err != nil {
   102  			return oid, oid, oid, "", gosmi.SmiNode{}, err
   103  		}
   104  		if s[1] == "" {
   105  			return "", oid, oid, "", gosmi.SmiNode{}, fmt.Errorf("cannot parse %v", oid)
   106  		}
   107  		// node becomes sysUpTime.0
   108  		node := s[1]
   109  		if strings.ContainsAny(node, ".") {
   110  			s = strings.SplitN(node, ".", 2)
   111  			// node becomes sysUpTime
   112  			node = s[0]
   113  			end = "." + s[1]
   114  		}
   115  
   116  		out, err = module.GetNode(node)
   117  		if err != nil {
   118  			return oid, oid, oid, "", out, err
   119  		}
   120  
   121  		if oidNum = out.RenderNumeric(); oidNum == "" {
   122  			return oid, oid, oid, "", out, fmt.Errorf("cannot translate %v into a numeric OID, please ensure all imported MIBs are in the path", oid)
   123  		}
   124  
   125  		oidNum = "." + oidNum + end
   126  	} else if strings.ContainsAny(oid, "abcdefghijklnmopqrstuvwxyz") {
   127  		//handle mixed oid ex. .iso.2.3
   128  		s := strings.Split(oid, ".")
   129  		for i := range s {
   130  			if strings.ContainsAny(s[i], "abcdefghijklmnopqrstuvwxyz") {
   131  				out, err = gosmi.GetNode(s[i])
   132  				if err != nil {
   133  					return oid, oid, oid, "", out, err
   134  				}
   135  				s[i] = out.RenderNumeric()
   136  			}
   137  		}
   138  		oidNum = strings.Join(s, ".")
   139  		out, _ = gosmi.GetNodeByOID(types.OidMustFromString(oidNum))
   140  	} else {
   141  		out, err = gosmi.GetNodeByOID(types.OidMustFromString(oid))
   142  		oidNum = oid
   143  		// ensure modules are loaded or node will be empty (might not error)
   144  		//nolint:nilerr // do not return the err as the oid is numeric and telegraf can continue
   145  		if err != nil || out.Name == "iso" {
   146  			return oid, oid, oid, "", out, nil
   147  		}
   148  	}
   149  
   150  	tc := out.GetSubtree()
   151  
   152  	for i := range tc {
   153  		// case where the mib doesn't have a conversion so Type struct will be nil
   154  		// prevents seg fault
   155  		if tc[i].Type == nil {
   156  			break
   157  		}
   158  		switch tc[i].Type.Name {
   159  		case "MacAddress", "PhysAddress":
   160  			conversion = "hwaddr"
   161  		case "InetAddressIPv4", "InetAddressIPv6", "InetAddress", "IPSIpAddress":
   162  			conversion = "ipaddr"
   163  		}
   164  	}
   165  
   166  	oidText = out.RenderQualified()
   167  	i := strings.Index(oidText, "::")
   168  	if i == -1 {
   169  		return "", oid, oid, "", out, errors.New("not found")
   170  	}
   171  	mibName = oidText[:i]
   172  	oidText = oidText[i+2:] + end
   173  
   174  	return mibName, oidNum, oidText, conversion, out, nil
   175  }
   176  
   177  // The following is for snmp_trap
   178  type MibEntry struct {
   179  	MibName string
   180  	OidText string
   181  }
   182  
   183  func TrapLookup(oid string) (e MibEntry, err error) {
   184  	var givenOid types.Oid
   185  	if givenOid, err = types.OidFromString(oid); err != nil {
   186  		return e, fmt.Errorf("could not convert OID %s: %w", oid, err)
   187  	}
   188  
   189  	// Get node name
   190  	var node gosmi.SmiNode
   191  	if node, err = gosmi.GetNodeByOID(givenOid); err != nil {
   192  		return e, err
   193  	}
   194  	e.OidText = node.Name
   195  
   196  	// Add not found OID part
   197  	if !givenOid.Equals(node.Oid) {
   198  		e.OidText += "." + givenOid[len(node.Oid):].String()
   199  	}
   200  
   201  	// Get module name
   202  	module := node.GetModule()
   203  	if module.Name != "<well-known>" {
   204  		e.MibName = module.Name
   205  	}
   206  
   207  	return e, nil
   208  }