1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
import pynetbox
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
# Take environment variables from .env
load_dotenv()
DOMAIN_NAME = os.environ['DOMAIN_NAME']
DOMAIN_SEARCH = os.environ['DOMAIN_SEARCH']
NAMESERVERS = os.environ['NAMESERVERS'].split()
nb = pynetbox.api(
os.getenv('NETBOX_URL'),
token=os.getenv('NETBOX_API_KEY'),
threading=True,
)
# DNS
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()]
rdns_zones = pdns.search("*.arpa", 2000, "zone")
kea4_subnets = []
kea6_subnets = []
kea_ddns_domains = []
kea_rddns_domains = []
# dhcp-client
vlans = nb.ipam.vlans.filter(tag='dhcp-client')
for vlan in vlans:
vlan_domain_name = f"{vlan.name}.{DOMAIN_NAME}"
prefixes4 = []
prefixes6 = []
kea_ddns_domains.append(ddns_domain(vlan_domain_name))
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)
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)
if 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'))
zone_rrsets = []
for prefix in prefixes4:
network = ipaddress.ip_network(prefix)
# Network ID
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': [
{'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': [
{'content': str(network[-1]), 'disabled': False, 'type':'A'}], 'ttl': 900})
rdns_zone = pdns.get_rdns_zone_from_ip(network[0])
rdns_rrsets = []
if rdns_zone is None:
print(f"Failed to find RDNS Zone for IP {network[0]}")
# 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})
# 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})
# 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})
# 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]))
# 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))
# 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 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": 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?")
|