pvl/snmp/lldp.py
author Tero Marttila <terom@paivola.fi>
Mon, 17 Mar 2014 23:54:00 +0200
changeset 389 93e3b199cbe6
child 394 7077379fb5a0
permissions -rw-r--r--
add missing pvl.snmp.* files
"""
    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]),
            }