diff options
-rw-r--r-- | tools/dhcpns/config/dhcp4.py | 34 | ||||
-rw-r--r-- | tools/dhcpns/main.py | 50 | ||||
-rw-r--r-- | tools/dhcpns/pdns.py | 4 | ||||
-rw-r--r-- | tools/dhcpns/pyproject.toml | 2 | ||||
-rw-r--r-- | tools/dhcpns/tests/__init__.py | 0 |
5 files changed, 62 insertions, 28 deletions
diff --git a/tools/dhcpns/config/dhcp4.py b/tools/dhcpns/config/dhcp4.py index a70365d..b0917dd 100644 --- a/tools/dhcpns/config/dhcp4.py +++ b/tools/dhcpns/config/dhcp4.py @@ -1,5 +1,6 @@ import os import ipaddress +import math def base(subnet4): @@ -118,7 +119,7 @@ def base(subnet4): "client-classes": [ { "name": "client-juniper-vendor", - "test": "substring(option[vendor-class-identifier].hex,0,10) == 'Juniper-ex'" + "test": "substring(option[vendor-class-identifier].hex,0,7) == 'Juniper'" }, { "name": "client-juniper-mac", @@ -198,7 +199,23 @@ def base(subnet4): "boot-file-name": "netboot.xyz-undionly.kpxe" } ], - "subnet4": subnet4 + "subnet4": subnet4, + "loggers": [ + { + "name": "kea-dhcp4", + "output_options": [ + { + "output": "/var/log/kea/debug.log", + "maxver": 8, + "maxsize": 204800, + "flush": True, + "pattern": "%d{%j %H:%M:%S.%q} %c %m\n" + } + ], + "severity": "DEBUG", + "debuglevel": 40 + } + ] } @@ -235,9 +252,10 @@ def subnet(vlan, prefix, domain_name, vlan_domain_name): } -def fap(vlan, prefix, domain_name, vlan_domain_name): +def fap(vlan, prefix): network = ipaddress.ip_network(prefix.prefix) - gw, start_ip, end_ip = network[1], network[len(network) / 2], network[-2] + gw, start_ip, end_ip = network[1], network[( + math.ceil(network.num_addresses / 2))], network[-2] return { "id": prefix.id, "client-class": "fap-class", @@ -251,14 +269,6 @@ def fap(vlan, prefix, domain_name, vlan_domain_name): { "name": "routers", "data": f"{gw}" - }, - { - "name": "domain-name", - "data": f"{vlan_domain_name}, {domain_name}" - }, - { - "name": "domain-search", - "data": f"{vlan_domain_name}, {domain_name}" } ], "user-context": { diff --git a/tools/dhcpns/main.py b/tools/dhcpns/main.py index ef6eb25..32087e0 100644 --- a/tools/dhcpns/main.py +++ b/tools/dhcpns/main.py @@ -3,22 +3,24 @@ import os from dotenv import load_dotenv import json from pdns import PowerDNS +import ipaddress +import subprocess from config.dhcp4 import base as dhcp4 from config.dhcp6 import base as dhcp6 +from config.dhcp4 import fap from config.ddns import base as ddns from config.ddns import ddns_domain from config.dhcp4 import subnet as subnet4 from config.dhcp6 import subnet as subnet6 -import ipaddress # Take environment variables from .env load_dotenv() DOMAIN_NAME = os.environ['DOMAIN_NAME'] DOMAIN_SEARCH = os.environ['DOMAIN_SEARCH'] -NAMESERVERS = os.environ['NAMESERVERS'].strip().split(',') +NAMESERVERS = os.environ['NAMESERVERS'].split() nb = pynetbox.api( os.getenv('NETBOX_URL'), @@ -30,7 +32,7 @@ nb = pynetbox.api( pdns = PowerDNS(os.environ['PDNS_API_URL'], os.environ['PDNS_API_KEY']) # Load all zones to later check if a zone already exist -zones = [ zone['name'] for zone in pdns.list_zones() ] +zones = [zone['name'] for zone in pdns.list_zones()] rdns_zones = pdns.search("*.arpa", 2000, "zone") @@ -39,7 +41,8 @@ kea6_subnets = [] kea_ddns_domains = [] kea_rddns_domains = [] -vlans = nb.ipam.vlans.filter(tag='dhcp') +# dhcp-client +vlans = nb.ipam.vlans.filter(tag='dhcp-client') for vlan in vlans: vlan_domain_name = f"{vlan.name}.{DOMAIN_NAME}" prefixes4 = [] @@ -49,17 +52,17 @@ for vlan in vlans: for prefix in nb.ipam.prefixes.filter(vlan_id=vlan.id, family=4): kea4_subnets.append( subnet4(vlan, prefix, DOMAIN_NAME, vlan_domain_name)) - #prefixes4.append(prefix) + prefixes4.append(prefix) for prefix in nb.ipam.prefixes.filter(vlan_id=vlan.id, family=6): kea6_subnets.append( subnet6(vlan, prefix, DOMAIN_NAME, vlan_domain_name)) - #prefixes6.append(prefix) + prefixes6.append(prefix) if vlan_domain_name not in zones and len(prefixes4) >= 1: - pdns.create_zone(vlan_domain_name, NAMESERVERS) - pdns.create_zone_metadata( - vlan_domain_name, 'TSIG-ALLOW-DNSUPDATE', 'dhcp_updater') + print(pdns.create_zone(f"{vlan_domain_name}.", NAMESERVERS)) + print(pdns.create_zone_metadata( + f"{vlan_domain_name}.", 'TSIG-ALLOW-DNSUPDATE', 'dhcpns')) zone_rrsets = [] @@ -95,6 +98,14 @@ for vlan in vlans: rdns_rrsets.append({"name": network[-1].reverse_pointer + '.', "changetype": "replace", "type": "PTR", "records": [ {"content": f'broadcast-{network[-1]}.{vlan_domain_name}', "disabled": False, "type": "PTR"}], "ttl": 900}) +# dhcp-mgmt-edge +vlans = nb.ipam.vlans.filter(tag='dhcp-mgmt-edge') +for vlan in vlans: + prefixes4 = [] + for prefix in nb.ipam.prefixes.filter(vlan_id=vlan.id, family=4): + kea4_subnets.append( + fap(vlan, prefix)) + for zone in rdns_zones: kea_rddns_domains.append(ddns_domain(zone['name'][:-1])) @@ -102,14 +113,27 @@ for zone in rdns_zones: # Write DDNS if os.environ['KEA_DDNS_FILE'] is not None: with open(os.environ['KEA_DDNS_FILE'], "w") as outfile: - outfile.write(json.dumps({"DhcpDdns": ddns(kea_ddns_domains, kea_rddns_domains)}, indent=2)) + outfile.write(json.dumps( + {"DhcpDdns": ddns(kea_ddns_domains, kea_rddns_domains)}, indent=2)) # Write DHCPv4 if os.environ['KEA_DHCP4_FILE'] is not None: with open(os.environ['KEA_DHCP4_FILE'], "w") as outfile: outfile.write(json.dumps({"Dhcp4": dhcp4(kea4_subnets)}, indent=2)) - -# Write DHCPv4 + +# Write DHCPv6 if os.environ['KEA_DHCP6_FILE'] is not None: with open(os.environ['KEA_DHCP6_FILE'], "w") as outfile: - outfile.write(json.dumps({"Dhcp6": dhcp4(kea6_subnets)}, indent=2)) + outfile.write(json.dumps({"Dhcp6": dhcp6(kea6_subnets)}, indent=2)) + +# Test DHCPv4 +try: + subprocess.check_call(['/usr/sbin/kea-dhcp4', '-t', os.environ['KEA_DHCP4_FILE']]) +except subprocess.CalledProcessError: + print("Failed to validate kea-dhcp4 config. What do we do now?") + +# Test DHCPv6 +try: + subprocess.check_call(['/usr/sbin/kea-dhcp6', '-t', os.environ['KEA_DHCP6_FILE']]) +except subprocess.CalledProcessError: + print("Failed to validate kea-dhcp6 config. What do we do now?")
\ No newline at end of file diff --git a/tools/dhcpns/pdns.py b/tools/dhcpns/pdns.py index 296a151..0e3b3ab 100644 --- a/tools/dhcpns/pdns.py +++ b/tools/dhcpns/pdns.py @@ -26,13 +26,13 @@ class PowerDNS: request = requests.post( self.base_url + uri, headers=headers, - data=kwargs + json=kwargs ) elif method == "PUT": request = requests.put( self.base_url + uri, headers=headers, - data=kwargs + json=kwargs ) elif method == "PATCH": request = requests.patch( diff --git a/tools/dhcpns/pyproject.toml b/tools/dhcpns/pyproject.toml index 5219168..c0e5fdf 100644 --- a/tools/dhcpns/pyproject.toml +++ b/tools/dhcpns/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Ole Mathias Heggem <olemathias.aa.heggem@gmail.com>"] readme = "README.md" [tool.poetry.dependencies] -python = "^3.11" +python = "^3.9" requests = "^2.28.2" pynetbox = "^7.0.1" python-dotenv = "^1.0.0" diff --git a/tools/dhcpns/tests/__init__.py b/tools/dhcpns/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/dhcpns/tests/__init__.py |