mirror of
https://github.com/xcat2/confluent.git
synced 2025-04-15 09:39:37 +00:00
Querying Cisco MIB on certain firmware levels of non-cisco switches causes a crash. Tolerate and wait a bit to give SNMP a chance to restart.
585 lines
23 KiB
Python
585 lines
23 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2016-2019 Lenovo
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# This provides the implementation of locating MAC addresses on ethernet
|
|
# switches. It is, essentially, a port of 'MacMap.pm' to confluent.
|
|
# However, there are enhancements.
|
|
# For one, each switch interrogation is handled in an eventlet 'thread'
|
|
# For another, MAC addresses are checked in the dictionary on every
|
|
# switch return, rather than waiting for all switches to check in
|
|
# (which makes it more responsive when there is a missing or bad switch)
|
|
# Also, we track the quantity, actual ifName value, and provide a mechanism
|
|
# to detect ambiguous result (e.g. if two matches are found, can log an error
|
|
# rather than doing the wrong one, complete with the detected ifName value).
|
|
# Further, the map shall be available to all facets of the codebase, not just
|
|
# the discovery process, so that the cached data maintenance will pay off
|
|
# for direct queries
|
|
|
|
# this module will provide mac to switch and full 'ifName' label
|
|
# This functionality is restricted to the null tenant
|
|
from confluent.networking.lldp import _handle_neighbor_query, get_fingerprint
|
|
from confluent.networking.netutil import get_switchcreds, list_switches, get_portnamemap
|
|
import eventlet.green.socket as socket
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
import confluent.config.configmanager as cfm
|
|
import confluent.exceptions as exc
|
|
import confluent.log as log
|
|
import confluent.messages as msg
|
|
import confluent.snmputil as snmp
|
|
import confluent.util as util
|
|
from eventlet.greenpool import GreenPool
|
|
import eventlet
|
|
import eventlet.semaphore
|
|
import re
|
|
webclient = eventlet.import_patched('pyghmi.util.webclient')
|
|
|
|
|
|
noaffluent = set([])
|
|
|
|
_macmap = {}
|
|
_apimacmap = {}
|
|
_macsbyswitch = {}
|
|
_nodesbymac = {}
|
|
_switchportmap = {}
|
|
vintage = None
|
|
|
|
|
|
_whitelistnames = (
|
|
# 3com
|
|
re.compile(r'^RMON Port (\d+) on unit \d+'),
|
|
# Dell
|
|
re.compile(r'^Unit \d+ Port (\d+)\Z'),
|
|
)
|
|
|
|
_blacklistnames = (
|
|
re.compile(r'vl'),
|
|
re.compile(r'Nu'),
|
|
re.compile(r'RMON'),
|
|
re.compile(r'onsole'),
|
|
re.compile(r'Stack'),
|
|
re.compile(r'Trunk'),
|
|
re.compile(r'po\d'),
|
|
re.compile(r'XGE'),
|
|
re.compile(r'LAG'),
|
|
re.compile(r'CPU'),
|
|
re.compile(r'Management'),
|
|
)
|
|
|
|
|
|
def _namesmatch(switchdesc, userdesc):
|
|
if switchdesc == userdesc:
|
|
return True
|
|
try:
|
|
portnum = int(userdesc)
|
|
except ValueError:
|
|
portnum = None
|
|
if portnum is not None:
|
|
for exp in _whitelistnames:
|
|
match = exp.match(switchdesc)
|
|
if match:
|
|
snum = int(match.groups()[0])
|
|
if snum == portnum:
|
|
return True
|
|
anymatch = re.search(r'[^0123456789]' + userdesc + r'(\.0)?\Z', switchdesc)
|
|
if anymatch:
|
|
for blexp in _blacklistnames:
|
|
if blexp.match(switchdesc):
|
|
return False
|
|
return True
|
|
return False
|
|
|
|
def _map_switch(args):
|
|
try:
|
|
return _map_switch_backend(args)
|
|
except (UnicodeError, socket.gaierror):
|
|
log.log({'error': "Cannot resolve switch '{0}' to an address".format(
|
|
args[0])})
|
|
except exc.TargetEndpointUnreachable:
|
|
log.log({'error': "Timeout or bad SNMPv1 community string trying to "
|
|
"reach switch '{0}'".format(
|
|
args[0])})
|
|
except exc.TargetEndpointBadCredentials:
|
|
log.log({'error': "Bad SNMPv3 credentials for \'{0}\'".format(
|
|
args[0])})
|
|
except Exception as e:
|
|
log.log({'error': 'Unexpected condition trying to reach switch "{0}"'
|
|
' check trace log for more'.format(args[0])})
|
|
log.logtrace()
|
|
|
|
|
|
def _nodelookup(switch, ifname):
|
|
"""Get a nodename for a given switch and interface name
|
|
"""
|
|
for portdesc in _switchportmap.get(switch, {}):
|
|
if _namesmatch(ifname, portdesc):
|
|
return _switchportmap[switch][portdesc]
|
|
return None
|
|
|
|
|
|
def _affluent_map_switch(args):
|
|
switch, password, user, cfm = args
|
|
kv = util.TLSCertVerifier(cfm, switch,
|
|
'pubkeys.tls_hardwaremanager').verify_cert
|
|
wc = webclient.SecureHTTPConnection(
|
|
switch, 443, verifycallback=kv, timeout=5)
|
|
wc.set_basic_credentials(user, password)
|
|
macs, retcode = wc.grab_json_response_with_status('/affluent/macs/by-port')
|
|
if retcode != 200:
|
|
raise Exception("No affluent detected")
|
|
_macsbyswitch[switch] = macs
|
|
|
|
for iface in macs:
|
|
nummacs = len(macs[iface])
|
|
for mac in macs[iface]:
|
|
if mac in _macmap:
|
|
_macmap[mac].append((switch, iface, nummacs))
|
|
else:
|
|
_macmap[mac] = [(switch, iface, nummacs)]
|
|
nodename = _nodelookup(switch, iface)
|
|
if nodename is not None:
|
|
if mac in _nodesbymac and _nodesbymac[mac][0] != 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][0])})
|
|
_nodesbymac[mac] = (None, None)
|
|
else:
|
|
_nodesbymac[mac] = (nodename, nummacs)
|
|
|
|
|
|
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) == 4:
|
|
switch, password, user, cfm = args
|
|
if not user:
|
|
user = None
|
|
else:
|
|
switch, password = args
|
|
user = None
|
|
if switch not in noaffluent:
|
|
try:
|
|
return _affluent_map_switch(args)
|
|
except Exception:
|
|
pass
|
|
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)
|
|
noaffluent.add(switch)
|
|
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
|
|
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
|
|
newmacs = {}
|
|
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 newmacs:
|
|
newmacs[ifname].append(mac)
|
|
else:
|
|
newmacs[ifname] = [mac]
|
|
nodename = _nodelookup(switch, ifname)
|
|
if nodename is not None:
|
|
if mac in _nodesbymac and _nodesbymac[mac][0] != 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][0])})
|
|
_nodesbymac[mac] = (None, None)
|
|
else:
|
|
_nodesbymac[mac] = (nodename, maccounts[ifname])
|
|
_macsbyswitch[switch] = newmacs
|
|
|
|
|
|
switchbackoff = 30
|
|
|
|
|
|
def find_nodeinfo_by_mac(mac, configmanager):
|
|
now = util.monotonic_time()
|
|
if vintage and (now - vintage) < 90 and mac in _nodesbymac:
|
|
return _nodesbymac[mac][0], {'maccount': _nodesbymac[mac][1]}
|
|
# do not actually sweep switches more than once every 30 seconds
|
|
# however, if there is an update in progress, wait on it
|
|
for _ in update_macmap(configmanager,
|
|
vintage and (now - vintage) < switchbackoff):
|
|
if mac in _nodesbymac:
|
|
return _nodesbymac[mac][0], {'maccount': _nodesbymac[mac][1]}
|
|
# If update_mac bailed out, still check one last time
|
|
if mac in _nodesbymac:
|
|
return _nodesbymac[mac][0], {'maccount': _nodesbymac[mac][1]}
|
|
return None, {'maccount': 0}
|
|
|
|
|
|
mapupdating = eventlet.semaphore.Semaphore()
|
|
|
|
|
|
def update_macmap(configmanager, impatient=False):
|
|
"""Interrogate switches to build/update mac table
|
|
|
|
Begin a rebuild process. This process is a generator that will yield
|
|
as each switch interrogation completes, allowing a caller to
|
|
recheck the cache as results become possible, rather
|
|
than having to wait for the process to complete to interrogate.
|
|
"""
|
|
if mapupdating.locked():
|
|
while mapupdating.locked():
|
|
eventlet.sleep(1)
|
|
yield None
|
|
return
|
|
if impatient:
|
|
return
|
|
completions = _full_updatemacmap(configmanager)
|
|
for completion in completions:
|
|
try:
|
|
yield completion
|
|
except GeneratorExit:
|
|
# the calling function has stopped caring, but we want to finish
|
|
# the sweep, background it
|
|
eventlet.spawn_n(_finish_update, completions)
|
|
raise
|
|
|
|
|
|
def _finish_update(completions):
|
|
for _ in completions:
|
|
pass
|
|
|
|
|
|
def _full_updatemacmap(configmanager):
|
|
global vintage
|
|
global _apimacmap
|
|
global _macmap
|
|
global _nodesbymac
|
|
global _switchportmap
|
|
global _macsbyswitch
|
|
global switchbackoff
|
|
start = util.monotonic_time()
|
|
with mapupdating:
|
|
vintage = util.monotonic_time()
|
|
# Clear all existing entries
|
|
_macmap = {}
|
|
_nodesbymac = {}
|
|
_switchportmap = {}
|
|
if configmanager.tenant is not None:
|
|
raise exc.ForbiddenRequest(
|
|
'Network topology not available to tenants')
|
|
# here's a list of switches... need to add nodes that are switches
|
|
nodelocations = configmanager.get_node_attributes(
|
|
configmanager.list_nodes(), ('type', 'net*.switch', 'net*.switchport'))
|
|
switches = set([])
|
|
for node in nodelocations:
|
|
cfg = nodelocations[node]
|
|
if cfg.get('type', {}).get('value', None) == 'switch':
|
|
switches.add(node)
|
|
for attr in cfg:
|
|
if not attr.endswith('.switch') or 'value' not in cfg[attr]:
|
|
continue
|
|
curswitch = cfg[attr].get('value', None)
|
|
if not curswitch:
|
|
continue
|
|
switches.add(curswitch)
|
|
switchportattr = attr + 'port'
|
|
if switchportattr in cfg:
|
|
portname = cfg[switchportattr].get('value', '')
|
|
if not portname:
|
|
continue
|
|
if curswitch not in _switchportmap:
|
|
_switchportmap[curswitch] = {}
|
|
if (portname in _switchportmap[curswitch] and
|
|
_switchportmap[curswitch][portname] != node):
|
|
if _switchportmap[curswitch][portname] is None:
|
|
errstr = ('Duplicate switch attributes for {0} and '
|
|
'a previously logged duplicate'.format(
|
|
node))
|
|
else:
|
|
errstr = ('Duplicate switch topology config '
|
|
'for {0} and {1}'.format(
|
|
node,
|
|
_switchportmap[curswitch][
|
|
portname]))
|
|
log.log({'error': errstr})
|
|
_switchportmap[curswitch][portname] = None
|
|
else:
|
|
_switchportmap[curswitch][portname] = node
|
|
for switch in list(_macsbyswitch):
|
|
if switch not in switches:
|
|
del _macsbyswitch[switch]
|
|
switchauth = get_switchcreds(configmanager, switches)
|
|
pool = GreenPool(64)
|
|
for ans in pool.imap(_map_switch, switchauth):
|
|
vintage = util.monotonic_time()
|
|
yield ans
|
|
_apimacmap = _macmap
|
|
endtime = util.monotonic_time()
|
|
duration = endtime - start
|
|
duration = duration * 15 # wait 15 times as long as it takes to walk
|
|
# avoid spending a large portion of the time hitting switches with snmp
|
|
# requests
|
|
if duration > switchbackoff:
|
|
switchbackoff = duration
|
|
|
|
|
|
def _dump_locations(info, macaddr, nodename=None):
|
|
yield msg.KeyValueData({'possiblenode': nodename, 'mac': macaddr})
|
|
retdata = {}
|
|
portinfo = []
|
|
for location in info:
|
|
portinfo.append({'switch': location[0],
|
|
'port': location[1], 'macsonport': location[2]})
|
|
retdata['ports'] = sorted(portinfo, key=lambda x: x['macsonport'],
|
|
reverse=True)
|
|
yield msg.KeyValueData(retdata)
|
|
|
|
|
|
def handle_api_request(configmanager, inputdata, operation, pathcomponents):
|
|
if operation == 'retrieve':
|
|
return handle_read_api_request(pathcomponents, configmanager)
|
|
if (operation in ('update', 'create') and
|
|
pathcomponents == ['networking', 'macs', 'rescan']):
|
|
if inputdata != {'rescan': 'start'}:
|
|
raise exc.InvalidArgumentException('Input must be rescan=start')
|
|
eventlet.spawn_n(rescan, configmanager)
|
|
return [msg.KeyValueData({'rescan': 'started'})]
|
|
raise exc.NotImplementedException(
|
|
'Operation {0} on {1} not implemented'.format(
|
|
operation, '/'.join(pathcomponents)))
|
|
|
|
|
|
def get_node_fingerprints(nodename, configmanager):
|
|
cfg = configmanager.get_node_attributes(nodename, ['net*.switch',
|
|
'net*.switchport'])
|
|
for attrkey in cfg[nodename]:
|
|
if attrkey.endswith('switch'):
|
|
switch = cfg[nodename][attrkey]['value']
|
|
port = cfg[nodename][attrkey + 'port']['value']
|
|
yield get_fingerprint(switch, port, configmanager,
|
|
_namesmatch)
|
|
|
|
|
|
def handle_read_api_request(pathcomponents, configmanager):
|
|
# TODO(jjohnson2): discovery core.py api handler design, apply it here
|
|
# to make this a less tangled mess as it gets extended
|
|
if len(pathcomponents) == 1:
|
|
return [msg.ChildCollection('macs/'),
|
|
msg.ChildCollection('neighbors/')]
|
|
elif pathcomponents[1] == 'neighbors':
|
|
if len(pathcomponents) == 3 and pathcomponents[-1] == 'by-switch':
|
|
return [msg.ChildCollection(x + '/')
|
|
for x in list_switches(configmanager)]
|
|
else:
|
|
return _handle_neighbor_query(pathcomponents[2:], configmanager)
|
|
elif len(pathcomponents) == 2:
|
|
if pathcomponents[-1] == 'macs':
|
|
return [msg.ChildCollection(x) for x in (# 'by-node/',
|
|
'by-mac/', 'by-switch/',
|
|
'rescan')]
|
|
elif pathcomponents[-1] == 'neighbors':
|
|
return [msg.ChildCollection('by-switch/')]
|
|
else:
|
|
raise exc.NotFoundException(
|
|
'Unknown networking resource {0}'.format(pathcomponents[-1]))
|
|
if False and pathcomponents[2] == 'by-node':
|
|
# TODO: should be list of node names, and then under that 'by-mac'
|
|
if len(pathcomponents) == 3:
|
|
return [msg.ChildCollection(x.replace(':', '-'))
|
|
for x in util.natural_sort(list(_nodesbymac))]
|
|
elif len(pathcomponents) == 4:
|
|
macaddr = pathcomponents[-1].replace('-', ':')
|
|
return dump_macinfo(macaddr)
|
|
elif pathcomponents[2] == 'by-mac':
|
|
if len(pathcomponents) == 3:
|
|
return [msg.ChildCollection(x.replace(':', '-'))
|
|
for x in sorted(list(_apimacmap))]
|
|
elif len(pathcomponents) == 4:
|
|
return dump_macinfo(pathcomponents[-1])
|
|
elif pathcomponents[2] == 'by-switch':
|
|
if len(pathcomponents) == 3:
|
|
return [msg.ChildCollection(x + '/')
|
|
for x in list_switches(configmanager)]
|
|
if len(pathcomponents) == 4:
|
|
return [msg.ChildCollection('by-port/')]
|
|
if len(pathcomponents) == 5:
|
|
switchname = pathcomponents[-2]
|
|
if switchname not in _macsbyswitch:
|
|
raise exc.NotFoundException(
|
|
'No known macs for switch {0}'.format(switchname))
|
|
return [msg.ChildCollection(x.replace('/', '-') + '/')
|
|
for x in util.natural_sort(list(_macsbyswitch[switchname]))]
|
|
if len(pathcomponents) == 6:
|
|
return [msg.ChildCollection('by-mac/')]
|
|
if len(pathcomponents) == 7:
|
|
switchname = pathcomponents[-4]
|
|
portname = pathcomponents[-2]
|
|
try:
|
|
if portname not in _macsbyswitch[switchname]:
|
|
portname = portname.replace('-', '/')
|
|
maclist = _macsbyswitch[switchname][portname]
|
|
except KeyError:
|
|
foundsomemacs = False
|
|
if switchname in _macsbyswitch:
|
|
try:
|
|
matcher = re.compile(portname)
|
|
except Exception:
|
|
raise exc.InvalidArgumentException('Invalid regular expression specified')
|
|
maclist = []
|
|
for actualport in _macsbyswitch[switchname]:
|
|
if bool(matcher.match(actualport)):
|
|
foundsomemacs = True
|
|
maclist = maclist + _macsbyswitch[switchname][actualport]
|
|
if not foundsomemacs:
|
|
raise exc.NotFoundException('No known macs for switch {0} '
|
|
'port {1}'.format(switchname,
|
|
portname))
|
|
return [msg.ChildCollection(x.replace(':', '-'))
|
|
for x in sorted(maclist)]
|
|
if len(pathcomponents) == 8:
|
|
return dump_macinfo(pathcomponents[-1])
|
|
elif pathcomponents[2] == 'rescan':
|
|
return [msg.KeyValueData({'scanning': mapupdating.locked()})]
|
|
raise exc.NotFoundException('Unrecognized path {0}'.format(
|
|
'/'.join(pathcomponents)))
|
|
|
|
|
|
def dump_macinfo(macaddr):
|
|
macaddr = macaddr.replace('-', ':').lower()
|
|
info = _macmap.get(macaddr, None)
|
|
if info is None:
|
|
raise exc.NotFoundException(
|
|
'{0} not found in mac table of '
|
|
'any known switches'.format(macaddr))
|
|
return _dump_locations(info, macaddr, _nodesbymac.get(macaddr, (None,))[0])
|
|
|
|
|
|
def rescan(cfg):
|
|
for _ in update_macmap(cfg):
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
cg = cfm.ConfigManager(None)
|
|
for res in update_macmap(cg):
|
|
print("map has updated")
|
|
if len(sys.argv) > 1:
|
|
print(repr(_macmap[sys.argv[1]]))
|
|
print(repr(_nodesbymac[sys.argv[1]]))
|
|
else:
|
|
print("Mac to Node lookup table: -------------------")
|
|
print(repr(_nodesbymac))
|
|
print("Mac to location lookup table: -------------------")
|
|
print(repr(_macmap))
|
|
print("switch to fdb lookup table: -------------------")
|
|
print(repr(_macsbyswitch)) |