def snmpResult(x, type=None): # We don't care about the type in the annotator if type is None: type = 'INTEGER' if isinstance(x, int) else 'OCTETSTR' if isinstance(x, bytes): return snmp.ResultTuple(x, type) else: return snmp.ResultTuple(str(x), type)
def walk(self, target, oid, vlan=None): sess = self._snmp_session(target, vlan) ret = {} nextoid = oid offset = 0 # Abort the walk when it exits the OID tree we are interested in while nextoid.startswith(oid): var_list = self.netsnmp.VarList( self.netsnmp.Varbind(nextoid, offset)) sess.getbulk(nonrepeaters=0, maxrepetitions=target.max_size, varlist=var_list) # WORKAROUND FOR NEXUS BUG (2014-11-24) # Indy told blueCmd that Nexus silently drops the SNMP response # if the packet is fragmented. Try with large size first, but drop down # to smaller one. if sess.ErrorStr == 'Timeout': if target.max_size == 1: raise TimeoutError('Timeout getting %s from %s' % (nextoid, target.host)) target.max_size = int(target.max_size / 16) logging.debug( 'Timeout getting %s from %s, lowering max size to %d' % (nextoid, target.host, target.max_size)) continue if sess.ErrorStr != '': raise snmp.SnmpError('SNMP error while walking host %s: %s' % (target.host, sess.ErrorStr)) for result in var_list: currentoid = '%s.%s' % (result.tag, int(result.iid)) # We don't want to save extra oids that the bulk walk might have # contained. if not currentoid.startswith(oid): break try: ret[currentoid] = snmp.ResultTuple(result.val.decode(), result.type) except UnicodeDecodeError: ret[currentoid] = snmp.ResultTuple(result.val, result.type) # Continue bulk walk offset = int(var_list[-1].iid) nextoid = var_list[-1].tag return ret
def get(self, target, oid): sess = self._snmp_session(target, timeout=500000, retries=2) var = self.netsnmp.Varbind(oid) var_list = self.netsnmp.VarList(var) sess.get(var_list) if sess.ErrorStr != '': if sess.ErrorStr == 'Timeout': raise TimeoutError('Timeout getting %s from %s' % (oid, target.host)) raise snmp.SnmpError('SNMP error while talking to host %s: %s' % ( target.host, sess.ErrorStr)) return {var.tag: snmp.ResultTuple(var.val.decode(), var.type)}
def process_overrides(self, results): if not self.overrides: return results overridden_oids = set(self.overrides.keys()) overriden_results = results for (oid, vlan), result in results.items(): root = '.'.join(oid.split('.')[:-1]) if root in overridden_oids: overriden_results[(oid, vlan)] = snmp.ResultTuple( result.value, self.overrides[root]) return overriden_results
def get(self, target, oid): # Nexus is quite slow sometimes to answer SNMP so use a high # timeout on these initial requests before failing out sess = self._snmp_session(target, timeout=5000000, retries=2) var = self.netsnmp.Varbind(oid) var_list = self.netsnmp.VarList(var) sess.get(var_list) if sess.ErrorStr != '': if sess.ErrorStr == 'Timeout': raise TimeoutError('Timeout getting %s from %s' % (oid, target.host)) raise snmp.SnmpError('SNMP error while talking to host %s: %s' % (target.host, sess.ErrorStr)) return {var.tag: snmp.ResultTuple(var.val.decode(), var.type)}
def annotate(self, results): annotations = self.config.get('annotations', []) # Calculate map to skip annotation if we're sure we're not going to annotate # TODO(bluecmd): This could be cached annotation_map = {} for annotation in annotations: for annotate in annotation['annotate']: # Support for processing the index (for OIDs that have X.Y where we're # interested in joining on X) if '[' in annotate: annotate, offset = annotate.split('[', 1) offset = int(offset.strip(']')) else: offset = None # Add '.' to not match .1.2.3 if we want to annotate 1.2.30 annotation_map[(annotate + '.', offset)] = annotation['with'] labelification = set( [x + '.' for x in self.config.get('labelify', [])]) # Pre-fill the OID/Enum cache to allow annotations to get enum values for (oid, ctxt), result in results.items(): resolve = self.mibresolver.resolve(oid) self.mibcache[oid] = resolve if resolve is None: logging.warning('Failed to look up OID %s, ignoring', oid) continue # Calculate annotator map split_oid_map = collections.defaultdict(dict) for (oid, ctxt), result in results.items(): name, _ = self.mibcache[oid] if '.' not in name: continue _, index = name.split('.', 1) key = oid[:-(len(index))] split_oid_map[(key, ctxt)][index] = result.value annotated_results = {} for (oid, ctxt), result in results.items(): labels = {} vlan = None # TODO(bluecmd): If we support more contexts we need to be smarter here if not ctxt is None: vlan = ctxt name, enum = self.mibcache[oid] if not '::' in name: logging.warning('OID %s resolved to %s (no MIB), ignoring', oid, name) continue mib, part = name.split('::', 1) obj, index = part.split('.', 1) if '.' in part else (part, None) labels = {} if not vlan is None: labels['vlan'] = vlan labels.update( self.annotated_join(oid, index, ctxt, annotation_map, split_oid_map, results)) # Handle labelification if oid[:-(len(index) if index else 0)] in labelification: # Skip empty strings or non-strings that are up for labelification if result.value == '' or result.type not in self.LABEL_TYPES: continue bytes_value = result.value if isinstance(result.value, str): bytes_value = result.value.encode() labels['value'] = self.string_to_label_value(bytes_value) labels['hex'] = binascii.hexlify(bytes_value).decode() result = snmp.ResultTuple('NaN', 'ANNOTATED') # Do something almost like labelification for enums if enum: enum_value = enum.get(result.value, None) if enum_value is None: logging.warning( 'Got invalid enum value for %s (%s), not labling', oid, result.value) else: labels['enum'] = enum_value annotated_results[(oid, vlan)] = AnnotatedResultEntry( result, mib, obj, index, labels) logging.debug('Annotation completed for %d metrics', len(annotated_results)) return annotated_results