Merge pull request #1165 from LibreQoE/develop

Develop
This commit is contained in:
Robert Chacón authored and GitHub committed 2026-09-08 12:58:44 -06:00
commit 3eeaf942c7
2 files changed
+119 -24

No files matched your search

+1 -1
View File
@@ -38,7 +38,7 @@ def ultimateReplace(app, docname, source):
source[0] = result
ultimate_replacements = {
"{deb_url_v1_5}" : "libreqos_2.2.202608181534-1_amd64.deb"
"{deb_url_v1_5}" : "libreqos_2.2.202609081854-1_amd64.deb"
}
def setup(app):
+118 -23
View File
@@ -7,7 +7,7 @@ from integrationCommon import isIpv4Permitted
import base64
from requests.auth import HTTPBasicAuth
if find_ipv6_using_mikrotik() == True:
from mikrotikFindIPv6 import pullMikrotikIPv6
from mikrotikFindIPv6 import pullMikrotikIPv6
from integrationCommon import NetworkGraph, NetworkNode, NodeType, apply_client_bandwidth_multiplier
from urllib3.exceptions import InsecureRequestWarning
@@ -17,7 +17,7 @@ def getCustomerInfo():
data = {}
data['apiKey'] = powercode_api_key()
data['action'] = 'list_customers'
r = requests.post(url, data=data, headers=headers, verify=False, timeout=10)
return r.json()
@@ -27,69 +27,163 @@ def getListServices():
data = {}
data['apiKey'] = powercode_api_key()
data['action'] = 'list_services'
r = requests.post(url, data=data, headers=headers, verify=False, timeout=10)
servicesDict = {}
for service in r.json():
if service['rate_down'] and service['rate_up']:
servicesDict[service['id']] = {}
servicesDict[service['id']]['downloadMbps'] = apply_client_bandwidth_multiplier(int(service['rate_down']) / 1000)
servicesDict[service['id']]['uploadMbps'] = apply_client_bandwidth_multiplier(int(service['rate_up']) / 1000)
servicesDict[str(service['id'])] = {}
servicesDict[str(service['id'])]['downloadMbps'] = apply_client_bandwidth_multiplier(int(service['rate_down']) / 1000)
servicesDict[str(service['id'])]['uploadMbps'] = apply_client_bandwidth_multiplier(int(service['rate_up']) / 1000)
return servicesDict
def getListSites():
headers= {'Content-Type': 'application/x-www-form-urlencoded'}
url = powercode_api_url() + ":444/api/preseem/index.php"
data = {}
data['apiKey'] = powercode_api_key()
data['action'] = 'list_sites'
r = requests.post(url, data=data, headers=headers, verify=False, timeout=10)
return r.json()
def createShaper():
net = NetworkGraph()
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
print("Fetching data from Powercode")
customerInfo = getCustomerInfo()
customerIDs = []
for customer in customerInfo:
if customer['id'] != '1':
if customer['id'] != '':
if str(customer['id']) != '1':
if str(customer['id']) != '':
if customer['status'] == 'Active':
customerIDint = int(customer['id'])
if customerIDint != 0:
if customerIDint != None:
if customerIDint not in customerIDs:
customerIDs.append(customerIDint)
allServices = getListServices()
sitesInfo = getListSites()
# Map site infrastructure equipment (APs, routers, backhauls) by equipment id.
# Powercode returns ids as ints or strings depending on endpoint, so str() everything.
siteEquipment = {}
siteNames = {}
for site in sitesInfo:
siteNodeId = "site_" + str(site['id'])
siteNames[siteNodeId] = str(site['name']).strip() if site['name'] else site['name']
for equipment in site['equipment']:
siteEquipment[str(equipment['id'])] = {
'siteNodeId': siteNodeId,
'siteName': site['name'],
'name': equipment['name'],
'type': equipment['type']
}
acceptableEquipment = ['Customer Owned Equipment', 'Router', 'Customer Owned Equipment', 'Managed Routers', 'CPE']
devicesByCustomerID = {}
for customer in customerInfo:
if customer['status'] == 'Active':
chosenName = ''
if customer['name'] != '':
chosenName = customer['name']
chosenName = str(customer['name']).strip()
elif customer['company_name'] != '':
chosenName = customer['company_name']
chosenName = str(customer['company_name']).strip()
else:
chosenName = customer['id']
chosenName = str(customer['id'])
for equipment in customer['equipment']:
if equipment['type'] in acceptableEquipment:
if equipment['service_id'] in allServices:
if str(equipment['service_id']) in allServices:
device = {}
device['id'] = "c_" + customer['id'] + "_s_" + "_d_" + equipment['id']
device['id'] = "c_" + str(customer['id']) + "_s_" + "_d_" + str(equipment['id'])
device['name'] = equipment['name']
device['ipv4'] = equipment['ip_address']
device['mac'] = equipment['mac_address']
parentEquipmentId = equipment.get('parent_id')
if parentEquipmentId is not None and str(parentEquipmentId) in siteEquipment:
device['parentEquipment'] = str(parentEquipmentId)
else:
device['parentEquipment'] = None
if customer['id'] not in devicesByCustomerID:
devicesByCustomerID[customer['id']] = {}
devicesByCustomerID[customer['id']]['name'] = chosenName
devicesByCustomerID[customer['id']]['downloadMbps'] = allServices[equipment['service_id']]['downloadMbps']
devicesByCustomerID[customer['id']]['uploadMbps'] = allServices[equipment['service_id']]['uploadMbps']
devicesByCustomerID[customer['id']]['downloadMbps'] = allServices[str(equipment['service_id'])]['downloadMbps']
devicesByCustomerID[customer['id']]['uploadMbps'] = allServices[str(equipment['service_id'])]['uploadMbps']
if 'devices' not in devicesByCustomerID[customer['id']]:
devicesByCustomerID[customer['id']]['devices'] = []
devicesByCustomerID[customer['id']]['devices'].append(device)
# Resolve one topology parent (site AP) per customer: direct parent_id if it
# maps to live site equipment, otherwise inherit from a sibling device that
# resolves. Customers with no resolvable AP remain attached at the root.
for customerID in devicesByCustomerID:
resolvedParents = []
for device in devicesByCustomerID[customerID]['devices']:
if device['parentEquipment'] is not None:
resolvedParents.append(device['parentEquipment'])
devicesByCustomerID[customerID]['apEquipment'] = resolvedParents[0] if resolvedParents else None
referencedEquipment = set()
for customerID in devicesByCustomerID:
apEquipment = devicesByCustomerID[customerID]['apEquipment']
if apEquipment is not None:
referencedEquipment.add(apEquipment)
referencedSites = set()
for equipmentId in referencedEquipment:
referencedSites.add(siteEquipment[equipmentId]['siteNodeId'])
# Display names must be trimmed and unique: the topology compiler resolves
# parent references by (trimmed) name and validates exact matches.
usedTopologyNames = set()
def uniqueTopologyName(name):
clean = str(name).strip() if name else ''
if not clean:
clean = 'Unnamed'
base = clean
counter = 2
while clean in usedTopologyNames:
clean = base + ' (' + str(counter) + ')'
counter += 1
usedTopologyNames.add(clean)
return clean
for siteNodeId in referencedSites:
siteNode = NetworkNode(
id=siteNodeId,
displayName=uniqueTopologyName(siteNames[siteNodeId]),
parentId='',
type=NodeType.site,
networkJsonId="powercode:" + siteNodeId
)
net.addRawNode(siteNode)
for equipmentId in referencedEquipment:
equipmentInfo = siteEquipment[equipmentId]
apName = equipmentInfo['name'] if equipmentInfo['name'] else equipmentInfo['siteName']
apNode = NetworkNode(
id="ap_" + equipmentId,
displayName=uniqueTopologyName(apName),
parentId=equipmentInfo['siteNodeId'],
type=NodeType.ap,
networkJsonId="powercode:ap:" + equipmentId
)
net.addRawNode(apNode)
attachedCount = 0
for customerID in devicesByCustomerID:
apEquipment = devicesByCustomerID[customerID]['apEquipment']
clientParentId = ("ap_" + apEquipment) if apEquipment is not None else ''
if apEquipment is not None:
attachedCount += 1
customer = NetworkNode(
type=NodeType.client,
id=customerID,
id=str(customerID),
parentId=clientParentId,
displayName=devicesByCustomerID[customerID]['name'],
address='',
customerName=devicesByCustomerID[customerID]['name'],
@@ -102,13 +196,14 @@ def createShaper():
id=device['id'],
displayName=device["name"],
type=NodeType.device,
parentId=customerID,
parentId=str(customerID),
mac=device["mac"],
ipv4=[device['ipv4']],
ipv6=[]
)
net.addRawNode(newDevice)
print("Imported " + str(len(devicesByCustomerID)) + " customers")
print("Topology: attached " + str(attachedCount) + " of " + str(len(devicesByCustomerID)) + " customers to " + str(len(referencedEquipment)) + " APs across " + str(len(referencedSites)) + " sites (" + str(len(devicesByCustomerID) - attachedCount) + " unattached)")
net.prepareTree()
net.materializeCompiledTopology("python/powercode", "full")