--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/snmp/lldp.py Mon Mar 17 23:54:00 2014 +0200
@@ -0,0 +1,139 @@
+"""
+ Requirements:
+ memoized-property
+
+ Setup:
+ ./opt/bin/build-pysnmp-mib -o usr/mibs/LLDP-MIB.py etc/mibs/LLDP-MIB
+
+ Run:
+ PYSNMP_MIB_DIRS=usr/mibs/ ./opt/bin/python ...
+
+"""
+
+from pvl.snmp import snmp
+from pvl.snmp.snmp import pysnmp
+
+from memoized_property import memoized_property
+
+import logging; log = logging.getLogger('pvl.snmp.lldp')
+
+def macaddr (value) :
+ """
+ Excepts a MAC address from an SNMP OctetString.
+ """
+
+ return ':'.join('{octet:02x}'.format(octet=c) for c in value.asNumbers())
+
+class LLDPAgent (snmp.SNMPAgent) :
+ """
+ Query LLDP info from a remote agent.
+ """
+
+ @classmethod
+ def _chassis_id (cls, chassis_id, subtype) :
+ log.debug("%s: %r", subtype, chassis_id)
+
+ # XXX: reference names from LLDP-MIB.py
+ if subtype == 4:
+ return macaddr(chassis_id)
+ else :
+ return chassis_id
+
+ @classmethod
+ def _port_id (cls, port_id, subtype, desc) :
+ log.debug("%s: %r (%s)", subtype, port_id, desc)
+
+ # XXX: reference names from LLDP-MIB.py
+ if subtype == 5: # interfaceName -> IF-MIB::ifName ?
+ return str(port_id)
+ elif subtype == 3 : # macAddress
+ if desc :
+ # prefer desc in this case
+ return desc
+ else :
+ return macaddr(port_id)
+ elif subtype == 7 : # local
+ return str(port_id) # XXX: integer?
+ else :
+ log.warn("unknown subtype=%d: %r", subtype, port_id)
+
+ return port_id
+
+ LLDP_LOC_CHASSIS_ID = pysnmp.MibVariable('LLDP-MIB', 'lldpLocChassisId')
+ LLDP_LOC_CHASSIS_ID_SUBTYPE = pysnmp.MibVariable('LLDP-MIB', 'lldpLocChassisIdSubtype')
+ LLDP_LOC_SYS_NAME = pysnmp.MibVariable('LLDP-MIB', 'lldpLocSysName')
+
+ @memoized_property
+ def local (self) :
+ """
+ Describe the local system.
+ """
+
+ for idx, data in self.table(
+ self.LLDP_LOC_CHASSIS_ID,
+ self.LLDP_LOC_CHASSIS_ID_SUBTYPE,
+ self.LLDP_LOC_SYS_NAME,
+ ) :
+ return {
+ 'chassis': self._chassis_id(data[self.LLDP_LOC_CHASSIS_ID], data[self.LLDP_LOC_CHASSIS_ID_SUBTYPE]),
+ 'sys_name': str(data[self.LLDP_LOC_SYS_NAME]),
+ }
+
+ LLDP_LOC_PORT_DESC = pysnmp.MibVariable('LLDP-MIB', 'lldpLocPortDesc')
+ LLDP_LOC_PORT_ID = pysnmp.MibVariable('LLDP-MIB', 'lldpLocPortId')
+ LLDP_LOC_PORT_ID_SUBTYPE = pysnmp.MibVariable('LLDP-MIB', 'lldpLocPortIdSubtype')
+
+ @memoized_property
+ def ports (self) :
+ """
+ Describe the local ports.
+ """
+
+ ports = { }
+
+ for idx, data in self.table(
+ self.LLDP_LOC_PORT_DESC,
+ self.LLDP_LOC_PORT_ID,
+ self.LLDP_LOC_PORT_ID_SUBTYPE,
+ ) :
+ port, = idx
+
+ ports[int(port)] = {
+ 'port_id': int(port),
+ 'port': self._port_id(data[self.LLDP_LOC_PORT_ID], data[self.LLDP_LOC_PORT_ID_SUBTYPE], data[self.LLDP_LOC_PORT_DESC]),
+ }
+
+ return ports
+
+ def port (self, port) :
+ return self.ports[int(port)]
+
+ LLDP_REM_CHASSIS_ID = pysnmp.MibVariable('LLDP-MIB', 'lldpRemChassisId')
+ LLDP_REM_CHASSIS_ID_SUBTYPE = pysnmp.MibVariable('LLDP-MIB', 'lldpRemChassisIdSubtype')
+ LLDP_REM_SYS_NAME = pysnmp.MibVariable('LLDP-MIB', 'lldpRemSysName')
+ LLDP_REM_PORT_ID = pysnmp.MibVariable('LLDP-MIB', 'lldpRemPortId')
+ LLDP_REM_PORT_ID_SUBTYPE = pysnmp.MibVariable('LLDP-MIB', 'lldpRemPortIdSubtype')
+ LLDP_REM_PORT_DESC = pysnmp.MibVariable('LLDP-MIB', 'lldpRemPortDesc')
+
+ def remotes (self) :
+ """
+ Describe remote systems, indexed by local port.
+ """
+
+ for idx, data in self.table(
+ self.LLDP_REM_CHASSIS_ID,
+ self.LLDP_REM_CHASSIS_ID_SUBTYPE,
+ self.LLDP_REM_SYS_NAME,
+ self.LLDP_REM_PORT_ID,
+ self.LLDP_REM_PORT_ID_SUBTYPE,
+ self.LLDP_REM_PORT_DESC,
+ ) :
+ time, port, idx = idx
+
+ yield int(port), {
+ 'chassis': self._chassis_id(data[self.LLDP_REM_CHASSIS_ID], data[self.LLDP_REM_CHASSIS_ID_SUBTYPE]),
+ 'sys_name': str(data[self.LLDP_REM_SYS_NAME]),
+ 'port': self._port_id(data[self.LLDP_REM_PORT_ID], data[self.LLDP_REM_PORT_ID_SUBTYPE], data[self.LLDP_REM_PORT_DESC]),
+ }
+
+