Merge pull request #335 from LibreQoE/fix334

UISP Integration Improvements
This commit is contained in:
Robert Chacón 2023-04-14 05:49:36 -06:00 committed by GitHub
commit 24b4609fd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 148 additions and 46 deletions

58
src/csvToNetworkJSON.py Normal file
View File

@ -0,0 +1,58 @@
from pythonCheck import checkPythonVersion
checkPythonVersion()
import os
import csv
import json
from ispConfig import uispSite, uispStrategy, overwriteNetworkJSONalways
from ispConfig import generatedPNUploadMbps, generatedPNDownloadMbps, upstreamBandwidthCapacityDownloadMbps, upstreamBandwidthCapacityUploadMbps
from integrationCommon import NetworkGraph, NetworkNode, NodeType
def csvToNetworkJSONfile():
sites = []
with open('manualNetwork.csv') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
for row in csv_reader:
if 'Site Name' in row[0]:
header = row
else:
name, down, up, parent = row
site = {'name': name,
'download': down,
'upload': up,
'parent': parent}
sites.append(site)
net = NetworkGraph()
idCounter = 1000
nameToID = {}
for site in sites:
site['id'] = idCounter
idCounter = idCounter + 1
nameToID[site['name']] = site['id']
for site in sites:
id = site['id']
if site['parent'] == '':
parentID = None
else:
parentID = nameToID[site['parent']]
name = site['name']
parent = site['parent']
download = site['download']
upload = site['upload']
nodeType = NodeType.site
node = NetworkNode(id=id, displayName=name, type=nodeType,
parentId=parentID, download=download, upload=upload, address=None, customerName=None)
net.addRawNode(node)
net.prepareTree()
net.plotNetworkGraph(False)
if net.doesNetworkJsonExist():
if overwriteNetworkJSONalways:
net.createNetworkJson()
else:
print("network.json already exists and overwriteNetworkJSONalways set to False. Leaving in-place.")
else:
net.createNetworkJson()
net.createShapedDevices()
if __name__ == '__main__':
csvToNetworkJSONfile()

View File

