From 7a4c9a1fc0bd9e08bc84bffd91c756f45c3f5ce9 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 14 Jul 2016 08:55:50 -0400 Subject: [PATCH] Add mac map lookup against config to get node This brings things right to the level of xCAT in terms of underlying capability. mac addresses have both an all inclusive list of ports it is found on, and any nodes that it matches. It goes another step further by logging errors when ambiguity is detected (either verbatim config conflict or ambiguous result based on 'namesmatch' and the switch config). --- confluent_server/confluent/log.py | 1 + .../confluent/networking/macmap.py | 95 ++++++++++++++++++- 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/confluent_server/confluent/log.py b/confluent_server/confluent/log.py index 87b71e3a..662294a2 100644 --- a/confluent_server/confluent/log.py +++ b/confluent_server/confluent/log.py @@ -726,6 +726,7 @@ class Logger(object): globaleventlog = None tracelog = None + def log(logdata=None, ltype=None, event=0, eventdata=None): if globaleventlog is None: globaleventlog = Logger('events') diff --git a/confluent_server/confluent/networking/macmap.py b/confluent_server/confluent/networking/macmap.py index 0f60abf4..29344b09 100644 --- a/confluent_server/confluent/networking/macmap.py +++ b/confluent_server/confluent/networking/macmap.py @@ -34,11 +34,58 @@ import confluent.exceptions as exc import confluent.log as log import confluent.snmputil as snmp -import confluent.util as util -import eventlet from eventlet.greenpool import GreenPool +import re _macmap = {} +_macsbyswitch = {} +_nodesbymac = {} +_switchportmap = {} + + +_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 not anymatch: + return False + for blexp in _blacklistnames: + if blexp.match(switchdesc): + return False + def _map_switch(args): try: @@ -46,6 +93,16 @@ def _map_switch(args): except Exception as e: 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 _map_switch_backend(args): """Manipulate portions of mac address map relevant to a given switch """ @@ -105,6 +162,7 @@ def _map_switch_backend(args): 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. @@ -113,6 +171,18 @@ def _map_switch_backend(args): _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 def update_macmap(configmanager): @@ -124,17 +194,34 @@ def update_macmap(configmanager): than having to wait for the process to complete to interrogate. """ global _macmap + global _nodesbymac + global _switchportmap # Clear all existing entries _macmap = {} + _nodesbymac = {} + _switchportmap = {} if configmanager.tenant is not None: raise exc.ForbiddenRequest('Network topology not available to tenants') nodelocations = configmanager.get_node_attributes( - configmanager.list_nodes(), ('hardwaremanagement.switch',)) + configmanager.list_nodes(), ('hardwaremanagement.switch', + 'hardwaremanagement.switchport')) switches = set([]) for node in nodelocations: cfg = nodelocations[node] if 'hardwaremanagement.switch' in cfg: - switches.add(cfg['hardwaremanagement.switch']['value']) + curswitch = cfg['hardwaremanagement.switch']['value'] + switches.add(curswitch) + if 'hardwaremanagement.switchport' in cfg: + portname = cfg['hardwaremanagement.switchport']['value'] + if curswitch not in _switchportmap: + _switchportmap[curswitch] = {} + if portname in _switchportmap[curswitch]: + log.log({'warning': 'Duplicate switch topology config for ' + '{0} and {1}'.format(node, + _switchportmap[ + curswitch][ + portname])}) + _switchportmap[curswitch][portname] = node switchcfg = configmanager.get_node_attributes( switches, ('secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'), decrypt=True)