def address_index_to_ip(cls, index): """Convert a row index from cIpAddressTable to an IP object.""" entry = cls.nodes['cIpAddressPfxOrigin'] index = OID(index) if entry.oid.is_a_prefix_of(index): # Chop off the entry OID+column prefix index = OID(index.strip_prefix(entry.oid)[1:]) return super(CiscoIetfIpMib, cls).address_index_to_ip(index)
def address_index_to_ip(cls, index): """Convert a row index from ipAddressTable to an IP object.""" index = OID(index) if 'ipAddressEntry' in cls.nodes: entry = cls.nodes['ipAddressEntry'] if entry.oid.is_a_prefix_of(index): # Chop off the entry OID+column prefix index = OID(index.strip_prefix(entry.oid)[1:]) ip = cls.inetaddress_to_ip(index) return ip
def _result_formatter(result): formatted_result = {} for varlist in result.values(): # Build a table structure for oid in sorted(varlist.keys()): if not table.table.oid.is_a_prefix_of(oid): raise MibRetrieverError( "Received wrong response from client," "%s is not in %s" % (oid, table.table.oid)) # Extract table position of value oid_suffix = OID(oid).strip_prefix(table.row.oid) column_no = oid_suffix[0] row_index = oid_suffix[1:] if column_no not in table.reverse_column_index: self._logger.warning( "device response has bad table index %s in %s::%s, " "ignoring", oid_suffix, self.mib['moduleName'], table_name) continue column_name = table.reverse_column_index[column_no] if row_index not in formatted_result: formatted_result[row_index] = \ MibTableResultRow(row_index, table.columns.keys()) formatted_result[row_index][column_name] = varlist[oid] return formatted_result
def _make_result_dict(self, sensor_oid, base_oid, serial, desc, u_o_m=None, precision=0, scale=None, name=None): """ Make a simple dictionary to return to plugin""" if not sensor_oid or not base_oid or not serial or not desc: return {} oid = OID(base_oid) + OID(sensor_oid) internal_name = serial + desc return {'oid': str(oid), 'unit_of_measurement': u_o_m, 'precision': precision, 'scale': scale, 'description': desc, 'name': name, 'internal_name': internal_name, 'mib': self.get_module_name(), }
def prefix_index_to_ip(cls, index, prefix_entry=None): """Convert a row index from cIpAddressPfxTable to an IP object.""" entry = cls.nodes['cIpAddressPfxOrigin'] stripped_index = OID(index).strip_prefix(entry.oid) return super(CiscoIetfIpMib, cls).prefix_index_to_ip(stripped_index)
def retrieve_alternate_bridge_mibs(self): """Retrieves a list of alternate bridge mib instances. This is accomplished by looking at entLogicalTable. Returns a deferred whose result value is a list of tuples:: (entity_description, community) :NOTE: Some devices will return entities with the same community. These should effectively be filtered out for polling purposes. A Cisco WS-C3560CG-8PC-S running IOS 15.0(2)SE has also been shown to return communities with null bytes, which are unusable and will be filtered. """ # Define this locally to avoid external overhead bridge_mib_oid = OID('.1.3.6.1.2.1.17') def _bridge_mib_filter(result): def _is_bridge_mib_instance_with_valid_community(row): return (row['entLogicalType'] and OID(row['entLogicalType']) == bridge_mib_oid and '\x00' not in row['entLogicalCommunity']) new_result = [(r['entLogicalDescr'], r['entLogicalCommunity']) for r in result.values() if _is_bridge_mib_instance_with_valid_community(r)] return new_result df = self.retrieve_columns( ['entLogicalDescr', 'entLogicalType', 'entLogicalCommunity']) df.addCallback(_bridge_mib_filter) return df
def transform_trap_type(pdu): """Transforms trap information from an SNMP-v1 pdu to something that is consistent with SNMP-v2c, as documented in RFC2576. :returns: A tuple of (snmpTrapOID, genericType) """ enterprise_type = POINTER(c_long * pdu.enterprise_length) enterprise_p = cast(pdu.enterprise, enterprise_type) enterprise = OID(enterprise_p.contents) generic = pdu.trap_type # According to RFC2576 "Coexistence between Version 1, Version 2, # and Version 3 of the Internet-standard Network Management # Framework", we build snmpTrapOID from the snmp-v1 trap by # combining enterprise + 0 + specific trap parameter IF the # generic trap parameter is 6. If not, the traps are defined as # 1.3.6.1.6.3.1.1.5 + (generic trap parameter + 1) if generic == netsnmp.SNMP_TRAP_ENTERPRISESPECIFIC: snmp_trap_oid = enterprise + [0, pdu.specific_type] else: snmp_trap_oid = SNMP_TRAPS + [generic + 1] generic_type = TRAP_MAP.get(generic, str(generic)).upper() return snmp_trap_oid, generic_type
def get(self, query="1.3.6.1.2.1.1.1.0"): """Performs an SNMP GET query. :param query: OID to query for. :returns: The response value """ oid = OID(query) response = self.handle.sget([oid]) if response: value = response.get(oid, None) if isinstance(value, tuple): return OID(value) return value else: return None
def value_to_str(value): """Converts a value from a varbind to a string, as that is what the SNMPTrap API expects :-P """ if isinstance(value, tuple): return str(OID(value)) else: return str(value)
def get_dot1x_enabled_interfaces(self): _logger.error("Querying for dot1x enabled interfaces on Cisco") names = self._get_interface_names() return { names.get(OID(oid)[-1]): six.byte2int(state) & self.DOT1X_AUTHENTICATOR for oid, state in self._bulkwalk(self.dot1xPortAuth) }
def _fetch_sysobjectid(self): result = yield self.snmpv2_mib.get_sysObjectID() if not result: raise InvalidResponseError( "No response on sysObjectID query.", result, self.agent ) oid = OID(result) self._logger.debug("sysObjectID is %s", oid) defer.returnValue(oid)
def multi_set(self, varbinds): """Performs SNMP set with multiple operations :type varbinds: list[PDUVarbind] """ self.handle.sset([ PDUVarbind(OID(v.oid), self.translate_type(v.type), v.value) for v in varbinds ])
def callback(self, src, pdu): """Handles trap callbacks from a TrapSession""" if not self._client_callback: return agent_addr = None generic_type = None community = pdu.community[:pdu.community_len] varbinds = netsnmp.getResult(pdu, _logger) if pdu.version == netsnmp.SNMP_VERSION_1: version = 1 agent_addr = ".".join(str(d) for d in pdu.agent_addr) snmp_trap_oid, generic_type = transform_trap_type(pdu) uptime = pdu.time elif pdu.version == netsnmp.SNMP_VERSION_2c: version = 2 _time_oid, time_val = varbinds.pop(0) _trap_oid_oid, trap_oid = varbinds.pop(0) uptime = time_val snmp_trap_oid = OID(trap_oid) else: raise UnsupportedSnmpVersionError(pdu.version) # Dump varbinds to debug log _logger.debug("varbinds: %r", varbinds) # Add remaining varbinds to dict varbind_dict = dict( (str(OID(oid)), value_to_str(value)) for oid, value in varbinds) trap = SNMPTrap( str(src), agent_addr or str(src), None, generic_type, str(snmp_trap_oid), uptime, community, version, varbind_dict, ) self._client_callback(trap)
def _get_sysvariable(self, var): """Retrieves a system variable of the first agent instance. Will first try get-next on {var}, then fall back to getting {var}.0 on failure. This is a workaround for a faulty SNMP agent implementation observed in Weathergoose devices, where e.g. a GET-NEXT on sysObjectID would consistently return sysUpTime.0 instead. """ oid = self.nodes[var].oid result = yield self.get_next(var) if result: defer.returnValue(result) else: oid = oid + OID('0') result = yield self.agent_proxy.get([str(oid)]) for key, value in result.items(): if oid == OID(key): defer.returnValue(value)
def _process_alias_mapping(self, alias_mapping): mapping = defaultdict(list) for (phys_index, _logical), rowpointer in alias_mapping.items(): # Last element is ifindex. Preceding elements is an OID. ifindex = OID(rowpointer)[-1] mapping[phys_index].append(ifindex) self._logger.debug("alias mapping: %r", mapping) return mapping
def _get_cpu_names(self, indexes): if not indexes: defer.returnValue({}) self._logger.debug("getting cpu names from ENTITY-MIB") base_oid = EntityMib.nodes['entPhysicalName'].oid oids = [str(base_oid + (index, )) for index in indexes] names = yield self.agent_proxy.get(oids) self._logger.debug("cpu name result: %r", names) names = {OID(oid)[-1]: value for oid, value in names.items() if value} defer.returnValue(names)
def bulkwalk(self, query="1.3.6.1.2.1.1.1.0", strip_prefix=False): """Performs an SNMP walk on the host, using GETBULK requests. Will raise an UnsupportedSnmpVersionError if the current version is anything other than 2c. :param query: root OID for walk :param strip_prefix: A boolean. Set to True to strip the query prefix from the OIDs in the response. Default is False. :returns: A list of (response_oid, value) pairs, where the query prefix has been stripped from the response_oids. """ if str(self.version) != "2c": raise errors.UnsupportedSnmpVersionError( "Cannot use BULKGET in SNMP version " + self.version) result = [] root_oid = OID(query) current_oid = root_oid while 1: response = self.handle.sgetbulk(self.NON_REPEATERS, self.MAX_REPETITIONS, [current_oid]) if response is None: break for response_oid, value in response: if value is None: return result current_oid = OID(response_oid) if (not root_oid.is_a_prefix_of(current_oid) or current_oid == root_oid): return result if isinstance(value, tuple): value = str(OID(value))[1:] result.append((str(current_oid)[1:], value)) return result
def _make_result_dict(self, sensor_oid, base_oid, serial, desc, u_o_m=None, **kwargs): """Make a simple dictionary to return to plugin""" if not sensor_oid or not base_oid or not serial or not desc: return {} oid = OID(base_oid) + OID(sensor_oid) internal_name = smart_str(serial) + desc res = { 'oid': oid, 'unit_of_measurement': u_o_m, 'description': desc, 'internal_name': internal_name, 'mib': self.get_module_name(), } res.update(kwargs) return res
def test_entity_to_powersupply_or_fan(self): entity = { "entPhysicalName": "PSU 1", "entPhysicalModelName": "ABCDEF 720", "entPhysicalDescr": "Next-gen PSU Power fidelity foobar", "entPhysicalClass": NetboxEntity.CLASS_POWERSUPPLY, "entPhysicalSerialNum": "123ABC", 0: OID('.1'), } unit = _entity_to_powersupply_or_fan(entity) assert isinstance(unit, PowerSupplyOrFan) assert isinstance(unit.device, Device)
def test_get_vlan_hp(self): self.handler = ManagementFactory.get_instance(self.netboxHP) # get hold of the read-only Snmp-object self.snmpReadOnlyHandler = self.handler._get_read_only_handle() # replace get-method on Snmp-object with a mock-method # this get-method returns a vlan-number self.snmpReadOnlyHandler.get = Mock(return_value=666) ifc = Mock(baseport=1) self.assertEqual(self.handler.get_interface_native_vlan(ifc), 666, "getVlan-test failed") self.snmpReadOnlyHandler.get.assert_called_with( OID('.1.3.6.1.2.1.17.7.1.4.5.1.1.1'))
def convert_oids(mib): """Converts a mib data structure's oid strings to OID objects. mib is expected to be a data structure as dumped by the smidump utility (using the -f python option). """ for node in chain( mib.get('nodes', {}).values(), mib.get('notifications', {}).values() ): if isinstance(node['oid'], str): node['oid'] = OID(node['oid'])
def convert_oids(mib): """Convert a mib data structure's oid strings to OID objects. mib is expected to be a data structure as dumped by the smidump utility (using the -python option). """ for node_name in mib['nodes']: node = mib['nodes'][node_name] if isinstance(node['oid'], basestring): #oid_tuple = tuple(int(i) for i in node['oid'].split('.')) node['oid'] = OID(node['oid'])
def _get_sensor_count(self): """Count all available sensors in this WxGoose""" sensor_counts_oid = self.mib['nodes']['sensorCounts']['oid'] sensor_counts = yield self.retrieve_column('sensorCounts') mapped_counts = ((sensor_counts_oid + OID(key[0:1]), count) for key, count in sensor_counts.items()) result = dict((self.oid_name_map[oid], count) for oid, count in mapped_counts if oid in self.oid_name_map) self._debug('_get_sensor_count: result = %s', result) defer.returnValue(result)
def value_to_str(value): """Converts a value from a varbind to a string, as that is what the SNMPTrap API expects :-P """ if isinstance(value, tuple): return str(OID(value)) elif isinstance(value, six.binary_type): try: return value.decode("utf-8") except UnicodeDecodeError: pass return str(value)
def _process_alias_mapping(self, alias_mapping): mapping = defaultdict(list) try: for (phys_index, _logical), rowpointer in alias_mapping.items(): # Last element is ifindex. Preceding elements is an OID. ifindex = OID(rowpointer)[-1] mapping[phys_index].append(ifindex) except ValueError: self._logger.warning( "device has broken entAliasMappingTable implementation") self._logger.debug("alias mapping: %r", mapping) return mapping
def prefix_index_to_ip(cls, index, prefix_entry='ipAddressPrefixEntry'): """Convert a row index from ipAddressPrefixTable to an IP object.""" index = OID(index) if prefix_entry in cls.nodes: entry = cls.nodes[prefix_entry] if entry.oid.is_a_prefix_of(index): # Chop off the entry OID+column prefix index = OID(index.strip_prefix(entry.oid)[1:]) if len(index) < 4: cls._logger.debug("prefix_index_to_ip: index too short: %r", index) return None ifindex = index[0] addr_oid = index[1:-1] prefix_length = index[-1] ip = cls.inetaddress_to_ip(addr_oid) if ip: prefix = ip.make_net(prefix_length) return prefix
def _response_handler(self, result): """Handles a sysObjectID response.""" if not result: raise InvalidResponseError("No response on sysObjectID query.", result, self.agent) # Just pick the first result, there should never really be multiple self.sysobjectid = str(OID(result)) # ObjectIDs in the database are stored without the preceding dot. if self.sysobjectid[0] == '.': self.sysobjectid = self.sysobjectid[1:] self._logger.debug("sysObjectID is %s", self.sysobjectid) df = self._get_type_from_db() df.addCallback(self._check_for_typechange) df.addCallback(self._set_type) return df
def set(self, query, type, value): """Performs an SNMP SET operations. :param query: OID to set :param type: type of value to set. This may be i: INTEGER u: unsigned INTEGER t: TIMETICKS a: IPADDRESS o: OBJID s: OCTETSTRING U: COUNTER64 (version 2 and above) x: OCTETSTRING :param value: the value to set. Must ofcourse match type: i = 2, s = 'string' """ self.handle.sset([PDUVarbind(OID(query), self.translate_type(type), value)])
def resultFormatter(result): formatted_result = {} # result keys may be OID objects/tuples or strings, depending on # snmp library used if node.oid not in result and str(node.oid) not in result: self._logger.debug( "%s (%s) seems to be unsupported, result " "keys were: %r", column_name, node.oid, result.keys()) return {} varlist = result.get(node.oid, result.get(str(node.oid), None)) for oid, value in varlist.items(): # Extract index information from oid row_index = OID(oid).strip_prefix(node.oid) formatted_result[row_index] = value return formatted_result
class GeistMibV3(ItWatchDogsMibV3): """ A MibRetriever for retrieving information from Geist branded WeatherGoose products. Based on the GEIST-MIB-V3, which is more or less derived from the IT-WATCHDOGS-MIB-V3. Objects names in the derived MIB are mostly the same, except for a `cm` prefix on notification objects, which has changed to the `gst` prefix. This implementation does not use the notification objects for anything, so we don't need to care about this name change here. """ mib = get_mib('GEIST-MIB-V3') oid_name_map = {OID(attrs['oid']): name for name, attrs in mib['nodes'].items()} lowercase_nodes = {key.lower(): key for key in mib['nodes']}
class GeistMibV4(ItWatchDogsMibV4): """ A MibRetriever for retrieving information from Geist branded WeatherGoose products. Based on the GEIST-V4-MIB, which is more or less derived from the IT-WATCHDOGS-V4-MIB. Objects names in the derived MIB seems to be the same. """ from nav.smidumps.geist_mibv4 import MIB as mib oid_name_map = { OID(attrs['oid']): name for name, attrs in mib['nodes'].items() } lowercase_nodes = {key.lower(): key for key in mib['nodes']}
def _verify_sensor(self, object_name): result = yield self.get_next(object_name) if result: node = self.nodes[object_name] oid = node.oid + OID('.0') sensor = { 'oid': str(oid), 'internal_name': object_name, 'name': object_name, 'description': node.raw_mib_data.get('description', object_name), 'mib': self.mib['moduleName'], 'scale': None, } units = node.raw_mib_data.get('units', '') sensor['unit_of_measurement'] = units for mibunits, navunits in UNIT_TRANSLATION.items(): if units.lower().startswith(mibunits.lower()): sensor.update(navunits) if object_name == 'loadDistributionBreakerStatus': sensor['unit_of_measurement'] = 'boolean' returnValue(sensor)