mirror of
https://github.com/xcat2/confluent.git
synced 2024-11-22 17:43:14 +00:00
Offload macmap SNMP activity to an auxillary process
This doesn't make the code more efficient, but it keeps it from slowing down the main process and allows it to leverage an additional core to do the work. Still needs work to restore the error reporting.
This commit is contained in:
parent
3469c8ab2b
commit
374b87e2d7
@ -1,6 +1,6 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2019 Lenovo
|
||||
# Copyright 2016-2021 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -30,21 +30,32 @@
|
||||
|
||||
# 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.snmputil as snmp
|
||||
|
||||
|
||||
from confluent.networking.lldp import _handle_neighbor_query, get_fingerprint
|
||||
from confluent.networking.netutil import get_switchcreds, list_switches, get_portnamemap
|
||||
import eventlet.green.select as select
|
||||
|
||||
import eventlet.green.socket as socket
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
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.green.subprocess as subprocess
|
||||
import fcntl
|
||||
import eventlet
|
||||
import eventlet.semaphore
|
||||
import msgpack
|
||||
import random
|
||||
import re
|
||||
webclient = eventlet.import_patched('pyghmi.util.webclient')
|
||||
|
||||
@ -56,6 +67,8 @@ _apimacmap = {}
|
||||
_macsbyswitch = {}
|
||||
_nodesbymac = {}
|
||||
_switchportmap = {}
|
||||
_offloadevts = {}
|
||||
_offloader = None
|
||||
vintage = None
|
||||
|
||||
|
||||
@ -134,8 +147,8 @@ def _nodelookup(switch, ifname):
|
||||
|
||||
|
||||
def _affluent_map_switch(args):
|
||||
switch, password, user, cfm = args
|
||||
kv = util.TLSCertVerifier(cfm, switch,
|
||||
switch, password, user, cfgm = args
|
||||
kv = util.TLSCertVerifier(cfgm, switch,
|
||||
'pubkeys.tls_hardwaremanager').verify_cert
|
||||
wc = webclient.SecureHTTPConnection(
|
||||
switch, 443, verifycallback=kv, timeout=5)
|
||||
@ -164,6 +177,47 @@ def _affluent_map_switch(args):
|
||||
else:
|
||||
_nodesbymac[mac] = (nodename, nummacs)
|
||||
|
||||
def _offload_map_switch(switch, password, user):
|
||||
if _offloader is None:
|
||||
_start_offloader()
|
||||
evtid = random.randint(0, 4294967295)
|
||||
while evtid in _offloadevts:
|
||||
evtid = random.randint(0, 4294967295)
|
||||
_offloadevts[evtid] = eventlet.Event()
|
||||
_offloader.stdin.write(msgpack.packb((evtid, switch, password, user), use_bin_type=False))
|
||||
_offloader.stdin.flush()
|
||||
result = _offloadevts[evtid].wait()
|
||||
del _offloadevts[evtid]
|
||||
return result
|
||||
|
||||
|
||||
|
||||
def _start_offloader():
|
||||
global _offloader
|
||||
os.environ['PYTHONPATH'] = ':'.join(sys.path)
|
||||
_offloader = subprocess.Popen(
|
||||
[sys.executable, __file__, '-o'], bufsize=0, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
fl = fcntl.fcntl(_offloader.stdout.fileno(), fcntl.F_GETFL)
|
||||
fcntl.fcntl(_offloader.stdout.fileno(),
|
||||
fcntl.F_SETFL, fl | os.O_NONBLOCK)
|
||||
eventlet.spawn_n(_recv_offload)
|
||||
eventlet.sleep(0)
|
||||
|
||||
|
||||
def _recv_offload():
|
||||
upacker = msgpack.Unpacker(raw=False)
|
||||
instream = _offloader.stdout.fileno()
|
||||
while True:
|
||||
select.select([_offloader.stdout], [], [])
|
||||
upacker.feed(os.read(instream, 128))
|
||||
for result in upacker:
|
||||
if result[0] not in _offloadevts:
|
||||
print("Uh oh, unexpected event id... " + repr(result))
|
||||
continue
|
||||
_offloadevts[result[0]].send(result[1:])
|
||||
eventlet.sleep(0)
|
||||
|
||||
|
||||
def _map_switch_backend(args):
|
||||
"""Manipulate portions of mac address map relevant to a given switch
|
||||
@ -183,7 +237,7 @@ def _map_switch_backend(args):
|
||||
#
|
||||
global _macmap
|
||||
if len(args) == 4:
|
||||
switch, password, user, cfm = args
|
||||
switch, password, user, _ = args # 4th arg is for affluent only
|
||||
if not user:
|
||||
user = None
|
||||
else:
|
||||
@ -194,6 +248,84 @@ def _map_switch_backend(args):
|
||||
return _affluent_map_switch(args)
|
||||
except Exception:
|
||||
pass
|
||||
mactobridge, ifnamemap, bridgetoifmap = _offload_map_switch(
|
||||
switch, password, user)
|
||||
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 = {}
|
||||
noaffluent.add(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 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
|
||||
|
||||
def _snmp_map_switch_relay(rqid, switch, password, user):
|
||||
try:
|
||||
res = _snmp_map_switch(switch, password, user)
|
||||
try:
|
||||
sys.stdout.buffer.write(msgpack.packb((rqid,) + res,
|
||||
use_bin_type=False))
|
||||
except AttributeError:
|
||||
sys.stdout.write(msgpack.packb((rqid,) + res,
|
||||
use_bin_type=False))
|
||||
except Exception as e:
|
||||
try:
|
||||
sys.stdout.buffer.write(msgpack.packb(str(e)))
|
||||
except AttributeError:
|
||||
sys.stdout.write(msgpack.packb(str(e)))
|
||||
finally:
|
||||
sys.stdout.flush()
|
||||
|
||||
def _snmp_map_switch(switch, password, user):
|
||||
haveqbridge = False
|
||||
mactobridge = {}
|
||||
conn = snmp.Session(switch, password, user)
|
||||
@ -203,12 +335,12 @@ def _map_switch_backend(args):
|
||||
oid, bridgeport = vb
|
||||
if not bridgeport:
|
||||
continue
|
||||
oid = str(oid).rsplit('.', 6) # if 7, then oid[1] would be vlan id
|
||||
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
|
||||
@ -250,62 +382,8 @@ def _map_switch_backend(args):
|
||||
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
|
||||
#OFFLOAD: end of need to offload?
|
||||
return mactobridge,ifnamemap,bridgetoifmap
|
||||
|
||||
|
||||
switchbackoff = 30
|
||||
@ -572,6 +650,20 @@ def rescan(cfg):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 1 and sys.argv[1] == '-o':
|
||||
upacker = msgpack.Unpacker(raw=False)
|
||||
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
|
||||
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl | os.O_NONBLOCK)
|
||||
|
||||
while True:
|
||||
r = select.select([sys.stdin], [], [])
|
||||
try:
|
||||
upacker.feed(sys.stdin.buffer.read())
|
||||
except AttributeError:
|
||||
upacker.feed(sys.stdin.read())
|
||||
for cmd in upacker:
|
||||
eventlet.spawn_n(_snmp_map_switch_relay, *cmd)
|
||||
sys.exit(0)
|
||||
cg = cfm.ConfigManager(None)
|
||||
for res in update_macmap(cg):
|
||||
print("map has updated")
|
||||
|
@ -40,7 +40,7 @@ class WebClient(object):
|
||||
rsp, status = self.wc.grab_json_response_with_status(url)
|
||||
except exc.PubkeyInvalid:
|
||||
results.put(msg.ConfluentNodeError(self.node,
|
||||
'Extended information unavailable, mismatch detected between '
|
||||
'Mismatch detected between '
|
||||
'target certificate fingerprint and '
|
||||
'pubkeys.tls_hardwaremanager attribute'))
|
||||
return {}
|
||||
|
Loading…
Reference in New Issue
Block a user