mirror of
https://github.com/xcat2/confluent.git
synced 2025-08-23 19:50:23 +00:00
Further refactor and mature neighbor data
At this point, the /networknig/neighbors mostly works...
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016 Lenovo
|
||||
# Copyright 2016, 2017 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -30,10 +30,18 @@
|
||||
|
||||
# Provides support for viewing and processing lldp data for switches
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
import confluent.config.configmanager as cfm
|
||||
import confluent.exceptions as exc
|
||||
import confluent.log as log
|
||||
import confluent.messages as msg
|
||||
import confluent.snmputil as snmp
|
||||
import confluent.networking.netutil as netutil
|
||||
import confluent.util as util
|
||||
import eventlet
|
||||
from eventlet.greenpool import GreenPool
|
||||
import eventlet.semaphore
|
||||
import re
|
||||
|
||||
# The interesting OIDs are:
|
||||
@@ -70,6 +78,10 @@ import re
|
||||
# # 1.0.8802.1.1.2.1.4.1.1.10 - SysDesc - good stuff
|
||||
|
||||
|
||||
_neighdata = {}
|
||||
_updatelocks = {}
|
||||
|
||||
|
||||
def lenovoname(idx, desc):
|
||||
if desc.isdigit():
|
||||
return 'Ethernet' + str(idx)
|
||||
@@ -104,23 +116,52 @@ def _extract_neighbor_data_b(args):
|
||||
idxtoifname[idx] = _lldpdesc_to_ifname(sid, idx, str(oidindex[1]))
|
||||
for remotedesc in conn.walk('1.0.8802.1.1.2.1.4.1.1.10'):
|
||||
iname = idxtoifname[remotedesc[0][-2]]
|
||||
lldpdata[iname] = {'description': str(remotedesc[1])}
|
||||
lldpdata[iname] = {'peerdescription': str(remotedesc[1])}
|
||||
for remotename in conn.walk('1.0.8802.1.1.2.1.4.1.1.9'):
|
||||
iname = idxtoifname[remotename[0][-2]]
|
||||
if iname not in lldpdata:
|
||||
lldpdata[iname] = {}
|
||||
lldpdata[iname]['name'] = str(remotename[1])
|
||||
lldpdata[iname]['peername'] = str(remotename[1])
|
||||
for remotename in conn.walk('1.0.8802.1.1.2.1.4.1.1.7'):
|
||||
iname = idxtoifname[remotename[0][-2]]
|
||||
if iname not in lldpdata:
|
||||
lldpdata[iname] = {}
|
||||
lldpdata[iname]['peerport'] = str(remotename[1])
|
||||
for remoteid in conn.walk('1.0.8802.1.1.2.1.4.1.1.5'):
|
||||
iname = idxtoifname[remoteid[0][-2]]
|
||||
if iname not in lldpdata:
|
||||
lldpdata[iname] = {}
|
||||
lldpdata[iname]['chassisid'] = str(remoteid[1])
|
||||
print(repr(lldpdata))
|
||||
lldpdata[iname]['peerchassisid'] = str(remoteid[1])
|
||||
_neighdata[switch] = lldpdata
|
||||
|
||||
|
||||
def update_switch_data(switch, configmanager):
|
||||
switchcreds = netutil.get_switchcreds(configmanager, (switch,))[0]
|
||||
_extract_neighbor_data(switchcreds)
|
||||
return _neighdata.get(switch, {})
|
||||
|
||||
def _update_neighbors_backend(configmanager):
|
||||
global _neighdata
|
||||
_neighdata = {}
|
||||
switches = list_switches(configmanager)
|
||||
switchcreds = netutil.get_switchcreds(configmanager, switches)
|
||||
pool = GreenPool(64)
|
||||
for ans in pool.imap(_extract_neighbor_data, switchcreds):
|
||||
yield ans
|
||||
|
||||
|
||||
def _extract_neighbor_data(args):
|
||||
# single switch neighbor data update
|
||||
switch = args[0]
|
||||
if switch not in _updatelocks:
|
||||
_updatelocks[switch] = eventlet.semaphore.Semaphore()
|
||||
if _updatelocks[switch].locked():
|
||||
while _updatelocks[switch].locked():
|
||||
eventlet.sleep(1)
|
||||
return
|
||||
try:
|
||||
_extract_neighbor_data_b(args)
|
||||
with _updatelocks[switch]:
|
||||
_extract_neighbor_data_b(args)
|
||||
except Exception:
|
||||
log.logtrace()
|
||||
|
||||
@@ -128,4 +169,26 @@ if __name__ == '__main__':
|
||||
# a quick one-shot test, args are switch and snmpv1 string for now
|
||||
# (should do three argument form for snmpv3 test
|
||||
import sys
|
||||
_extract_neighbor_data((sys.argv[1], sys.argv[2]))
|
||||
_extract_neighbor_data((sys.argv[1], sys.argv[2], None))
|
||||
print(repr(_neighdata))
|
||||
|
||||
|
||||
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
|
||||
update_switch_data(switchname, configmanager)
|
||||
return [msg.ChildCollection(x) for x in util.natural_sort(
|
||||
_neighdata[switchname])]
|
||||
portname = pathcomponents[2]
|
||||
return [msg.ChildCollection(repr(_neighdata[switchname][portname]))]
|
||||
|
||||
|
||||
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())
|
@@ -30,11 +30,12 @@
|
||||
|
||||
# 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
|
||||
from confluent.networking.netutil import get_switchcreds, list_switches
|
||||
|
||||
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
|
||||
@@ -181,7 +182,7 @@ def _map_switch_backend(args):
|
||||
except ValueError:
|
||||
# ifidx might be '', skip in such a case
|
||||
continue
|
||||
ifnamemap = _get_portnamemap(conn)
|
||||
ifnamemap = netutil.get_portnamemap(conn)
|
||||
maccounts = {}
|
||||
bridgetoifvalid = False
|
||||
for mac in mactobridge:
|
||||
@@ -240,24 +241,6 @@ 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:
|
||||
@@ -299,27 +282,12 @@ def update_macmap(configmanager, impatient=False):
|
||||
eventlet.spawn_n(_finish_update, completions)
|
||||
raise
|
||||
|
||||
|
||||
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
|
||||
@@ -365,35 +333,13 @@ def _full_updatemacmap(configmanager):
|
||||
_switchportmap[curswitch][portname] = None
|
||||
else:
|
||||
_switchportmap[curswitch][portname] = node
|
||||
switchauth = _get_switchcreds(configmanager, switches)
|
||||
pool = GreenPool()
|
||||
switchauth = get_switchcreds(configmanager, switches)
|
||||
pool = GreenPool(64)
|
||||
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 = {}
|
||||
@@ -420,26 +366,6 @@ def handle_api_request(configmanager, inputdata, operation, pathcomponents):
|
||||
operation, '/'.join(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
|
||||
@@ -451,7 +377,7 @@ def handle_read_api_request(pathcomponents, configmanager):
|
||||
return [msg.ChildCollection('by-switch/')]
|
||||
elif len(pathcomponents) == 3:
|
||||
return [msg.ChildCollection(x + '/')
|
||||
for x in _list_switches(configmanager)]
|
||||
for x in list_switches(configmanager)]
|
||||
else:
|
||||
return _handle_neighbor_query(pathcomponents[3:], configmanager)
|
||||
elif len(pathcomponents) == 2:
|
||||
@@ -481,7 +407,7 @@ def handle_read_api_request(pathcomponents, configmanager):
|
||||
elif pathcomponents[2] == 'by-switch':
|
||||
if len(pathcomponents) == 3:
|
||||
return [msg.ChildCollection(x + '/')
|
||||
for x in _list_switches(configmanager)]
|
||||
for x in list_switches(configmanager)]
|
||||
if len(pathcomponents) == 4:
|
||||
return [msg.ChildCollection('by-port/')]
|
||||
if len(pathcomponents) == 5:
|
||||
|
72
confluent_server/confluent/networking/netutil.py
Normal file
72
confluent_server/confluent/networking/netutil.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import confluent.util as util
|
||||
|
||||
|
||||
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 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 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
|
Reference in New Issue
Block a user