예제 #1
0
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)
예제 #2
0
    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
예제 #3
0
  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)}
예제 #4
0
    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
예제 #5
0
    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)}
예제 #6
0
    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