mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Build complete spanning tree for data links and use it to orient tree
Rather than obeying the "parent" field, which leads to wrong-way up graphs when building from a non-root site - instead build a complete cost-based spanning tree from the specified root node. Then use the generated tree - falling back on parent if nothing is found - to populate the tree in order. Tested on Herbert's network, correctly generates topology from several different locations. Still to come: adding a mechanism for providing parenting overloads to allow for cases where a long path is actually optimal, but we have no way of knowing that.
This commit is contained in:
parent
d2aa804ace
commit
2d3874e812
@ -76,19 +76,36 @@ def buildFlatGraph():
|
|||||||
net.createNetworkJson()
|
net.createNetworkJson()
|
||||||
net.createShapedDevices()
|
net.createShapedDevices()
|
||||||
|
|
||||||
def buildFullGraph():
|
def linkSiteTarget(link, direction):
|
||||||
# Attempts to build a full network graph, incorporating as much of the UISP
|
# Helper function to extract the site ID from a data link. Returns
|
||||||
# hierarchy as possible.
|
# None if not present.
|
||||||
from integrationCommon import NetworkGraph, NetworkNode, NodeType
|
if link[direction]['site'] is not None:
|
||||||
from ispConfig import generatedPNUploadMbps, generatedPNDownloadMbps
|
return link[direction]['site']['identification']['id']
|
||||||
|
|
||||||
# Load network sites
|
return None
|
||||||
print("Loading Data from UISP")
|
|
||||||
sites = uispRequest("sites")
|
|
||||||
devices = uispRequest("devices?withInterfaces=true&authorized=true")
|
|
||||||
dataLinks = uispRequest("data-links?siteLinksOnly=true")
|
|
||||||
|
|
||||||
# Do we already have a integrationUISPbandwidths.csv file?
|
def findSiteLinks(dataLinks, siteId):
|
||||||
|
# Searches the Data Links for any links to/from the specified site.
|
||||||
|
# Returns a list of site IDs that are linked to the specified site.
|
||||||
|
links = []
|
||||||
|
for dl in dataLinks:
|
||||||
|
fromSiteId = linkSiteTarget(dl, "from")
|
||||||
|
if fromSiteId is not None and fromSiteId == siteId:
|
||||||
|
# We have a link originating in this site.
|
||||||
|
target = linkSiteTarget(dl, "to")
|
||||||
|
if target is not None:
|
||||||
|
links.append(target)
|
||||||
|
|
||||||
|
toSiteId = linkSiteTarget(dl, "to")
|
||||||
|
if toSiteId is not None and toSiteId == siteId:
|
||||||
|
# We have a link originating in this site.
|
||||||
|
target = linkSiteTarget(dl, "from")
|
||||||
|
if target is not None:
|
||||||
|
links.append(target)
|
||||||
|
return links
|
||||||
|
|
||||||
|
def buildSiteBandwidths():
|
||||||
|
# Builds a dictionary of site bandwidths from the integrationUISPbandwidths.csv file.
|
||||||
siteBandwidth = {}
|
siteBandwidth = {}
|
||||||
if os.path.isfile("integrationUISPbandwidths.csv"):
|
if os.path.isfile("integrationUISPbandwidths.csv"):
|
||||||
with open('integrationUISPbandwidths.csv') as csv_file:
|
with open('integrationUISPbandwidths.csv') as csv_file:
|
||||||
@ -99,8 +116,10 @@ def buildFullGraph():
|
|||||||
download = int(download)
|
download = int(download)
|
||||||
upload = int(upload)
|
upload = int(upload)
|
||||||
siteBandwidth[name] = {"download": download, "upload": upload}
|
siteBandwidth[name] = {"download": download, "upload": upload}
|
||||||
|
return siteBandwidth
|
||||||
|
|
||||||
# Find AP capacities from UISP
|
def findApCapacities(devices, siteBandwidth):
|
||||||
|
# Searches the UISP devices for APs and adds their capacities to the siteBandwidth dictionary.
|
||||||
for device in devices:
|
for device in devices:
|
||||||
if device['identification']['role'] == "ap":
|
if device['identification']['role'] == "ap":
|
||||||
name = device['identification']['name']
|
name = device['identification']['name']
|
||||||
@ -111,7 +130,7 @@ def buildFullGraph():
|
|||||||
siteBandwidth[device['identification']['name']] = {
|
siteBandwidth[device['identification']['name']] = {
|
||||||
"download": download, "upload": upload}
|
"download": download, "upload": upload}
|
||||||
|
|
||||||
# Find Site Capacities by AirFiber capacities
|
def findAirfibers(devices, generatedPNDownloadMbps, generatedPNUploadMbps):
|
||||||
foundAirFibersBySite = {}
|
foundAirFibersBySite = {}
|
||||||
for device in devices:
|
for device in devices:
|
||||||
if device['identification']['site']['type'] == 'site':
|
if device['identification']['site']['type'] == 'site':
|
||||||
@ -134,6 +153,99 @@ def buildFullGraph():
|
|||||||
foundAirFibersBySite[device['identification']['site']['id']]['upload'] = upload
|
foundAirFibersBySite[device['identification']['site']['id']]['upload'] = upload
|
||||||
else:
|
else:
|
||||||
foundAirFibersBySite[device['identification']['site']['id']] = {'download': download, 'upload': upload}
|
foundAirFibersBySite[device['identification']['site']['id']] = {'download': download, 'upload': upload}
|
||||||
|
return foundAirFibersBySite
|
||||||
|
|
||||||
|
def buildSiteList(sites, dataLinks):
|
||||||
|
# Builds a list of sites, including their IDs, names, and connections.
|
||||||
|
# Connections are determined by the dataLinks list.
|
||||||
|
siteList = []
|
||||||
|
for site in sites:
|
||||||
|
newSite = {
|
||||||
|
'id': site['identification']['id'],
|
||||||
|
'name': site['identification']['name'],
|
||||||
|
'connections': findSiteLinks(dataLinks, site['identification']['id']),
|
||||||
|
'cost': 10000,
|
||||||
|
'parent': "",
|
||||||
|
'type': type,
|
||||||
|
}
|
||||||
|
siteList.append(newSite)
|
||||||
|
return siteList
|
||||||
|
|
||||||
|
def findInSiteList(siteList, name):
|
||||||
|
# Searches the siteList for a site with the specified name.
|
||||||
|
for site in siteList:
|
||||||
|
if site['name'] == name:
|
||||||
|
return site
|
||||||
|
return None
|
||||||
|
|
||||||
|
def findInSiteListById(siteList, id):
|
||||||
|
# Searches the siteList for a site with the specified name.
|
||||||
|
for site in siteList:
|
||||||
|
if site['id'] == id:
|
||||||
|
return site
|
||||||
|
return None
|
||||||
|
|
||||||
|
def debugSpaces(n):
|
||||||
|
# Helper function to print n spaces.
|
||||||
|
spaces = ""
|
||||||
|
for i in range(int(n)):
|
||||||
|
spaces = spaces + " "
|
||||||
|
return spaces
|
||||||
|
|
||||||
|
def walkGraphOutwards(siteList, root):
|
||||||
|
def walkGraph(node, parent, cost, backPath):
|
||||||
|
site = findInSiteListById(siteList, node)
|
||||||
|
if cost < site['cost']:
|
||||||
|
# It's cheaper to get here this way, so update the cost and parent.
|
||||||
|
site['cost'] = cost
|
||||||
|
site['parent'] = parent['id']
|
||||||
|
#print(debugSpaces(cost/10) + parent['name'] + "->" + site['name'] + " -> New cost: " + str(cost))
|
||||||
|
|
||||||
|
for connection in site['connections']:
|
||||||
|
if not connection in backPath:
|
||||||
|
#target = findInSiteListById(siteList, connection)
|
||||||
|
#print(debugSpaces((cost+10)/10) + site['name'] + " -> " + target['name'] + " (" + str(target['cost']) + ")")
|
||||||
|
newBackPath = backPath.copy()
|
||||||
|
newBackPath.append(site['id'])
|
||||||
|
walkGraph(connection, site, cost+10, newBackPath)
|
||||||
|
|
||||||
|
for connection in root['connections']:
|
||||||
|
# Force the parent since we're at the top
|
||||||
|
site = findInSiteListById(siteList, connection)
|
||||||
|
site['parent'] = root['id']
|
||||||
|
walkGraph(connection, root, 20, [root['id']])
|
||||||
|
|
||||||
|
def buildFullGraph():
|
||||||
|
# Attempts to build a full network graph, incorporating as much of the UISP
|
||||||
|
# hierarchy as possible.
|
||||||
|
from integrationCommon import NetworkGraph, NetworkNode, NodeType
|
||||||
|
from ispConfig import generatedPNUploadMbps, generatedPNDownloadMbps
|
||||||
|
|
||||||
|
# Load network sites
|
||||||
|
print("Loading Data from UISP")
|
||||||
|
sites = uispRequest("sites")
|
||||||
|
devices = uispRequest("devices?withInterfaces=true&authorized=true")
|
||||||
|
dataLinks = uispRequest("data-links?siteLinksOnly=true")
|
||||||
|
|
||||||
|
# Build Site Capacities
|
||||||
|
siteBandwidth = buildSiteBandwidths()
|
||||||
|
findApCapacities(devices, siteBandwidth)
|
||||||
|
foundAirFibersBySite = findAirfibers(devices, generatedPNDownloadMbps, generatedPNUploadMbps)
|
||||||
|
|
||||||
|
# Create a list of just network sites
|
||||||
|
siteList = buildSiteList(sites, dataLinks)
|
||||||
|
rootSite = findInSiteList(siteList, uispSite)
|
||||||
|
if rootSite is None:
|
||||||
|
print("ERROR: Unable to find root site in UISP")
|
||||||
|
return
|
||||||
|
walkGraphOutwards(siteList, rootSite)
|
||||||
|
# Debug code: dump the list of site parents
|
||||||
|
# for s in siteList:
|
||||||
|
# if s['parent'] == "":
|
||||||
|
# p = "None"
|
||||||
|
# else:
|
||||||
|
# p = findInSiteListById(siteList, s['parent'])['name']
|
||||||
|
# print(s['name'] + " (" + str(s['cost']) + ") <-- " + p)
|
||||||
|
|
||||||
print("Building Topology")
|
print("Building Topology")
|
||||||
net = NetworkGraph()
|
net = NetworkGraph()
|
||||||
@ -146,10 +258,12 @@ def buildFullGraph():
|
|||||||
upload = generatedPNUploadMbps
|
upload = generatedPNUploadMbps
|
||||||
address = ""
|
address = ""
|
||||||
customerName = ""
|
customerName = ""
|
||||||
if site['identification']['parent'] is None:
|
parent = findInSiteListById(siteList, id)['parent']
|
||||||
parent = ""
|
if parent == "":
|
||||||
else:
|
if site['identification']['parent'] is None:
|
||||||
parent = site['identification']['parent']['id']
|
parent = ""
|
||||||
|
else:
|
||||||
|
parent = site['identification']['parent']['id']
|
||||||
match type:
|
match type:
|
||||||
case "site":
|
case "site":
|
||||||
nodeType = NodeType.site
|
nodeType = NodeType.site
|
||||||
|
Loading…
Reference in New Issue
Block a user