diff --git a/confluent_server/confluent/core.py b/confluent_server/confluent/core.py index ee62c5e8..997781cd 100644 --- a/confluent_server/confluent/core.py +++ b/confluent_server/confluent/core.py @@ -753,9 +753,9 @@ def _forward_rsp(connection, res): r = msgpack.packb( ['Exception', 'Unable to serialize response ' + repr(res)], use_bin_type=False) - except Exception: + except Exception as e: r = msgpack.packb( - ['Exception', 'Unable to serialize response ' + repr(res)], + ['Exception', 'Unable to serialize response ' + repr(res) + ' due to ' + str(e)], use_bin_type=False) rlen = len(r) if not rlen: diff --git a/confluent_server/confluent/exceptions.py b/confluent_server/confluent/exceptions.py index bce75763..cb856094 100644 --- a/confluent_server/confluent/exceptions.py +++ b/confluent_server/confluent/exceptions.py @@ -21,10 +21,12 @@ import msgpack def deserialize_exc(msg): excd = msgpack.unpackb(msg, raw=False) + if excd[0] == 'Exception': + return Exception(excd[1]) if excd[0] not in globals(): - return False + return Exception('Cannot deserialize: {0}'.format(repr(excd))) if not issubclass(excd[0], ConfluentException): - return False + return Exception('Cannot deserialize: {0}'.format(repr(excd))) return globals(excd[0])(*excd[1]) class ConfluentException(Exception): diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py index 3fc63b8e..b14d12ee 100644 --- a/confluent_server/confluent/messages.py +++ b/confluent_server/confluent/messages.py @@ -578,9 +578,11 @@ class DetachMedia(ConfluentMessage): class Media(ConfluentMessage): - def __init__(self, node, media): - self.myargs = (node, media) - self.kvpairs = {node: {'name': media.name, 'url': media.url}} + def __init__(self, node, media=None, rawmedia=None): + if media: + rawmedia = {'name': media.name, 'url': media.url} + self.myargs = (node, None, rawmedia) + self.kvpairs = {node: rawmedia} class SavedFile(ConfluentMessage): def __init__(self, node, file): diff --git a/confluent_server/confluent/networking/lldp.py b/confluent_server/confluent/networking/lldp.py index dead1da3..8211aee7 100644 --- a/confluent_server/confluent/networking/lldp.py +++ b/confluent_server/confluent/networking/lldp.py @@ -44,7 +44,7 @@ import eventlet from eventlet.greenpool import GreenPool import eventlet.semaphore import re - +webclient = eventlet.import_patched('pyghmi.util.webclient') # The interesting OIDs are: # lldpLocChassisId - to cross reference (1.0.8802.1.1.2.1.3.2.0) # lldpLocPortId - for cross referencing.. (1.0.8802.1.1.2.1.3.7.1.3) @@ -85,6 +85,7 @@ _neighdata = {} _neighbypeerid = {} _updatelocks = {} _chassisidbyswitch = {} +_noaffluent = set([]) def lenovoname(idx, desc): if desc.isdigit(): @@ -171,21 +172,58 @@ def _init_lldp(data, iname, idx, idxtoportid, switch): data[iname] = {'port': iname, 'portid': str(idxtoportid[idx]), 'chassisid': _chassisidbyswitch[switch]} +def _extract_neighbor_data_affluent(switch, user, password, cfm, lldpdata): + 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) + neighdata = wc.grab_json_response('/affluent/lldp/all') + chassisid = neighdata['chassis']['id'] + _chassisidbyswitch[switch] = chassisid, + for record in neighdata['neighbors']: + localport = record['localport'] + peerid = '{0}.{1}'.format( + record.get('peerchassisid', '').replace(':', '-').replace('/', '-'), + record.get('peerportid', '').replace(':', '-').replace('/', '-'), + ) + portdata = { + 'verified': True, # It is over TLS after all + 'peerdescription': record.get('peerdescription', None), + 'peerchassisid': record['peerchassisid'], + 'peername': record['peername'], + 'switch': switch, + 'chassisid': chassisid, + 'portid': record['localport'], + 'peerportid': record['peerportid'], + 'port': record['localport'], + 'peerid': peerid, + } + _neighbypeerid[peerid] = portdata + lldpdata[localport] = portdata + neighdata[switch] = 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, force = args[:4] + 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 - lldpdata = {'!!vintage': now} 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( @@ -268,8 +306,8 @@ def _extract_neighbor_data(args): return _extract_neighbor_data_b(args) except Exception as e: yieldexc = False - if len(args) >= 5: - yieldexc = args[4] + if len(args) >= 6: + yieldexc = args[5] if yieldexc: return e else: @@ -358,10 +396,3 @@ def _handle_neighbor_query(pathcomponents, configmanager): raise x return list_info(parms, listrequested) - -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()) \ No newline at end of file diff --git a/confluent_server/confluent/networking/macmap.py b/confluent_server/confluent/networking/macmap.py index b9277b9b..68bb619d 100644 --- a/confluent_server/confluent/networking/macmap.py +++ b/confluent_server/confluent/networking/macmap.py @@ -45,13 +45,16 @@ 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 = {} -_neighdata = {} vintage = None @@ -127,6 +130,36 @@ def _nodelookup(switch, ifname): 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 = wc.grab_json_response('/affluent/macs/by-port') + _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 """ @@ -144,13 +177,18 @@ def _map_switch_backend(args): # fallback if ifName is empty # global _macmap - if len(args) == 3: - switch, password, user = args + 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) @@ -164,6 +202,7 @@ def _map_switch_backend(args): *([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 diff --git a/confluent_server/confluent/networking/netutil.py b/confluent_server/confluent/networking/netutil.py index 508c8662..13692292 100644 --- a/confluent_server/confluent/networking/netutil.py +++ b/confluent_server/confluent/networking/netutil.py @@ -36,7 +36,7 @@ def get_switchcreds(configmanager, switches): 'secret.hardwaremanagementuser', {}).get('value', None) if not user: user = None - switchauth.append((switch, password, user)) + switchauth.append((switch, password, user, configmanager)) return switchauth