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 }