@ -131,11 +131,23 @@ def findApCapacities(devices, siteBandwidth):
if device['identification']['role'] == "ap":
name = device['identification']['name']
if not name in siteBandwidth and device['overview']['downlinkCapacity'] and device['overview']['uplinkCapacity']:
safeToUse = True
download = int(device['overview']
['downlinkCapacity'] / 1000000)
upload = int(device['overview']['uplinkCapacity'] / 1000000)
siteBandwidth[device['identification']['name']] = {
"download": download, "upload": upload}
if download < 15:
print("WARNING: Device '" + device['identification']['hostname'] + "' has unusually low download capacity (" + str(upload) + " Mbps). Discarding in favor of parent site rates.")
safeToUse = False
if upload < 15:
print("WARNING: Device '" + device['identification']['hostname'] + "' has unusually low upload capacity (" + str(download) + " Mbps). Discarding in favor of parent site rates.")
safeToUse = False
if device['identification']['model'] == 'WaveAP':
if (download < 500) or (upload < 500):
download = 2450
upload = 2450
if safeToUse:
siteBandwidth[device['identification']['name']] = {
"download": download, "upload": upload}
def findAirfibers(devices, generatedPNDownloadMbps, generatedPNUploadMbps):
foundAirFibersBySite = {}
@ -147,19 +159,17 @@ def findAirfibers(devices, generatedPNDownloadMbps, generatedPNUploadMbps):
if device['overview']['downlinkCapacity'] is not None and device['overview']['uplinkCapacity'] is not None:
download = int(device['overview']['downlinkCapacity']/ 1000000)
upload = int(device['overview']['uplinkCapacity']/ 1000000)
else:
download = generatedPNDownloadMbps
upload = generatedPNUploadMbps
# Make sure to use half of reported bandwidth for AF60-LRs
if device['identification']['model'] == "AF60-LR":
download = int(download / 2)
upload = int(download / 2)
if device['identification']['site']['id'] in foundAirFibersBySite:
if (download > foundAirFibersBySite[device['identification']['site']['id']]['download']) or (upload > foundAirFibersBySite[device['identification']['site']['id']]['upload']):
foundAirFibersBySite[device['identification']['site']['id']]['download'] = download
foundAirFibersBySite[device['identification']['site']['id']]['upload'] = upload
else:
foundAirFibersBySite[device['identification']['site']['id']] = {'download': download, 'upload': upload}
# Make sure to use half of reported bandwidth for AF60/AF60-LRs
if (device['identification']['model'] == "AF60-LR") or (device['identification']['model'] == "AF60"):
download = int(download / 2)
upload = int(download / 2)
if device['identification']['site']['id'] in foundAirFibersBySite:
if (download > foundAirFibersBySite[device['identification']['site']['id']]['download']) or (upload > foundAirFibersBySite[device['identification']['site']['id']]['upload']):
foundAirFibersBySite[device['identification']['site']['id']]['download'] = download
foundAirFibersBySite[device['identification']['site']['id']]['upload'] = upload
#print(device['identification']['name'] + ' will override bandwidth for site ' + device['identification']['site']['name'])
else:
foundAirFibersBySite[device['identification']['site']['id']] = {'download': download, 'upload': upload}
return foundAirFibersBySite
def buildSiteList(sites, dataLinks):
@ -240,6 +250,39 @@ def loadRoutingOverrides():
#print(overrides)
return overrides
def findNodesBranchedOffPtMP(siteList, dataLinks, sites, rootSite):
nodeOffPtMP = {}
for site in sites:
id = site['identification']['id']
name = site['identification']['name']
type = site['identification']['type']
if id != rootSite['id']:
trueParent = findInSiteListById(siteList, id)['parent']
if type == 'site':
#parent = findInSiteListById(siteList, id)['parent']
if ('parent' in site['identification']) and (site['identification']['parent'] is not None):
parent = site['identification']['parent']['id']
for link in dataLinks:
if (link['to']['site'] is not None) and (link['to']['site']['identification'] is not None):
if ('identification' in link['to']['site']) and (link['to']['site']['identification'] is not None):
if ('identification' in link['from']['site']) and (link['from']['site']['identification'] is not None):
# Respect parent defined by topology and overrides
if link['from']['site']['identification']['id'] == trueParent:
if link['to']['site']['identification']['id'] == id:
if link['from']['device']['overview']['wirelessMode'] == 'ap-ptmp':
if 'overview' in link['to']['device']:
if ('downlinkCapacity' in link['to']['device']['overview']) and ('uplinkCapacity' in link['to']['device']['overview']):
if (link['to']['device']['overview']['downlinkCapacity'] is not None) and (link['to']['device']['overview']['uplinkCapacity'] is not None):
# Capacity of the PtMP client radio feeding the PoP will be used as the site bandwidth limit
download = int(round(link['to']['device']['overview']['downlinkCapacity']/1000000))
upload = int(round(link['to']['device']['overview']['uplinkCapacity']/1000000))
nodeOffPtMP[id] = { 'parent': link['from']['device']['identification']['id'],
'parentName': link['from']['device']['identification']['name'],
'download': download,
'upload': upload
}
return nodeOffPtMP
def buildFullGraph():
# Attempts to build a full network graph, incorporating as much of the UISP
# hierarchy as possible.
@ -253,18 +296,28 @@ def buildFullGraph():
dataLinks = uispRequest("data-links?siteLinksOnly=true")
# Build Site Capacities
print("Compiling Site Bandwidths")
siteBandwidth = buildSiteBandwidths()
print("Finding AP Capacities")
findApCapacities(devices, siteBandwidth)
foundAirFibersBySite = findAirfibers(devices, generatedPNDownloadMbps, generatedPNUploadMbps)
# Create a list of just network sites
print('Creating list of sites')
siteList = buildSiteList(sites, dataLinks)
rootSite = findInSiteList(siteList, uispSite)
print("Finding PtP Capacities")
foundAirFibersBySite = findAirfibers(devices, generatedPNDownloadMbps, generatedPNUploadMbps)
print('Creating list of route overrides')
routeOverrides = loadRoutingOverrides()
if rootSite is None:
print("ERROR: Unable to find root site in UISP")
return
print('Creating graph')
walkGraphOutwards(siteList, rootSite, routeOverrides)
print("Finding PtMP Capacities")
nodeOffPtMP = findNodesBranchedOffPtMP(siteList, dataLinks, sites, rootSite)
# Debug code: dump the list of site parents
# for s in siteList:
# if s['parent'] == "":
@ -273,25 +326,8 @@ def buildFullGraph():
# p = findInSiteListById(siteList, s['parent'])['name']
# print(s['name'] + " (" + str(s['cost']) + ") <-- " + p)
# Find Nodes Connected By PtMP
nodeOffPtMP = {}
for site in sites:
id = site['identification']['id']
name = site['identification']['name']
type = site['identification']['type']
parent = findInSiteListById(siteList, id)['parent']
if type == 'site':
for link in dataLinks:
if link['from']['site'] is not None and link['from']['site']['identification']['id'] == parent:
if link['to']['site'] is not None and link['to']['site']['identification']['id'] == id:
if link['from']['device'] is not None and link['to']['device'] is not None and link['to']['device']['overview'] is not None and link['to']['device']['overview']['downlinkCapacity'] is not None and link['from']['device']['overview']['wirelessMode'] == 'ap-ptmp':
# Capacity of the PtMP client radio feeding the PoP will be used as the site bandwidth limit
download = int(round(link['to']['device']['overview']['downlinkCapacity']/1000000))
upload = int(round(link['to']['device']['overview']['uplinkCapacity']/1000000))
nodeOffPtMP[id] = { 'parent': link['from']['device']['identification']['id'],
'download': download,
'upload': upload
}
print("Building Topology")
net = NetworkGraph()
# Add all sites and client sites
@ -318,15 +354,19 @@ def buildFullGraph():
# Use the CSV bandwidth values
download = siteBandwidth[name]["download"]
upload = siteBandwidth[name]["upload"]
elif id in nodeOffPtMP:
download = nodeOffPtMP[id]['download']
upload = nodeOffPtMP[id]['upload']
elif id in foundAirFibersBySite:
download = foundAirFibersBySite[id]['download']
upload = foundAirFibersBySite[id]['upload']
else:
# Add them just in case
siteBandwidth[name] = {
else:
# Use limits from foundAirFibersBySite
if id in foundAirFibersBySite:
download = foundAirFibersBySite[id]['download']
upload = foundAirFibersBySite[id]['upload']
#print('Site ' + name + ' will use bandwidth from foundAirFibersBySite.')
# Use limits from nodeOffPtMP only if they're greater than what is already set
if id in nodeOffPtMP:
if (nodeOffPtMP[id]['download'] >= download) or (nodeOffPtMP[id]['upload'] >= upload):
download = nodeOffPtMP[id]['download']
upload = nodeOffPtMP[id]['upload']
#print('Site ' + name + ' will use bandwidth from nodeOffPtMP.')
siteBandwidth[name] = {
"download": download, "upload": upload}
case default:
nodeType = NodeType.client
@ -364,7 +404,7 @@ def buildFullGraph():
# TODO: Figure out Mikrotik IPv6?
mac = device['identification']['mac']
net.addRawNode(NetworkNode(id=device['identification']['id'], displayName=device['identification']
['name'], parentId=id, type=NodeType.device, ipv4=ipv4, ipv6=ipv6, mac=mac))

View File

@ -0,0 +1,4 @@
Site Name,Download Mbps,Upload Mbps,Parent Site Name
SiteA,1000,1000,
SiteB,975,975,SiteA
SiteC,950,950,SiteB
1 Site Name Download Mbps Upload Mbps Parent Site Name
2 SiteA 1000 1000
3 SiteB 975 975 SiteA
4 SiteC 950 950 SiteB