2
0
mirror of https://github.com/xcat2/confluent.git synced 2025-08-28 22:08:26 +00:00

Begin implementing the 'neighbors' collection

Refactor some common function and list interfaces on a switch
This commit is contained in:
Jarrod Johnson
2017-09-26 16:56:45 -04:00
parent 0741e3953e
commit ea27125587
2 changed files with 128 additions and 39 deletions

View File

@@ -34,6 +34,7 @@
if __name__ == '__main__':
import sys
import confluent.config.configmanager as cfm
import confluent.networking.lldp as lldp
import confluent.exceptions as exc
import confluent.log as log
import confluent.messages as msg
@@ -48,6 +49,7 @@ _macmap = {}
_macsbyswitch = {}
_nodesbymac = {}
_switchportmap = {}
_neighdata = {}
vintage = None
@@ -179,20 +181,7 @@ def _map_switch_backend(args):
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)
ifnamemap = _get_portnamemap(conn)
maccounts = {}
bridgetoifvalid = False
for mac in mactobridge:
@@ -251,6 +240,24 @@ def _map_switch_backend(args):
_nodesbymac[mac] = nodename
def _get_portnamemap(conn):
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)
return ifnamemap
def find_node_by_mac(mac, configmanager):
now = util.monotonic_time()
if vintage and (now - vintage) < 90 and mac in _nodesbymac:
@@ -296,6 +303,23 @@ def _finish_update(completions):
for _ in completions:
pass
def _list_switches(configmanager):
nodelocations = configmanager.get_node_attributes(
configmanager.list_nodes(), ('net*.switch', 'net*.switchport'))
switches = set([])
for node in nodelocations:
cfg = nodelocations[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)
return util.natural_sort(switches)
def _full_updatemacmap(configmanager):
global vintage
global _macmap
@@ -312,6 +336,7 @@ def _full_updatemacmap(configmanager):
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(), ('net*.switch', 'net*.switchport'))
switches = set([])
@@ -340,30 +365,35 @@ def _full_updatemacmap(configmanager):
_switchportmap[curswitch][portname] = None
else:
_switchportmap[curswitch][portname] = node
switchcfg = configmanager.get_node_attributes(
switches, ('secret.hardwaremanagementuser', 'secret.snmpcommunity',
'secret.hardwaremanagementpassword'), decrypt=True)
switchauth = []
for switch in switches:
if not switch:
continue
switchparms = switchcfg.get(switch, {})
user = None
password = switchparms.get(
'secret.snmpcommunity', {}).get('value', None)
if not password:
password = switchparms.get(
'secret.hardwaremanagementpassword', {}).get('value',
'public')
user = switchparms.get(
'secret.hardwaremanagementuser', {}).get('value', None)
switchauth.append((switch, password, user))
switchauth = _get_switchcreds(configmanager, switches)
pool = GreenPool()
for ans in pool.imap(_map_switch, switchauth):
vintage = util.monotonic_time()
yield ans
def _get_switchcreds(configmanager, switches):
switchcfg = configmanager.get_node_attributes(
switches, ('secret.hardwaremanagementuser', 'secret.snmpcommunity',
'secret.hardwaremanagementpassword'), decrypt=True)
switchauth = []
for switch in switches:
if not switch:
continue
switchparms = switchcfg.get(switch, {})
user = None
password = switchparms.get(
'secret.snmpcommunity', {}).get('value', None)
if not password:
password = switchparms.get(
'secret.hardwaremanagementpassword', {}).get('value',
'public')
user = switchparms.get(
'secret.hardwaremanagementuser', {}).get('value', None)
switchauth.append((switch, password, user))
return switchauth
def _dump_locations(info, macaddr, nodename=None):
yield msg.KeyValueData({'possiblenode': nodename, 'mac': macaddr})
retdata = {}
@@ -378,7 +408,7 @@ def _dump_locations(info, macaddr, nodename=None):
def handle_api_request(configmanager, inputdata, operation, pathcomponents):
if operation == 'retrieve':
return handle_read_api_request(pathcomponents)
return handle_read_api_request(pathcomponents, configmanager)
if (operation in ('update', 'create') and
pathcomponents == ['networking', 'macs', 'rescan']):
if inputdata != {'rescan': 'start'}:
@@ -390,15 +420,50 @@ def handle_api_request(configmanager, inputdata, operation, pathcomponents):
operation, '/'.join(pathcomponents)))
def handle_read_api_request(pathcomponents):
def _list_interfaces(switchname, configmanager):
switchcreds = _get_switchcreds(configmanager, (switchname,))
switchcreds = switchcreds[0]
conn = snmp.Session(*switchcreds)
ifnames = _get_portnamemap(conn)
return util.natural_sort(ifnames.values())
def _handle_neighbor_query(pathcomponents, configmanager):
switchname = pathcomponents[0]
if len(pathcomponents) == 1:
return [msg.ChildCollection('by-port/')]
if len(pathcomponents) == 2:
# need to list ports for the switchname
return [msg.ChildCollection(x) for x in _list_interfaces(switchname,
configmanager)]
portname = pathcomponents[2]
return [msg.ChildCollection(portname)]
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/')]
return [msg.ChildCollection('macs/'),
msg.ChildCollection('neighbors/')]
elif pathcomponents[1] == 'neighbors':
if len(pathcomponents) == 2:
return [msg.ChildCollection('by-switch/')]
elif len(pathcomponents) == 3:
return [msg.ChildCollection(x + '/')
for x in _list_switches(configmanager)]
else:
return _handle_neighbor_query(pathcomponents[3:], configmanager)
elif len(pathcomponents) == 2:
return [msg.ChildCollection(x) for x in (# 'by-node/',
'by-mac/', 'by-switch/',
'rescan')]
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:
@@ -416,7 +481,7 @@ def handle_read_api_request(pathcomponents):
elif pathcomponents[2] == 'by-switch':
if len(pathcomponents) == 3:
return [msg.ChildCollection(x + '/')
for x in sorted(list(_switchportmap))]
for x in _list_switches(configmanager)]
if len(pathcomponents) == 4:
return [msg.ChildCollection('by-port/')]
if len(pathcomponents) == 5:

View File

@@ -22,6 +22,7 @@ import confluent.log as log
import hashlib
import netifaces
import os
import re
import socket
import struct
@@ -150,3 +151,26 @@ class TLSCertVerifier(object):
raise cexc.PubkeyInvalid(
'Mismatched certificate detected', certificate, fingerprint,
self.fieldname, 'mismatch')
numregex = re.compile('([0-9]+)')
def naturalize_string(key):
"""Analyzes string in a human way to enable natural sort
:param nodename: The node name to analyze
:returns: A structure that can be consumed by 'sorted'
"""
return [int(text) if text.isdigit() else text.lower()
for text in re.split(numregex, key)]
def natural_sort(iterable):
"""Return a sort using natural sort if possible
:param iterable:
:return:
"""
try:
return sorted(iterable, key=naturalize_string)
except TypeError:
# The natural sort attempt failed, fallback to ascii sort
return sorted(iterable)