From 887d2e5a8a257b91973826883c442ec656b204af Mon Sep 17 00:00:00 2001 From: DM Date: Sat, 1 Jul 2023 23:48:34 +0100 Subject: [PATCH] Pushing existing code --- README.md | 22 +++++++++ ipam | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ipam.json | 21 +++++++++ 3 files changed, 173 insertions(+) create mode 100755 ipam create mode 100644 ipam.json diff --git a/README.md b/README.md index 196d638..81b1ffb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ # IPAMLite +## Command Line Operation +``` + IP Address Manager (Lite) + Example usage: + $ cd IPAMLite + $ chmod +x ipam + $ ./ipam + + $ curl -X POST -H "Content-type: application/json" -d "{\"ip\" : \"192.168.1.5\", \"subnet\" : \"24\"}" "127.0.0.1:5000/specific_lease" + $ curl -X POST -H "Content-type: application/json" -d "{\"network\" : \"192.168.1.0\", \"subnet\" : \"24\"}" "127.0.0.1:5000/sequential_lease" + $ curl -X POST -H "Content-type: application/json" -d "{\"ip\" : \"192.168.1.5\", \"subnet\" : \"24\"}" "127.0.0.1:5000/release" + $ curl 127.0.0.1:5000/database + + Currently supported API calls: + - /specific_lease - HTTP POST, adds a specified IP address to the database + - Takes: ip, subnet + - /sequential_lease - HTTP POST, adds the next available IP address in the subnet to the database + - Takes: network, subnet + - /release - HTTP POST, release an IP address from the database + - Takes: ip, subnet + - /database - HTTP GET, returns the IP address database +``` \ No newline at end of file diff --git a/ipam b/ipam new file mode 100755 index 0000000..97d13cb --- /dev/null +++ b/ipam @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 + +import ipaddress +import json +import os + +from flask import Flask, request + +class IPAMLite(object): + def __init__(self, database_file='ipam.json'): + self.database_file = database_file + self.leased = [] + + def database(self): + return(json.dumps(self.leased, indent=4)) + + def sequential_lease(self, network, subnet): + network_exists = False + host_addresses = [str(ip) for ip in ipaddress.IPv4Network(f'{network}/{subnet}').hosts()] + + for network_dict in self.leased: + if f'{network}/{subnet}' in network_dict.keys(): + for network_address, leased_addresses in network_dict.items(): + leaseable_addresses = [ip for ip in host_addresses if ip not in leased_addresses] + if len(leaseable_addresses) >= 1: + next_available_lease = leaseable_addresses[0] + leased_addresses.append(next_available_lease) + network_exists = True + break + else: + return f'\U0001F916 No address space left in: {network}/{subnet}\n' + + if not network_exists: + next_available_lease = host_addresses[0] + self.leased.append({str(f'{network}/{subnet}'): [next_available_lease]}) + + self.save_database() + return f'\U0001F916 Lease added: {next_available_lease}/{subnet}\n' + + def specific_lease(self, ip, subnet): + network_exists = False + network_address = ipaddress.ip_interface(f'{ip}/{subnet}').network + + for network_dict in self.leased: + for network, leased_addresses in network_dict.items(): + if ip in leased_addresses: + return f'\U0001F916 Lease already exists: {ip}/{subnet}\n' + if str(network) == str(network_address): + leased_addresses.append(ip) + network_exists = True + + if not network_exists: + self.leased.append({str(network_address): [ip]}) + + self.save_database() + return f'\U0001F916 Lease added: {ip}/{subnet}\n' + + def release(self, ip, subnet): + for index in range(len(self.leased)): + for network, leased_addresses in self.leased[index].items(): + if ip in leased_addresses and len(leased_addresses) < 2: + del self.leased[index] + self.save_database() + return f'\U0001F916 Lease removed: {ip}/{subnet}\n' + break + elif ip in leased_addresses and len(leased_addresses) > 1: + leased_addresses.remove(ip) + self.save_database() + return f'\U0001F916 Lease removed: {ip}/{subnet}\n' + break + + return f'\U0001F916 Nothing to release for: {ip}/{subnet}\n' + + def save_database(self): + with open(self.database_file, 'w') as ipam_file: + json.dump(self.leased, ipam_file, indent=4) + + def load_database(self): + if os.path.exists(self.database_file): + with open(self.database_file, 'r') as ipam_file: + db = json.load(ipam_file) + + for lease in db: + self.leased.append(lease) + + print(f'\U0001F916 Database file "{self.database_file}" loaded successfully') + else: + print(f'\U0001F916 Cannot load database file "{self.database_file}"') + +IPAM = IPAMLite() +IPAM.load_database() + +app = Flask(__name__) + +@app.route('/database', methods=['GET']) +def get_database(): + return IPAM.database() + +@app.route('/sequential_lease', methods=['POST']) +def handle_sequential_lease(): + if request.is_json: + data = request.json + lease_response = IPAM.sequential_lease(data['network'], data['subnet']) + + return f'{lease_response}' + else: + return 'Data type not supported, supported data types: JSON\n' + +@app.route('/specific_lease', methods=['POST']) +def handle_specific_lease(): + if request.is_json: + data = request.json + lease_response = IPAM.specific_lease(data['ip'], data['subnet']) + + return f'{lease_response}' + else: + return 'Data type not supported, supported data types: JSON\n' + +@app.route('/release', methods=['POST']) +def handle_release(): + if request.is_json: + data = request.json + release_response = IPAM.release(data['ip'], data['subnet']) + + return f'{release_response}' + else: + return 'Data type not supported, supported data types: JSON\n' + +if __name__ == '__main__': + app.run(debug = True) \ No newline at end of file diff --git a/ipam.json b/ipam.json new file mode 100644 index 0000000..bef023c --- /dev/null +++ b/ipam.json @@ -0,0 +1,21 @@ +[ + { + "10.0.0.0/24": [ + "10.0.0.1", + "10.0.0.2", + "10.0.0.3" + ] + }, + { + "192.168.1.0/24": [ + "192.168.1.1", + "192.168.1.4" + ] + }, + { + "10.0.0.0/8": [ + "10.0.0.1", + "10.0.0.2" + ] + } +] \ No newline at end of file