aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOle Mathias Heggem <olemathias.aa.heggem@gmail.com>2023-04-01 10:48:21 +0200
committerOle Mathias Heggem <olemathias.aa.heggem@gmail.com>2023-04-01 10:48:21 +0200
commit23cceabc42f16ce00d155d861614ea72420b19fd (patch)
treec9ed830f7dcd999794acec4c9eba4dedc0d9993c
parent715206a50b9cb80d2ff4b17556bc6fae3538e9f3 (diff)
dhcpns update
-rw-r--r--tools/dhcpns/config/dhcp4.py34
-rw-r--r--tools/dhcpns/main.py50
-rw-r--r--tools/dhcpns/pdns.py4
-rw-r--r--tools/dhcpns/pyproject.toml2
-rw-r--r--tools/dhcpns/tests/__init__.py0
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