From 095717fc35ce0e4553cc48b8bf12b23472f80cec Mon Sep 17 00:00:00 2001 From: lemonheads Date: Sun, 5 Dec 2021 12:11:49 -0700 Subject: [PATCH] Updated v1.0 --- v1.0/AccessPoints.csv | 9 + v1.0/Integrations/uispImport.py | 83 +++++++++ v1.0/LibreQoS.py | 304 ++++++++++++++++++++++++++++++++ v1.0/Shaper.csv | 6 + v1.0/Sites.csv | 5 + v1.0/ispConfig.py | 36 ++++ v1.0/scheduled.py | 11 ++ 7 files changed, 454 insertions(+) create mode 100644 v1.0/AccessPoints.csv create mode 100644 v1.0/Integrations/uispImport.py create mode 100644 v1.0/LibreQoS.py create mode 100644 v1.0/Shaper.csv create mode 100644 v1.0/Sites.csv create mode 100644 v1.0/ispConfig.py create mode 100644 v1.0/scheduled.py diff --git a/v1.0/AccessPoints.csv b/v1.0/AccessPoints.csv new file mode 100644 index 00000000..041ad477 --- /dev/null +++ b/v1.0/AccessPoints.csv @@ -0,0 +1,9 @@ +AP,Max Download,Max Upload,Parent Site +AP1,250,60,Site1 +AP2,250,60,Site2 +AP3,250,60,Site3 +AP4,250,60,Site4 +AP5,250,60,Site1 +AP6,250,60,Site2 +AP7,250,60,Site3 +AP8,250,60,Site4 diff --git a/v1.0/Integrations/uispImport.py b/v1.0/Integrations/uispImport.py new file mode 100644 index 00000000..1a813638 --- /dev/null +++ b/v1.0/Integrations/uispImport.py @@ -0,0 +1,83 @@ +import requests +import csv +from ispConfig.py import UISPbaseURL, uispAuthToken, shapeRouterOrStation + + +stationModels = ['LBE-5AC-Gen2', 'LBE-5AC-Gen2', 'LBE-5AC-LR', 'AF-LTU5', 'AFLTULR', 'AFLTUPro', 'LTU-LITE'] +routerModels = ['ACB-AC', 'ACB-ISP'] + +def pullShapedDevices(): + devices = [] + uispSitesToImport = [] + url = UISPbaseURL + "/nms/api/v2.1/sites?type=client&ucrm=true&ucrmDetails=true" + headers = {'accept':'application/json', 'x-auth-token': uispAuthToken} + r = requests.get(url, headers=headers) + jsonData = r.json() + uispDevicesToImport = [] + for uispClientSite in jsonData: + if (uispClientSite['identification']['status'] == 'active'): + if (uispClientSite['qos']['downloadSpeed']) and (uispClientSite['qos']['uploadSpeed']): + downloadSpeedMbps = int(round(uispClientSite['qos']['downloadSpeed']/1000000)) + uploadSpeedMbps = int(round(uispClientSite['qos']['uploadSpeed']/1000000)) + address = uispClientSite['description']['address'] + uispClientSiteID = uispClientSite['id'] + devicesInUISPsite = getUISPdevicesAtClientSite(uispClientSiteID) + UCRMclientID = uispClientSite['ucrm']['client']['id'] + AP = 'none' + thisSiteDevices = [] + #Look for station devices, use those to find AP name + for device in devicesInUISPsite: + deviceName = device['identification']['name'] + deviceRole = device['identification']['role'] + deviceModel = device['identification']['model'] + deviceModelName = device['identification']['modelName'] + if (deviceRole == 'station') or (deviceModel in stationModels): + if device['attributes']['apDevice']: + AP = device['attributes']['apDevice']['name'] + if shapeRouterOrStation == 'router': + #Look for router devices, use those as shaped CPE + for device in devicesInUISPsite: + deviceName = device['identification']['name'] + deviceRole = device['identification']['role'] + deviceMAC = device['identification']['mac'] + deviceIPstring = device['ipAddress'] + if '/' in deviceIPstring: + deviceIPstring = deviceIPstring.split("/")[0] + deviceModel = device['identification']['model'] + deviceModelName = device['identification']['modelName'] + if (deviceRole == 'router') or (deviceModel in routerModels): + print("Added " + ":\t" + deviceName) + devices.append((UCRMclientID, AP,deviceMAC, deviceName, deviceIPstring,'', str(downloadSpeedMbps/4), str(uploadSpeedMbps/4), str(downloadSpeedMbps),str(uploadSpeedMbps))) + elif shapeRouterOrStation == 'station': + #Look for station devices, use those as shaped CPE + for device in devicesInUISPsite: + deviceName = device['identification']['name'] + deviceRole = device['identification']['role'] + deviceMAC = device['identification']['mac'] + deviceIPstring = device['ipAddress'] + if '/' in deviceIPstring: + deviceIPstring = deviceIPstring.split("/")[0] + deviceModel = device['identification']['model'] + deviceModelName = device['identification']['modelName'] + if (deviceRole == 'station') or (deviceModel in stationModels): + print("Added " + ":\t" + deviceName) + devices.append((UCRMclientID, AP,deviceMAC, deviceName, deviceIPstring,'', str(downloadSpeedMbps/4), str(uploadSpeedMbps/4), str(downloadSpeedMbps),str(uploadSpeedMbps))) + uispSitesToImport.append(thisSiteDevices) + print("Imported " + address) + else: + print("Failed to import devices from " + uispClientSite['description']['address'] + ". Missing QoS.") + return devices + +def getUISPdevicesAtClientSite(siteID): + url = UISPbaseURL + "/nms/api/v2.1/devices?siteId=" + siteID + headers = {'accept':'application/json', 'x-auth-token': uispAuthToken} + r = requests.get(url, headers=headers) + return (r.json()) + +if __name__ == '__main__': + devicesList = pullShapedDevices() + with open('Shaper.csv', 'w') as csvfile: + wr = csv.writer(csvfile, quoting=csv.QUOTE_ALL) + wr.writerow(['ID', 'AP', 'MAC', 'Hostname', 'IPv4', 'IPv6', 'Download Min', 'Upload Min', 'Download Max', 'Upload Max']) + for device in devicesList: + wr.writerow(device) diff --git a/v1.0/LibreQoS.py b/v1.0/LibreQoS.py new file mode 100644 index 00000000..69007f50 --- /dev/null +++ b/v1.0/LibreQoS.py @@ -0,0 +1,304 @@ +# Copyright (C) 2020-2021 Robert Chacón +# This file is part of LibreQoS. +# +# LibreQoS is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# LibreQoS is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with LibreQoS. If not, see . +# +# _ _ _ ___ ____ +# | | (_) |__ _ __ ___ / _ \ ___/ ___| +# | | | | '_ \| '__/ _ \ | | |/ _ \___ \ +# | |___| | |_) | | | __/ |_| | (_) |__) | +# |_____|_|_.__/|_| \___|\__\_\\___/____/ +# v.1.0-beta +# +import random +import logging +import os +import io +import json +import csv +import subprocess +from subprocess import PIPE +import ipaddress +from ipaddress import IPv4Address, IPv6Address +import time +from datetime import date, datetime +from ispConfig import fqOrCAKE, upstreamBandwidthCapacityDownloadMbps, upstreamBandwidthCapacityUploadMbps, defaultClassCapacityDownloadMbps, defaultClassCapacityUploadMbps, interfaceA, interfaceB, shapeBySite, enableActualShellCommands, runShellCommandsAsSudo +import collections + +def shell(command): + if enableActualShellCommands: + if runShellCommandsAsSudo: + command = 'sudo ' + command + commands = command.split(' ') + print(command) + proc = subprocess.Popen(commands, stdout=subprocess.PIPE) + for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"): # or another encoding + print(line) + else: + print(command) + +def clearPriorSettings(interfaceA, interfaceB): + shell('tc filter delete dev ' + interfaceA) + shell('tc filter delete dev ' + interfaceA + ' root') + shell('tc qdisc delete dev ' + interfaceA + ' root') + shell('tc qdisc delete dev ' + interfaceA) + shell('tc filter delete dev ' + interfaceB) + shell('tc filter delete dev ' + interfaceB + ' root') + shell('tc qdisc delete dev ' + interfaceB + ' root') + shell('tc qdisc delete dev ' + interfaceB) + if runShellCommandsAsSudo: + clearMemoryCache() + +def refreshShapers(): + tcpOverheadFactor = 1.09 + + # Load Devices + devices = [] + with open('Shaper.csv') as csv_file: + csv_reader = csv.reader(csv_file, delimiter=',') + next(csv_reader) + for row in csv_reader: + deviceID, AP, mac, hostname,ipv4, ipv6, downloadMin, uploadMin, downloadMax, uploadMax = row + ipv4 = ipv4.strip() + ipv6 = ipv6.strip() + if AP == "": + AP = "none" + AP = AP.strip() + thisDevice = { + "id": deviceID, + "mac": mac, + "AP": AP, + "hostname": hostname, + "ipv4": ipv4, + "ipv6": ipv6, + "downloadMin": round(int(downloadMin)*tcpOverheadFactor), + "uploadMin": round(int(uploadMin)*tcpOverheadFactor), + "downloadMax": round(int(downloadMax)*tcpOverheadFactor), + "uploadMax": round(int(uploadMax)*tcpOverheadFactor), + "qdisc": '', + } + devices.append(thisDevice) + + # Load Access Points + accessPoints = [] + accessPointNamesOnly = [] + with open('AccessPoints.csv') as csv_file: + csv_reader = csv.reader(csv_file, delimiter=',') + next(csv_reader) + for row in csv_reader: + APname, apDownload, apUpload, parentSite = row + accessPointNamesOnly.append(APname) + apDownload = round(int(apDownload)*tcpOverheadFactor) + apUpload = round(int(apUpload)*tcpOverheadFactor) + devicesForThisAP = [] + for device in devices: + if APname == device['AP']: + devicesForThisAP.append(device) + accessPoints.append((APname, apDownload, apUpload, parentSite, devicesForThisAP)) + + # If an AP is specified for a device in Shaper.csv, but AP is not listed in AccessPoints.csv, raise exception + for device in devices: + if (device['AP'] not in accessPointNamesOnly): + print(device['AP']) + raise ValueError('AP for device ' + device['hostname'] + ' not listed in AccessPoints.csv') + + # Load Sites + sites = [] + with open('Sites.csv') as csv_file: + csv_reader = csv.reader(csv_file, delimiter=',') + next(csv_reader) + for row in csv_reader: + siteName, download, upload = row + siteDownloadMbps = int(download) + siteUploadMbps = int(upload) + apsForThisSite = [] + for AP in accessPoints: + APname, apDownload, apUpload, parentSite, devicesForThisAP = AP + if parentSite == siteName: + apsForThisSite.append((APname, apDownload, apUpload, parentSite, devicesForThisAP)) + sites.append((siteName, siteDownloadMbps, siteUploadMbps, apsForThisSite)) + + #Clear Prior Settings + clearPriorSettings(interfaceA, interfaceB) + + # XDP-CPUMAP-TC + shell('./xdp-cpumap-tc/bin/xps_setup.sh -d ' + interfaceA + ' --default --disable') + shell('./xdp-cpumap-tc/bin/xps_setup.sh -d ' + interfaceB + ' --default --disable') + shell('./xdp-cpumap-tc/src/xdp_iphash_to_cpu --dev ' + interfaceA + ' --lan') + shell('./xdp-cpumap-tc/src/xdp_iphash_to_cpu --dev ' + interfaceB + ' --wan') + shell('./xdp-cpumap-tc/src/xdp_iphash_to_cpu_cmdline --clear') + shell('./xdp-cpumap-tc/src/tc_classify --dev-egress ' + interfaceA) + shell('./xdp-cpumap-tc/src/tc_classify --dev-egress ' + interfaceB) + + # Find queues available + queuesAvailable = 0 + path = '/sys/class/net/' + interfaceA + '/queues/' + directory_contents = os.listdir(path) + print(directory_contents) + for item in directory_contents: + if "tx-" in str(item): + queuesAvailable += 1 + + # For VMs, must reduce queues if more than 9, for some reason + if queuesAvailable > 9: + command = 'grep -q ^flags.*\ hypervisor\ /proc/cpuinfo && echo "This machine is a VM"' + try: + output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True).decode() + success = True + except subprocess.CalledProcessError as e: + output = e.output.decode() + success = False + if "This machine is a VM" in output: + queuesAvailable = 9 + + # Create MQ + thisInterface = interfaceA + shell('tc qdisc replace dev ' + thisInterface + ' root handle 7FFF: mq') + for queue in range(queuesAvailable): + shell('tc qdisc add dev ' + thisInterface + ' parent 7FFF:' + str(queue+1) + ' handle ' + str(queue+1) + ': htb default 2') + shell('tc class add dev ' + thisInterface + ' parent ' + str(queue+1) + ': classid ' + str(queue+1) + ':1 htb rate '+ str(upstreamBandwidthCapacityDownloadMbps) + 'mbit ceil ' + str(upstreamBandwidthCapacityDownloadMbps) + 'mbit') + shell('tc qdisc add dev ' + thisInterface + ' parent ' + str(queue+1) + ':1 ' + fqOrCAKE) + # Default class - traffic gets passed through this limiter with lower priority if not otherwise classified by the Shaper.csv + # Only 1/4 of defaultClassCapacity is guarenteed (to prevent hitting ceiling of upstream), for the most part it serves as an "up to" ceiling. + # Default class can use up to defaultClassCapacityDownloadMbps when that bandwidth isn't used by known hosts. + shell('tc class add dev ' + thisInterface + ' parent ' + str(queue+1) + ':1 classid ' + str(queue+1) + ':2 htb rate ' + str(defaultClassCapacityDownloadMbps/4) + 'mbit ceil ' + str(defaultClassCapacityDownloadMbps) + 'mbit prio 5') + shell('tc qdisc add dev ' + thisInterface + ' parent ' + str(queue+1) + ':2 ' + fqOrCAKE) + + thisInterface = interfaceB + shell('tc qdisc replace dev ' + thisInterface + ' root handle 7FFF: mq') + for queue in range(queuesAvailable): + shell('tc qdisc add dev ' + thisInterface + ' parent 7FFF:' + str(queue+1) + ' handle ' + str(queue+1) + ': htb default 2') + shell('tc class add dev ' + thisInterface + ' parent ' + str(queue+1) + ': classid ' + str(queue+1) + ':1 htb rate '+ str(upstreamBandwidthCapacityUploadMbps) + 'mbit ceil ' + str(upstreamBandwidthCapacityUploadMbps) + 'mbit') + shell('tc qdisc add dev ' + thisInterface + ' parent ' + str(queue+1) + ':1 ' + fqOrCAKE) + # Default class - traffic gets passed through this limiter with lower priority if not otherwise classified by the Shaper.csv. + # Only 1/4 of defaultClassCapacity is guarenteed (to prevent hitting ceiling of upstream), for the most part it serves as an "up to" ceiling. + # Default class can use up to defaultClassCapacityUploadMbps when that bandwidth isn't used by known hosts. + shell('tc class add dev ' + thisInterface + ' parent ' + str(queue+1) + ':1 classid ' + str(queue+1) + ':2 htb rate ' + str(defaultClassCapacityUploadMbps/4) + 'mbit ceil ' + str(defaultClassCapacityUploadMbps) + 'mbit prio 5') + shell('tc qdisc add dev ' + thisInterface + ' parent ' + str(queue+1) + ':2 ' + fqOrCAKE) + print() + + #If shapeBySite == True, Shape by Site, AP and Client + if shapeBySite: + currentQueueCounter = 1 + queueMinorCounterDict = {} + + # :1 and :2 are used for root and default classes, so start each counter at :3 + for queueNum in range(queuesAvailable): + queueMinorCounterDict[queueNum+1] = 3 + for site in sites: + siteName, siteDownloadMbps, siteUploadMbps, apsForThisSite = site + print("Adding site " + siteName) + major = currentQueueCounter + minor = queueMinorCounterDict[currentQueueCounter] + thisSiteclassID = str(currentQueueCounter) + ':' + str(minor) + # HTB + qdisc for each Site + # Guarentee Site gets at least 1/4 of its capacity, allow up to its max capacity when network not at peak load + shell('tc class add dev ' + interfaceA + ' parent ' + str(major) + ':1 classid ' + str(minor) + ' htb rate '+ str(round(siteDownloadMbps/4)) + 'mbit ceil '+ str(round(siteDownloadMbps)) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceA + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + shell('tc class add dev ' + interfaceB + ' parent ' + str(major) + ':1 classid ' + str(minor) + ' htb rate '+ str(round(siteUploadMbps/4)) + 'mbit ceil '+ str(round(siteUploadMbps)) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceB + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + minor += 1 + print() + for AP in apsForThisSite: + APname, apDownload, apUpload, parentSite, devicesForThisAP = AP + print("Adding AP " + APname) + # HTB + qdisc for each AP + # Guarentee AP gets at least 1/4 of its capacity, allow up to its max capacity when network not at peak load + shell('tc class add dev ' + interfaceA + ' parent ' + thisSiteclassID + ' classid ' + str(minor) + ' htb rate '+ str(round(apDownload/4)) + 'mbit ceil '+ str(round(apDownload)) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceA + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + shell('tc class add dev ' + interfaceB + ' parent ' + thisSiteclassID + ' classid ' + str(minor) + ' htb rate '+ str(round(apUpload/4)) + 'mbit ceil '+ str(round(apUpload)) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceB + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + thisAPclassID = str(currentQueueCounter) + ':' + str(minor) + minor += 1 + print() + for device in devicesForThisAP: + print("Adding device " + device['hostname']) + #HTB + qdisc for each device + shell('tc class add dev ' + interfaceA + ' parent ' + thisAPclassID + ' classid ' + str(minor) + ' htb rate '+ str(device['downloadMin']) + 'mbit ceil '+ str(device['downloadMax']) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceA + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + shell('tc class add dev ' + interfaceB + ' parent ' + thisAPclassID + ' classid ' + str(minor) + ' htb rate '+ str(device['uploadMin']) + 'mbit ceil '+ str(device['uploadMax']) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceB + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + if device['ipv4']: + parentString = str(major) + ':' + flowIDstring = str(major) + ':' + str(minor) + shell('./xdp-cpumap-tc/src/xdp_iphash_to_cpu_cmdline --add --ip ' + device['ipv4'] + ' --cpu ' + str(currentQueueCounter-1) + ' --classid ' + flowIDstring) + #Once XDP-CPUMAP-TC handles IPv6, this can be added + #if device['ipv6']: + # parentString = str(major) + ':' + # flowIDstring = str(major) + ':' + str(minor) + # shell('./xdp-cpumap-tc/src/xdp_iphash_to_cpu_cmdline --add --ip ' + device['ipv6'] + ' --cpu ' + str(currentQueueCounter-1) + ' --classid ' + flowIDstring) + device['qdisc'] = str(major) + ':' + str(minor) + minor += 1 + queueMinorCounterDict[currentQueueCounter] = minor + + currentQueueCounter += 1 + if currentQueueCounter > queuesAvailable: + currentQueueCounter = 1 + + #If shapeBySite == False, shape by AP and Client only, not by Site + else: + currentQueueCounter = 1 + queueMinorCounterDict = {} + # :1 and :2 are used for root and default classes, so start each counter at :3 + for queueNum in range(queuesAvailable): + queueMinorCounterDict[queueNum+1] = 3 + + for AP in devicesByAP: + currentAPname = AP[0]['AP'] + thisAPdownload = accessPointDownloadMbps[currentAPname] + thisAPupload = accessPointUploadMbps[currentAPname] + major = currentQueueCounter + minor = queueMinorCounterDict[currentQueueCounter] + thisAPclassID = str(currentQueueCounter) + ':' + str(minor) + # HTB + qdisc for each AP + # Guarentee AP gets at least 1/4 of its radio capacity, allow up to its max radio capacity when network not at peak load + shell('tc class add dev ' + interfaceA + ' parent ' + str(major) + ':1 classid ' + str(minor) + ' htb rate '+ str(round(thisAPdownload/4)) + 'mbit ceil '+ str(round(thisAPdownload)) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceA + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + shell('tc class add dev ' + interfaceB + ' parent ' + str(major) + ':1 classid ' + str(minor) + ' htb rate '+ str(round(thisAPupload/4)) + 'mbit ceil '+ str(round(thisAPupload)) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceB + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + minor += 1 + for device in AP: + #HTB + qdisc for each device + shell('tc class add dev ' + interfaceA + ' parent ' + thisAPclassID + ' classid ' + str(minor) + ' htb rate '+ str(device['downloadMin']) + 'mbit ceil '+ str(device['downloadMax']) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceA + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + shell('tc class add dev ' + interfaceB + ' parent ' + thisAPclassID + ' classid ' + str(minor) + ' htb rate '+ str(device['uploadMin']) + 'mbit ceil '+ str(device['uploadMax']) + 'mbit prio 3') + shell('tc qdisc add dev ' + interfaceB + ' parent ' + str(major) + ':' + str(minor) + ' ' + fqOrCAKE) + if device['ipv4']: + parentString = str(major) + ':' + flowIDstring = str(major) + ':' + str(minor) + shell('./xdp-cpumap-tc/src/xdp_iphash_to_cpu_cmdline --add --ip ' + device['ipv4'] + ' --cpu ' + str(currentQueueCounter-1) + ' --classid ' + flowIDstring) + #Once XDP-CPUMAP-TC handles IPv6, this can be added + #if device['ipv6']: + # parentString = str(major) + ':' + # flowIDstring = str(major) + ':' + str(minor) + # shell('./xdp-cpumap-tc/src/xdp_iphash_to_cpu_cmdline --add --ip ' + device['ipv6'] + ' --cpu ' + str(currentQueueCounter-1) + ' --classid ' + flowIDstring) + device['qdisc'] = str(major) + ':' + str(minor) + minor += 1 + queueMinorCounterDict[currentQueueCounter] = minor + + currentQueueCounter += 1 + if currentQueueCounter > queuesAvailable: + currentQueueCounter = 1 + + # Save devices to file to allow for statistics runs + with open('devices.json', 'w') as outfile: + json.dump(devices, outfile) + + # Done + currentTimeString = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + print("Successful run completed on " + currentTimeString) + +if __name__ == '__main__': + refreshShapers() + print("Program complete") diff --git a/v1.0/Shaper.csv b/v1.0/Shaper.csv new file mode 100644 index 00000000..b04fd54d --- /dev/null +++ b/v1.0/Shaper.csv @@ -0,0 +1,6 @@ +ID,AP,MAC,Hostname,IPv4,IPv6,Download Min,Upload Min,Download Max,Upload Max +3001,AP1,32:3B:FE:B0:92:C1,CPE-Customer1,100.126.0.77,2001:495:1f0f:58a::4/64,25,8,115,18 +3002,AP2,AE:EC:D3:70:DD:36,CPE-Customer2,100.126.0.78,2001:495:1f0f:58a::8/64,25,8,115,18 +3003,AP3,1C:1E:60:69:88:9A,CPE-Customer3,100.126.0.79,2001:495:1f0f:58a::12/64,25,8,115,18 +3004,AP4,11:B1:63:C4:DA:4C,CPE-Customer4,100.126.0.80,2001:495:1f0f:58a::16/64,25,8,115,18 +3005,AP5,46:2F:B5:C2:0B:15,CPE-Customer5,100.126.0.81,2001:495:1f0f:58a::20/64,25,8,115,18 diff --git a/v1.0/Sites.csv b/v1.0/Sites.csv new file mode 100644 index 00000000..63db046d --- /dev/null +++ b/v1.0/Sites.csv @@ -0,0 +1,5 @@ +Site,Max Download,Max Upload +Site1,920,920 +Site2,920,920 +Site3,200,30 +Site4,100,15 diff --git a/v1.0/ispConfig.py b/v1.0/ispConfig.py new file mode 100644 index 00000000..ab5cb147 --- /dev/null +++ b/v1.0/ispConfig.py @@ -0,0 +1,36 @@ +#'fq_codel' or 'cake diffserv4' +#'cake diffserv4' is recommended + +#fqOrCAKE = 'fq_codel' +fqOrCAKE = 'cake diffserv4' + +# How many Mbps are available to the edge of this network +upstreamBandwidthCapacityDownloadMbps = 1000 +upstreamBandwidthCapacityUploadMbps = 1000 + +# Traffic from devices not specified in Shaper.csv will be rate limited by an HTB of this many Mbps +defaultClassCapacityDownloadMbps = 500 +defaultClassCapacityUploadMbps = 500 + +# Interface connected to core router +interfaceA = 'eth1' + +# Interface connected to edge router +interfaceB = 'eth2' + +# Shape by Site in addition to by AP and Client +shapeBySite = True + +# Allow shell commands. False causes commands print to console only without being executed. MUST BE ENABLED FOR PROGRAM TO FUNCTION +enableActualShellCommands = True + +# Add 'sudo' before execution of any shell commands. May be required depending on distribution and environment. +runShellCommandsAsSudo = False + +# Optional UISP integration +# Everything before /nms/ on your UISP instance +UISPbaseURL = 'https://examplesite.com' +# UISP Auth Token +uispAuthToken = '' +# Whether to shape router at customer premises, or instead shape the station radio. When station radio is in router mode, use 'station'. Otherwise, use 'router'. +shapeRouterOrStation = 'router' diff --git a/v1.0/scheduled.py b/v1.0/scheduled.py new file mode 100644 index 00000000..99dfb6a1 --- /dev/null +++ b/v1.0/scheduled.py @@ -0,0 +1,11 @@ +import time +import schedule +from datetime import date +from LibreQoS import refreshShapers + +if __name__ == '__main__': + refreshShapers() + schedule.every().day.at("04:00").do(refreshShapers) + while True: + schedule.run_pending() + time.sleep(60) # wait one minute