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:
@@ -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:
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user