From c87e06d000f85da85b2977b66a6924ba6348768d Mon Sep 17 00:00:00 2001 From: Ole Mathias Heggem Date: Sun, 2 Apr 2023 22:59:31 +0200 Subject: feat: add more stuff --- tools/dhcpns/.gitignore | 3 ++ tools/dhcpns/main.py | 87 +++++++++++++++++++++++++++++++++++++++------ tools/dhcpns/poetry.lock | 16 +++++++-- tools/dhcpns/pyproject.toml | 1 + 4 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 tools/dhcpns/.gitignore diff --git a/tools/dhcpns/.gitignore b/tools/dhcpns/.gitignore new file mode 100644 index 0000000..ba7663d --- /dev/null +++ b/tools/dhcpns/.gitignore @@ -0,0 +1,3 @@ +dhcp4.conf +dhcp6.conf +ddns.conf \ No newline at end of file diff --git a/tools/dhcpns/main.py b/tools/dhcpns/main.py index 32087e0..b81042f 100644 --- a/tools/dhcpns/main.py +++ b/tools/dhcpns/main.py @@ -5,6 +5,8 @@ import json from pdns import PowerDNS import ipaddress import subprocess +import re +import netaddr from config.dhcp4 import base as dhcp4 from config.dhcp6 import base as dhcp6 @@ -59,7 +61,7 @@ for vlan in vlans: subnet6(vlan, prefix, DOMAIN_NAME, vlan_domain_name)) prefixes6.append(prefix) - if vlan_domain_name not in zones and len(prefixes4) >= 1: + if f"{vlan_domain_name}." not in zones and len(prefixes4) >= 1: print(pdns.create_zone(f"{vlan_domain_name}.", NAMESERVERS)) print(pdns.create_zone_metadata( f"{vlan_domain_name}.", 'TSIG-ALLOW-DNSUPDATE', 'dhcpns')) @@ -70,17 +72,20 @@ for vlan in vlans: network = ipaddress.ip_network(prefix) # Network ID - zone_rrsets.append({'name': f'net-{network[0]}.{vlan_domain_name}', 'changetype': 'replace', 'type': 'A', 'records': [ + zone_rrsets.append({'name': f'net-{network[0]}.{vlan_domain_name}.', 'changetype': 'replace', 'type': 'A', 'records': [ {'content': str(network[0]), 'disabled': False, 'type':'A'}], 'ttl': 900}) # Gateway - zone_rrsets.append({'name': f'gw-{network[1]}.{vlan_domain_name}', 'changetype': 'replace', 'type': 'A', 'records': [ + zone_rrsets.append({'name': f'gw-{network[1]}.{vlan_domain_name}.', 'changetype': 'replace', 'type': 'A', 'records': [ {'content': str(network[1]), 'disabled': False, 'type':'A'}], 'ttl': 900}) # Broadcast - zone_rrsets.append({'name': f'broadcast-{network[-1]}.{vlan_domain_name}', 'changetype': 'replace', 'type': 'A', 'records': [ + zone_rrsets.append({'name': f'broadcast-{network[-1]}.{vlan_domain_name}.', 'changetype': 'replace', 'type': 'A', 'records': [ {'content': str(network[-1]), 'disabled': False, 'type':'A'}], 'ttl': 900}) + # Apply zone_rrsets + pdns.set_records(vlan_domain_name, zone_rrsets) + rdns_zone = pdns.get_rdns_zone_from_ip(network[0]) rdns_rrsets = [] if rdns_zone is None: @@ -88,15 +93,18 @@ for vlan in vlans: # Network ID rdns_rrsets.append({"name": network[0].reverse_pointer + '.', "changetype": "replace", "type": "PTR", "records": [ - {"content": f'net-{network[0]}.{vlan_domain_name}', "disabled": False, "type": "PTR"}], "ttl": 900}) + {"content": f'net-{network[0]}.{vlan_domain_name}.', "disabled": False, "type": "PTR"}], "ttl": 900}) # Gateway rdns_rrsets.append({"name": network[1].reverse_pointer + '.', "changetype": "replace", "type": "PTR", "records": [ - {"content": f'gw-{network[1]}.{vlan_domain_name}', "disabled": False, "type": "PTR"}], "ttl": 900}) + {"content": f'gw-{network[1]}.{vlan_domain_name}.', "disabled": False, "type": "PTR"}], "ttl": 900}) # Broadcast 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}) + {"content": f'broadcast-{network[-1]}.{vlan_domain_name}.', "disabled": False, "type": "PTR"}], "ttl": 900}) + + # Apply rdns_rrsets + pdns.set_records(network[1].reverse_pointer + '.', rdns_rrsets) # dhcp-mgmt-edge vlans = nb.ipam.vlans.filter(tag='dhcp-mgmt-edge') @@ -128,12 +136,69 @@ if os.environ['KEA_DHCP6_FILE'] is not None: # Test DHCPv4 try: - subprocess.check_call(['/usr/sbin/kea-dhcp4', '-t', os.environ['KEA_DHCP4_FILE']]) + 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']]) + 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 + print("Failed to validate kea-dhcp6 config. What do we do now?") + + +# Reload all zones +zones = [zone['name'] for zone in pdns.list_zones()] + +# Create DNS for devices +devices = nb.dcim.devices.all() +for device in devices: + if device.primary_ip4 is None or device.primary_ip6 is None: + continue + + zone = "tg23.gathering.org" + + print(device.name) + r = re.search('^([A-Za-z1-9]*)\.([A-Za-z1-9]*)$', device.name) + + if f"{device.name}.{zone}." in zones: + device_name = "" + zone = f"{device.name}.{zone}" + elif r is not None: + device_name = r.group(1) + "." + zone = "{}.{}".format(r.group(2), zone) + elif re.search('^([A-Za-z1-9]*) \(([A-Za-z1-9 -\/]*)\)', device.name) is not None: + zone = "{}".format(zone) + device_name = re.search( + '^([A-Za-z1-9]*) \(([A-Za-z1-9 -\/]*)\)', device.name).group(1) + "." + else: + zone = "{}".format(zone) + device_name = device.name + "." + + print(f"zone: {zone}") + print(f"name: {device_name}") + print(str(netaddr.IPNetwork(str(device.primary_ip4)).ip)) + print() + + # Network ID + zone_rrsets = [] + zone_rrsets.append({'name': f'{device_name}{zone}.', 'changetype': 'replace', 'type': 'A', 'records': [ + {'content': str(netaddr.IPNetwork(str(device.primary_ip4)).ip), 'disabled': False, 'type': 'A'}], 'ttl': 900}) + + # Apply zone_rrsets + print(pdns.set_records(f"{zone}", zone_rrsets)) + + rdns_zone = pdns.get_rdns_zone_from_ip( + str(netaddr.IPNetwork(str(device.primary_ip4)).ip)) + rdns_rrsets = [] + if rdns_zone is None: + print(f"Failed to find RDNS Zone for IP") + + # Broadcast + rdns_rrsets.append({"name": ipaddress.ip_address(str(netaddr.IPNetwork(str(device.primary_ip4)).ip)).reverse_pointer + '.', "changetype": "replace", "type": "PTR", "records": [ + {"content": f'{device_name}{zone}.', "disabled": False, "type": "PTR"}], "ttl": 900}) + + # Apply rdns_rrsets + print(pdns.set_records(rdns_zone, rdns_rrsets)) diff --git a/tools/dhcpns/poetry.lock b/tools/dhcpns/poetry.lock index ff010f1..69b8885 100644 --- a/tools/dhcpns/poetry.lock +++ b/tools/dhcpns/poetry.lock @@ -109,6 +109,18 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "netaddr" +version = "0.8.0" +description = "A network address manipulation library for Python" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "netaddr-0.8.0-py2.py3-none-any.whl", hash = "sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac"}, + {file = "netaddr-0.8.0.tar.gz", hash = "sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243"}, +] + [[package]] name = "pynetbox" version = "7.0.1" @@ -180,5 +192,5 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [metadata] lock-version = "2.0" -python-versions = "^3.11" -content-hash = "4135825998043fdcea3674cc119324247b3de7379607a62979b5a8d392d615c4" +python-versions = "^3.9" +content-hash = "6b6ab04814884674356e174b6d88393b881c7aa0a3632f05bcc5335699b3e715" diff --git a/tools/dhcpns/pyproject.toml b/tools/dhcpns/pyproject.toml index c0e5fdf..8b71ac3 100644 --- a/tools/dhcpns/pyproject.toml +++ b/tools/dhcpns/pyproject.toml @@ -10,6 +10,7 @@ python = "^3.9" requests = "^2.28.2" pynetbox = "^7.0.1" python-dotenv = "^1.0.0" +netaddr = "^0.8.0" [build-system] -- cgit v1.2.3