diff --git a/v1.1/lqAPI.py b/v1.1/lqAPI.py new file mode 100644 index 00000000..653f7c44 --- /dev/null +++ b/v1.1/lqAPI.py @@ -0,0 +1,356 @@ +from flask import Flask +from flask_restful import Resource, Api, reqparse +from flask_httpauth import HTTPBasicAuth +import ast +import csv +from werkzeug.security import generate_password_hash, check_password_hash +from ispConfig import apiUsername, apiPassword, apiHostIP, apiHostPost +from LibreQoS import refreshShapers + +app = Flask(__name__) +api = Api(app) +auth = HTTPBasicAuth() + +users = { + apiUsername: generate_password_hash(apiPassword) +} + +@auth.verify_password +def verify_password(username, password): + if username in users and check_password_hash(users.get(username), password): + return username + +class Devices(Resource): + # Get + @auth.login_required + def get(self): + devices = [] + with open('Shaper.csv') as csv_file: + csv_reader = csv.reader(csv_file, delimiter=',') + header_store = next(csv_reader) + for row in csv_reader: + deviceID, parentNode, mac, hostname,ipv4, ipv6, downloadMin, uploadMin, downloadMax, uploadMax = row + ipv4 = ipv4.strip() + ipv6 = ipv6.strip() + if parentNode == "": + parentNode = "none" + parentNode = parentNode.strip() + thisDevice = { + "id": deviceID, + "mac": mac, + "parentNode": parentNode, + "hostname": hostname, + "ipv4": ipv4, + "ipv6": ipv6, + "downloadMin": int(downloadMin), + "uploadMin": int(uploadMin), + "downloadMax": int(downloadMax), + "uploadMax": int(uploadMax), + "qdisc": '', + } + devices.append(thisDevice) + return {'data': devices}, 200 # return data and 200 OK code + + # Post + @auth.login_required + def post(self): + devices = [] + idOnlyList = [] + ipv4onlyList = [] + ipv6onlyList = [] + hostnameOnlyList = [] + with open('Shaper.csv') as csv_file: + csv_reader = csv.reader(csv_file, delimiter=',') + header_store = next(csv_reader) + for row in csv_reader: + deviceID, parentNode, mac, hostname,ipv4, ipv6, downloadMin, uploadMin, downloadMax, uploadMax = row + ipv4 = ipv4.strip() + ipv6 = ipv6.strip() + if parentNode == "": + parentNode = "none" + parentNode = parentNode.strip() + thisDevice = { + "id": deviceID, + "mac": mac, + "parentNode": parentNode, + "hostname": hostname, + "ipv4": ipv4, + "ipv6": ipv6, + "downloadMin": int(downloadMin), + "uploadMin": int(uploadMin), + "downloadMax": int(downloadMax), + "uploadMax": int(uploadMax), + "qdisc": '', + } + devices.append(thisDevice) + ipv4onlyList.append(ipv4) + ipv6onlyList.append(ipv6) + idOnlyList.append(deviceID) + hostnameOnlyList.append(hostname) + + parser = reqparse.RequestParser() # initialize + + parser.add_argument('id', required=False) + parser.add_argument('mac', required=False) + parser.add_argument('parentNode', required=False) + parser.add_argument('hostname', required=False) + parser.add_argument('ipv4', required=False) + parser.add_argument('ipv6', required=False) + parser.add_argument('downloadMin', required=True) + parser.add_argument('uploadMin', required=True) + parser.add_argument('downloadMax', required=True) + parser.add_argument('uploadMax', required=True) + parser.add_argument('qdisc', required=False) + + args = parser.parse_args() # parse arguments to dictionary + + args['downloadMin'] = int(args['downloadMin']) + args['uploadMin'] = int(args['uploadMin']) + args['downloadMax'] = int(args['downloadMax']) + args['uploadMax'] = int(args['uploadMax']) + + if (args['id'] in idOnlyList): + return { + 'message': f"'{args['id']}' already exists." + }, 401 + elif (args['ipv4'] in ipv4onlyList): + return { + 'message': f"'{args['ipv4']}' already exists." + }, 401 + elif (args['ipv6'] in ipv6onlyList): + return { + 'message': f"'{args['ipv6']}' already exists." + }, 401 + elif (args['hostname'] in hostnameOnlyList): + return { + 'message': f"'{args['hostname']}' already exists." + }, 401 + else: + if args['parentNode'] == None: + args['parentNode'] = "none" + + newDevice = { + "id": args['id'], + "mac": args['mac'], + "parentNode": args['parentNode'], + "hostname": args['hostname'], + "ipv4": args['ipv4'], + "ipv6": args['ipv6'], + "downloadMin": int(args['downloadMin']), + "uploadMin": int(args['uploadMin']), + "downloadMax": int(args['downloadMax']), + "uploadMax": int(args['uploadMax']), + "qdisc": '', + } + + entryExistsAlready = False + revisedDevices = [] + revisedDevices.append(newDevice) + + # create new Shaper.csv containing new values + with open('Shaper.csv', 'w') as csvfile: + wr = csv.writer(csvfile, quoting=csv.QUOTE_ALL) + wr.writerow(header_store) + for device in revisedDevices: + wr.writerow((device['id'], device['parentNode'], device['mac'], device['hostname'] , device['ipv4'], device['ipv6'], device['downloadMin'], device['uploadMin'], device['downloadMax'], device['uploadMax'])) + + return {'data': newDevice}, 200 # return data with 200 OK + + # Put + @auth.login_required + def put(self): + devices = [] + idOnlyList = [] + ipv4onlyList = [] + ipv6onlyList = [] + hostnameOnlyList = [] + with open('Shaper.csv') as csv_file: + csv_reader = csv.reader(csv_file, delimiter=',') + header_store = next(csv_reader) + for row in csv_reader: + deviceID, parentNode, mac, hostname,ipv4, ipv6, downloadMin, uploadMin, downloadMax, uploadMax = row + ipv4 = ipv4.strip() + ipv6 = ipv6.strip() + if parentNode == "": + parentNode = "none" + parentNode = parentNode.strip() + thisDevice = { + "id": deviceID, + "mac": mac, + "parentNode": parentNode, + "hostname": hostname, + "ipv4": ipv4, + "ipv6": ipv6, + "downloadMin": int(downloadMin), + "uploadMin": int(uploadMin), + "downloadMax": int(downloadMax), + "uploadMax": int(uploadMax), + "qdisc": '', + } + devices.append(thisDevice) + ipv4onlyList.append(ipv4) + ipv6onlyList.append(ipv6) + idOnlyList.append(deviceID) + hostnameOnlyList.append(hostname) + + parser = reqparse.RequestParser() # initialize + + parser.add_argument('id', required=False) + parser.add_argument('mac', required=False) + parser.add_argument('parentNode', required=False) + parser.add_argument('hostname', required=False) + parser.add_argument('ipv4', required=False) + parser.add_argument('ipv6', required=False) + parser.add_argument('downloadMin', required=True) + parser.add_argument('uploadMin', required=True) + parser.add_argument('downloadMax', required=True) + parser.add_argument('uploadMax', required=True) + parser.add_argument('qdisc', required=False) + + args = parser.parse_args() # parse arguments to dictionary + + args['downloadMin'] = int(args['downloadMin']) + args['uploadMin'] = int(args['uploadMin']) + args['downloadMax'] = int(args['downloadMax']) + args['uploadMax'] = int(args['uploadMax']) + + if (args['id'] in idOnlyList) or (args['ipv4'] in ipv4onlyList) or (args['ipv6'] in ipv6onlyList) or (args['hostname'] in hostnameOnlyList): + + if args['parentNode'] == None: + args['parentNode'] = "none" + + newDevice = { + "id": args['id'], + "mac": args['mac'], + "parentNode": args['parentNode'], + "hostname": args['hostname'], + "ipv4": args['ipv4'], + "ipv6": args['ipv6'], + "downloadMin": int(args['downloadMin']), + "uploadMin": int(args['uploadMin']), + "downloadMax": int(args['downloadMax']), + "uploadMax": int(args['uploadMax']), + "qdisc": '', + } + + successfullyFoundMatch = False + revisedDevices = [] + for device in devices: + if (device['id'] == args['id']) or (device['mac'] == args['mac']) or (device['hostname'] == args['hostname']) or (device['ipv4'] == args['ipv4']) or (device['ipv6'] == args['ipv6']): + revisedDevices.append(newDevice) + successfullyFoundMatch = True + else: + revisedDevices.append(device) + + # create new Shaper.csv containing new values + with open('Shaper.csv', 'w') as csvfile: + wr = csv.writer(csvfile, quoting=csv.QUOTE_ALL) + wr.writerow(header_store) + for device in revisedDevices: + wr.writerow((device['id'], device['parentNode'], device['mac'], device['hostname'] , device['ipv4'], device['ipv6'], device['downloadMin'], device['uploadMin'], device['downloadMax'], device['uploadMax'])) + + return {'data': newDevice}, 200 # return data with 200 OK + else: + return { + 'message': f" Matching device entry not found." + }, 404 + + # Delete + @auth.login_required + def delete(self): + devices = [] + idOnlyList = [] + ipv4onlyList = [] + ipv6onlyList = [] + hostnameOnlyList = [] + with open('Shaper.csv') as csv_file: + csv_reader = csv.reader(csv_file, delimiter=',') + header_store = next(csv_reader) + for row in csv_reader: + deviceID, parentNode, mac, hostname,ipv4, ipv6, downloadMin, uploadMin, downloadMax, uploadMax = row + ipv4 = ipv4.strip() + ipv6 = ipv6.strip() + if parentNode == "": + parentNode = "none" + parentNode = parentNode.strip() + thisDevice = { + "id": deviceID, + "mac": mac, + "parentNode": parentNode, + "hostname": hostname, + "ipv4": ipv4, + "ipv6": ipv6, + "downloadMin": int(downloadMin), + "uploadMin": int(uploadMin), + "downloadMax": int(downloadMax), + "uploadMax": int(uploadMax), + "qdisc": '', + } + devices.append(thisDevice) + ipv4onlyList.append(ipv4) + ipv6onlyList.append(ipv6) + idOnlyList.append(deviceID) + hostnameOnlyList.append(hostname) + + parser = reqparse.RequestParser() # initialize + + parser.add_argument('id', required=False) + parser.add_argument('mac', required=False) + parser.add_argument('parentNode', required=False) + parser.add_argument('hostname', required=False) + parser.add_argument('ipv4', required=False) + parser.add_argument('ipv6', required=False) + parser.add_argument('downloadMin', required=False) + parser.add_argument('uploadMin', required=False) + parser.add_argument('downloadMax', required=False) + parser.add_argument('uploadMax', required=False) + parser.add_argument('qdisc', required=False) + + args = parser.parse_args() # parse arguments to dictionary + + if (args['id'] in idOnlyList) or (args['ipv4'] in ipv4onlyList) or (args['ipv6'] in ipv6onlyList) or (args['hostname'] in hostnameOnlyList): + + successfullyFoundMatch = False + revisedDevices = [] + for device in devices: + if (device['id'] == args['id']) or (device['mac'] == args['mac']) or (device['hostname'] == args['hostname']) or (device['ipv4'] == args['ipv4']) or (device['ipv6'] == args['ipv6']): + # Simply do not add device to revisedDevices + successfullyFoundMatch = True + else: + revisedDevices.append(device) + + # create new Shaper.csv containing new values + with open('Shaper.csv', 'w') as csvfile: + wr = csv.writer(csvfile, quoting=csv.QUOTE_ALL) + wr.writerow(header_store) + for device in revisedDevices: + wr.writerow((device['id'], device['parentNode'], device['mac'], device['hostname'] , device['ipv4'], device['ipv6'], device['downloadMin'], device['uploadMin'], device['downloadMax'], device['uploadMax'])) + + return { + 'message': "Matching device entry successfully deleted." + }, 200 # return data with 200 OK + else: + return { + 'message': f" Matching device entry not found." + }, 404 + +class Shaper(Resource): + # Post + @auth.login_required + def post(self): + parser = reqparse.RequestParser() # initialize + parser.add_argument('refresh', required=True) + args = parser.parse_args() # parse arguments to dictionary + if (args['refresh'] == True): + refreshShapers() + return { + 'message': "Successfully refreshed LibreQoS device shaping." + }, 200 # return data and 200 OK code + +api.add_resource(Devices, '/devices') # '/devices' is our 1st entry point +api.add_resource(Shaper, '/shaper') # '/shaper' is our 2nd entry point + +if __name__ == '__main__': + from waitress import serve + #app.run(debug=True) # debug mode + serve(app, host=apiHostIP, port=apiHostPost)