def _snmp_map_switch(switch, password, user): haveqbridge = False mactobridge = {} conn = snmp.Session(switch, password, user) ifnamemap = get_portnamemap(conn) for vb in conn.walk('1.3.6.1.2.1.17.7.1.2.2.1.2'): haveqbridge = True oid, bridgeport = vb if not bridgeport: continue oid = str(oid).rsplit('.', 6) # if 7, then oid[1] would be vlan id macaddr = '{0:02x}:{1:02x}:{2:02x}:{3:02x}:{4:02x}:{5:02x}'.format( *([int(x) for x in oid[-6:]])) mactobridge[macaddr] = int(bridgeport) if not haveqbridge: for vb in conn.walk('1.3.6.1.2.1.17.4.3.1.2'): oid, bridgeport = vb if not bridgeport: continue oid = str(oid).rsplit('.', 6) macaddr = '{0:02x}:{1:02x}:{2:02x}:{3:02x}:{4:02x}:{5:02x}'.format( *([int(x) for x in oid[-6:]])) mactobridge[macaddr] = int(bridgeport) vlanstocheck = set([]) try: #ciscoiftovlanmap = {} for vb in conn.walk('.1.3.6.1.4.1.9.9.68.1.2.2.1.2'): vlanstocheck.add(vb[1]) #ciscotrunktovlanmap = {} for vb in conn.walk('.1.3.6.1.4.1.9.9.46.1.6.1.1.5'): vlanstocheck.add(vb[1]) except Exception: # We might have crashed snmp on a non-cisco switch # in such a case, delay 8 seconds to allow recovery to complete eventlet.sleep(8) if not vlanstocheck: vlanstocheck.add(None) bridgetoifmap = {} for vlan in vlanstocheck: if vlan: if user: conn = snmp.Session(switch, password, user, 'vlan-{}'.format(vlan)) else: if not isinstance(password, str): password = password.decode('utf8') conn = snmp.Session(switch, '{}@{}'.format(password, vlan)) for vb in conn.walk('1.3.6.1.2.1.17.1.4.1.2'): bridgeport, ifidx = vb bridgeport = int(str(bridgeport).rsplit('.', 1)[1]) try: bridgetoifmap[bridgeport] = int(ifidx) except ValueError: # ifidx might be '', skip in such a case continue #OFFLOAD: end of need to offload? return mactobridge, ifnamemap, bridgetoifmap
def _extract_neighbor_data_b(args): """Build LLDP data about elements connected to switch args are carried as a tuple, because of eventlet convenience """ switch, password, user = args conn = snmp.Session(switch, password, user) sid = None lldpdata = {} for sysid in conn.walk('1.3.6.1.2.1.1.2'): sid = str(sysid[1][6:]) idxtoifname = {} for oidindex in conn.walk('1.0.8802.1.1.2.1.3.7.1.4'): idx = oidindex[0][-1] idxtoifname[idx] = _lldpdesc_to_ifname(sid, idx, str(oidindex[1])) for remotedesc in conn.walk('1.0.8802.1.1.2.1.4.1.1.10'): iname = idxtoifname[remotedesc[0][-2]] lldpdata[iname] = {'description': str(remotedesc[1])} for remotename in conn.walk('1.0.8802.1.1.2.1.4.1.1.9'): iname = idxtoifname[remotename[0][-2]] if iname not in lldpdata: lldpdata[iname] = {} lldpdata[iname]['name'] = str(remotename[1]) for remoteid in conn.walk('1.0.8802.1.1.2.1.4.1.1.5'): iname = idxtoifname[remoteid[0][-2]] if iname not in lldpdata: lldpdata[iname] = {} lldpdata[iname]['chassisid'] = str(remoteid[1]) print(repr(lldpdata))
def _extract_neighbor_data_b(args): """Build LLDP data about elements connected to switch args are carried as a tuple, because of eventlet convenience """ switch, password, user, cfm, force = args[:5] vintage = _neighdata.get(switch, {}).get('!!vintage', 0) now = util.monotonic_time() if vintage > (now - 60) and not force: return lldpdata = {'!!vintage': now} try: return _extract_neighbor_data_affluent(switch, user, password, cfm, lldpdata) except Exception: pass conn = snmp.Session(switch, password, user) sid = None for sysid in conn.walk('1.3.6.1.2.1.1.2'): sid = str(sysid[1][6:]) _noaffluent.add(switch) idxtoifname = {} idxtoportid = {} _chassisidbyswitch[switch] = sanitize( list(conn.walk('1.0.8802.1.1.2.1.3.2'))[0][1]) for oidindex in conn.walk('1.0.8802.1.1.2.1.3.7.1.3'): idx = oidindex[0][-1] idxtoportid[idx] = sanitize(oidindex[1]) for oidindex in conn.walk('1.0.8802.1.1.2.1.3.7.1.4'): idx = oidindex[0][-1] idxtoifname[idx] = _lldpdesc_to_ifname(sid, idx, str(oidindex[1])) for remotedesc in conn.walk('1.0.8802.1.1.2.1.4.1.1.10'): iname = idxtoifname[remotedesc[0][-2]] _init_lldp(lldpdata, iname, remotedesc[0][-2], idxtoportid, switch) _extract_extended_desc(lldpdata[iname], remotedesc[1], user) for remotename in conn.walk('1.0.8802.1.1.2.1.4.1.1.9'): iname = idxtoifname[remotename[0][-2]] _init_lldp(lldpdata, iname, remotename[0][-2], idxtoportid, switch) lldpdata[iname]['peername'] = str(remotename[1]) for remotename in conn.walk('1.0.8802.1.1.2.1.4.1.1.7'): iname = idxtoifname[remotename[0][-2]] _init_lldp(lldpdata, iname, remotename[0][-2], idxtoportid, switch) lldpdata[iname]['peerportid'] = sanitize(remotename[1]) for remoteid in conn.walk('1.0.8802.1.1.2.1.4.1.1.5'): iname = idxtoifname[remoteid[0][-2]] _init_lldp(lldpdata, iname, remoteid[0][-2], idxtoportid, switch) lldpdata[iname]['peerchassisid'] = sanitize(remoteid[1]) for entry in lldpdata: if entry == '!!vintage': continue entry = lldpdata[entry] entry['switch'] = switch peerid = '{0}.{1}'.format( entry.get('peerchassisid', '').replace(':', '-').replace('/', '-'), entry.get('peerportid', '').replace(':', '-').replace('/', '-')) entry['peerid'] = peerid _neighbypeerid[peerid] = entry _neighdata[switch] = lldpdata
def _map_switch_backend(args): """Manipulate portions of mac address map relevant to a given switch """ # 1.3.6.1.2.1.17.7.1.2.2.1.2 - mactoindex (qbridge - preferred) # if not, check for cisco and if cisco, build list of all relevant vlans: # .1.3.6.1.4.1.9.9.46.1.6.1.1.5 - trunk port vlan map (cisco only) # .1.3.6.1.4.1.9.9.68.1.2.2.1.2 - access port vlan map (cisco only) # if cisco, vlan community string indexed or snmpv3 contest for: # 1.3.6.1.2.1.17.4.3.1.2 - mactoindx (bridge - low-end switches and cisco) # .1.3.6.1.2.1.17.1.4.1.2 - bridge index to if index map # no vlan index or context for: # .1.3.6.1.2.1.31.1.1.1.1 - ifName... but some switches don't do it # .1.3.6.1.2.1.2.2.1.2 - ifDescr, usually useless, but a # fallback if ifName is empty # global _macmap if len(args) == 3: switch, password, user = args if not user: user = None else: switch, password = args user = None haveqbridge = False mactobridge = {} conn = snmp.Session(switch, password, user) for vb in conn.walk('1.3.6.1.2.1.17.7.1.2.2.1.2'): haveqbridge = True oid, bridgeport = vb if not bridgeport: continue oid = str(oid).rsplit('.', 6) # if 7, then oid[1] would be vlan id macaddr = '{0:02x}:{1:02x}:{2:02x}:{3:02x}:{4:02x}:{5:02x}'.format( *([int(x) for x in oid[-6:]])) mactobridge[macaddr] = int(bridgeport) if not haveqbridge: for vb in conn.walk('1.3.6.1.2.1.17.4.3.1.2'): oid, bridgeport = vb if not bridgeport: continue oid = str(oid).rsplit('.', 6) macaddr = '{0:02x}:{1:02x}:{2:02x}:{3:02x}:{4:02x}:{5:02x}'.format( *([int(x) for x in oid[-6:]])) mactobridge[macaddr] = int(bridgeport) bridgetoifmap = {} for vb in conn.walk('1.3.6.1.2.1.17.1.4.1.2'): bridgeport, ifidx = vb bridgeport = int(str(bridgeport).rsplit('.', 1)[1]) try: bridgetoifmap[bridgeport] = int(ifidx) except ValueError: # ifidx might be '', skip in such a case continue ifnamemap = {} havenames = False for vb in conn.walk('1.3.6.1.2.1.31.1.1.1.1'): ifidx, ifname = vb if not ifname: continue havenames = True ifidx = int(str(ifidx).rsplit('.', 1)[1]) ifnamemap[ifidx] = str(ifname) if not havenames: for vb in conn.walk('1.3.6.1.2.1.2.2.1.2'): ifidx, ifname = vb ifidx = int(str(ifidx).rsplit('.', 1)[1]) ifnamemap[ifidx] = str(ifname) maccounts = {} bridgetoifvalid = False for mac in mactobridge: try: ifname = ifnamemap[bridgetoifmap[mactobridge[mac]]] bridgetoifvalid = True except KeyError: continue if ifname not in maccounts: maccounts[ifname] = 1 else: maccounts[ifname] += 1 if not bridgetoifvalid: bridgetoifmap = {} # Not a single mac address resolved to an interface index, chances are # that the switch is broken, and the mactobridge is reporting ifidx # instead of bridge port index # try again, skipping the bridgetoifmap lookup for mac in mactobridge: try: ifname = ifnamemap[mactobridge[mac]] bridgetoifmap[mactobridge[mac]] = mactobridge[mac] except KeyError: continue if ifname not in maccounts: maccounts[ifname] = 1 else: maccounts[ifname] += 1 _macsbyswitch[switch] = {} for mac in mactobridge: # We want to merge it so that when a mac appears in multiple # places, it is captured. try: ifname = ifnamemap[bridgetoifmap[mactobridge[mac]]] except KeyError: continue if mac in _macmap: _macmap[mac].append((switch, ifname, maccounts[ifname])) else: _macmap[mac] = [(switch, ifname, maccounts[ifname])] if ifname in _macsbyswitch[switch]: _macsbyswitch[switch][ifname].append(mac) else: _macsbyswitch[switch][ifname] = [mac] nodename = _nodelookup(switch, ifname) if nodename is not None: if mac in _nodesbymac and _nodesbymac[mac] != nodename: # For example, listed on both a real edge port # and by accident a trunk port log.log({ 'error': '{0} and {1} described by ambiguous' ' switch topology values'.format(nodename, _nodesbymac[mac]) }) _nodesbymac[mac] = None else: _nodesbymac[mac] = nodename
def _list_interfaces(switchname, configmanager): switchcreds = get_switchcreds(configmanager, (switchname,)) switchcreds = switchcreds[0] conn = snmp.Session(*switchcreds) ifnames = netutil.get_portnamemap(conn) return util.natural_sort(ifnames.values())
def _map_switch_backend(args): """Manipulate portions of mac address map relevant to a given switch """ # 1.3.6.1.2.1.17.7.1.2.2.1.2 - mactoindex (qbridge - preferred) # if not, check for cisco and if cisco, build list of all relevant vlans: # .1.3.6.1.4.1.9.9.46.1.6.1.1.5 - trunk port vlan map (cisco only) # .1.3.6.1.4.1.9.9.68.1.2.2.1.2 - access port vlan map (cisco only) # if cisco, vlan community string indexed or snmpv3 contest for: # 1.3.6.1.2.1.17.4.3.1.2 - mactoindx (bridge - low-end switches and cisco) # .1.3.6.1.2.1.17.1.4.1.2 - bridge index to if index map # no vlan index or context for: # .1.3.6.1.2.1.31.1.1.1.1 - ifName... but some switches don't do it # .1.3.6.1.2.1.2.2.1.2 - ifDescr, usually useless, but a # fallback if ifName is empty # global _macmap switch, password, user = args haveqbridge = False mactobridge = {} conn = snmp.Session(switch, password, user) for vb in conn.walk('1.3.6.1.2.1.17.7.1.2.2.1.2'): haveqbridge = True oid, bridgeport = vb if not bridgeport: continue oid = str(oid).rsplit('.', 6) # if 7, then oid[1] would be vlan id macaddr = '{0:02x}:{1:02x}:{2:02x}:{3:02x}:{4:02x}:{5:02x}'.format( *([int(x) for x in oid[-6:]])) mactobridge[macaddr] = int(bridgeport) if not haveqbridge: raise exc.NotImplementedException('TODO: Bridge-MIB without QBRIDGE') bridgetoifmap = {} for vb in conn.walk('1.3.6.1.2.1.17.1.4.1.2'): bridgeport, ifidx = vb bridgeport = int(str(bridgeport).rsplit('.', 1)[1]) bridgetoifmap[bridgeport] = int(ifidx) ifnamemap = {} havenames = False for vb in conn.walk('1.3.6.1.2.1.31.1.1.1.1'): ifidx, ifname = vb if not ifname: continue havenames = True ifidx = int(str(ifidx).rsplit('.', 1)[1]) ifnamemap[ifidx] = str(ifname) if not havenames: for vb in conn.walk('1.3.6.1.2.1.2.2.1.2'): ifidx, ifname = vb ifidx = int(str(ifidx).rsplit('.', 1)[1]) ifnamemap[ifidx] = str(ifname) maccounts = {} for mac in mactobridge: ifname = ifnamemap[bridgetoifmap[mactobridge[mac]]] if ifname not in maccounts: maccounts[ifname] = 1 else: maccounts[ifname] += 1 _macsbyswitch[switch] = {} for mac in mactobridge: # We want to merge it so that when a mac appears in multiple # places, it is captured. ifname = ifnamemap[bridgetoifmap[mactobridge[mac]]] if mac in _macmap: _macmap[mac].append((switch, ifname, maccounts[ifname])) else: _macmap[mac] = [(switch, ifname, maccounts[ifname])] if ifname in _macsbyswitch[switch]: _macsbyswitch[switch][ifname].append(mac) else: _macsbyswitch[switch][ifname] = [mac] nodename = _nodelookup(switch, ifname) if nodename is not None: if mac in _nodesbymac and _nodesbymac[mac] != nodename: log.log({ 'warning': '{0} and {1} described by ambiguous' ' switch topology values'.format(nodename, _nodesbymac[mac]) }) _nodesbymac[mac] = nodename