Use parent subnet for DNS/TFTP server if none available in child subnet
Change-Id: Id58483cc739de570812cd6493ef5114a56f81625
diff --git a/scripts/netbox_edgeconfig.py b/scripts/netbox_edgeconfig.py
index a9b2172..1ba9dc5 100644
--- a/scripts/netbox_edgeconfig.py
+++ b/scripts/netbox_edgeconfig.py
@@ -38,6 +38,9 @@
device_services_cache = {}
interface_mac_cache = {}
+# parent prefixes
+parent_prefixes = {}
+
def parse_nb_args():
"""
@@ -124,7 +127,7 @@
return data
-def create_dns_zone(extension, devs):
+def create_dns_zone(extension, devs, parent_devs={}):
# Checks for dns entries
a_recs = {} # PTR records created by inverting this
@@ -201,6 +204,23 @@
if svc["port"] == 53 and svc["protocol"] == "udp":
ns_recs.append("%s.%s." % (dns_name, extension))
+ # iterate over the parent devs to add additional nameservers
+ for pname, pval in parent_devs.items():
+ if "services" in pval:
+ for svc in pval["services"]:
+ # look for DNS servers
+ if svc["port"] == 53 and svc["protocol"] == "udp":
+ # make name
+ dns_name = re.sub("[^a-z0-9.-]", "-", pname, 0, re.ASCII)
+
+ # add an a record for this nameserver if IP is outside of subnet
+ a_recs[dns_name] = pval["ip4"]
+
+ # add a NS record if it doesn't already exist
+ ns_name = "%s.%s." % (dns_name, extension)
+ if ns_name not in ns_recs:
+ ns_recs.append(ns_name)
+
return {
"a": a_recs,
"cname": cname_recs,
@@ -210,7 +230,7 @@
}
-def create_dhcp_subnet(prefix, prefix_search, devs):
+def create_dhcp_subnet(prefix, prefix_search, devs, parent_devs={}):
# makes DHCP subnet information
subnet = {}
@@ -218,46 +238,75 @@
subnet["subnet"] = prefix
subnet["dns_search"] = [prefix_search]
- hosts = []
- dns_servers = []
+ def dhcp_iterate(devs):
+ # inner function to iterate over a dev list
+ ihosts = []
+ idyn_range = None
+ irouter = None
+ idns_servers = []
+ itftpd_server = None
- for name, value in devs.items():
+ for name, value in devs.items():
- # handle a DHCP range
- if name == "prefix_dhcp":
- subnet["range"] = value["dhcp_range"]
- continue
+ # handle a DHCP range
+ if name == "prefix_dhcp":
+ idyn_range = value["dhcp_range"]
+ continue
- # handle a router reservation
- if name == "router":
- subnet["routers"] = value["ip4"]
- continue
+ # handle a router reservation
+ if name == "router":
+ irouter = value["ip4"]
+ continue
- # has a MAC address, and it's not null
- if "macaddr" in value and value["macaddr"]:
+ # has a MAC address, and it's not null
+ if "macaddr" in value and value["macaddr"]:
- hosts.append(
- {
- "name": name,
- "ip_addr": value["ip4"],
- "mac_addr": value["macaddr"].lower(),
- }
- )
+ ihosts.append(
+ {
+ "name": name,
+ "ip_addr": value["ip4"],
+ "mac_addr": value["macaddr"].lower(),
+ }
+ )
- # Add dns based on service entries
- if "services" in value:
- for svc in value["services"]:
+ # Add dns based on service entries
+ if "services" in value:
+ for svc in value["services"]:
- # add DNS server
- if svc["port"] == 53 and svc["protocol"] == "udp":
- dns_servers.append(value["ip4"])
+ # add DNS server
+ if svc["port"] == 53 and svc["protocol"] == "udp":
+ idns_servers.append(value["ip4"])
- # add tftp server
- if svc["port"] == 69 and svc["protocol"] == "udp":
- subnet["tftpd_server"] = value["ip4"]
+ # add tftp server
+ if svc["port"] == 69 and svc["protocol"] == "udp":
+ itftpd_server = value["ip4"]
+ return (ihosts, idyn_range, irouter, idns_servers, itftpd_server)
+
+ # run inner function and build
+ hosts, dyn_range, router, dns_servers, tftpd_server = dhcp_iterate(devs)
+
+ # assign only hosts, dynamic range, based on the prefix
subnet["hosts"] = hosts
- subnet["dns_servers"] = dns_servers
+ subnet["range"] = dyn_range
+
+ # only assign router if specified
+ if router:
+ subnet["routers"] = router
+
+ # find parent prefix devices, to fill in where needed
+ phosts, pdyn_range, prouter, pdns_servers, ptftpd_server = dhcp_iterate(parent_devs)
+
+ # use parent prefix devices if dns/tftp services needed aren't found within prefix
+ if dns_servers:
+ subnet["dns_servers"] = dns_servers
+ else:
+ subnet["dns_servers"] = pdns_servers
+
+ if tftpd_server:
+ subnet["tftpd_server"] = tftpd_server
+ else:
+ subnet["tftpd_server"] = ptftpd_server
return subnet
@@ -267,9 +316,14 @@
first_ip = str(netaddr.IPAddress(netaddr.IPNetwork(prefix).first + 1))
+ # look for interface corresponding to first IP address in range
for name, value in devs.items():
- if value["ip4"] == first_ip:
- return value["iface"]
+ if "ip4" in value:
+ if value["ip4"] == first_ip:
+ return value["iface"]
+
+ # if interface not found, return None and ignore
+ return None
def get_device_services(device_id, filters=""):
@@ -445,6 +499,27 @@
return devs
+def get_parent_prefix(child_prefix):
+ # returns a parent prefix given a child prefix
+ # FIXME: only returns the first found prefix, so doesn't handle more than 2 layers of hierarchy
+
+ # get all devices in a prefix
+ url = "%s%s" % (
+ settings["api_endpoint"],
+ "api/ipam/prefixes/?contains=%s" % child_prefix,
+ )
+
+ raw_prefixes = json_api_get(url, headers, validate_certs=settings["validate_certs"])
+
+ logger.debug(raw_prefixes)
+
+ for prefix in raw_prefixes["results"]:
+ if prefix["prefix"] != child_prefix:
+ return prefix["prefix"]
+
+ return None
+
+
def get_prefix_data(prefix):
# get all devices in a prefix
@@ -494,11 +569,28 @@
dhcpd_subnets = []
dhcpd_interfaces = []
devs_per_prefix = {}
+ prefixes = {}
+ parent_prefixes = {}
for prefix in settings["ip_prefixes"]:
prefix_data = get_prefix_data(prefix)
+ parent_prefix = get_parent_prefix(prefix)
+ prefix_data["parent"] = parent_prefix
+
+ pdevs = {}
+ if parent_prefix:
+ if parent_prefix in parent_prefixes:
+ pdevs = devs_per_prefix[parent_prefix]
+ else:
+ pdevs = get_prefix_devices(parent_prefix)
+ devs_per_prefix[parent_prefix] = pdevs
+
+ prefix_data["parent_devs"] = pdevs
+
+ prefixes[prefix] = prefix_data
+
prefix_domain_extension = prefix_data["description"]
devs = get_prefix_devices(prefix)
@@ -506,16 +598,18 @@
devs_per_prefix[prefix] = devs
dns_zones[prefix_domain_extension] = create_dns_zone(
- prefix_domain_extension, devs
+ prefix_domain_extension, devs, pdevs
)
dns_zones[prefix_domain_extension]["ip_range"] = prefix
- dhcpd_subnets.append(create_dhcp_subnet(prefix, prefix_domain_extension, devs))
+ dhcpd_subnets.append(
+ create_dhcp_subnet(prefix, prefix_domain_extension, devs, pdevs)
+ )
dhcpd_if = find_dhcpd_interface(prefix, devs)
- if dhcpd_if not in dhcpd_interfaces:
+ if dhcpd_if and dhcpd_if not in dhcpd_interfaces:
dhcpd_interfaces.append(dhcpd_if)
yaml_out.update(
@@ -524,7 +618,9 @@
"dns_rev_zones": dns_rev_zones,
"dhcpd_subnets": dhcpd_subnets,
"dhcpd_interfaces": dhcpd_interfaces,
- # "devs_per_prefix": devs_per_prefix, # useful when debugging
+ # the below are useful when debugging
+ # "devs_per_prefix": devs_per_prefix,
+ # "prefixes": prefixes,
}
)