blob: fe78a9f4230d61f034ea4d8e81e2149498de35ad [file] [log] [blame]
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +08001#!/usr/bin/env python3
2
3# SPDX-FileCopyrightText: © 2021 Open Networking Foundation <support@opennetworking.org>
4# SPDX-License-Identifier: Apache-2.0
5
6# service.py
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +08007
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +08008import netaddr
9
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +080010from .container import ServiceInfoContainer
11
12
13def getIPaddress(addressWithMask):
14 return str(netaddr.IPNetwork(addressWithMask).ip)
15
16
17def dhcpSubnetConfigGenerator():
18 dhcpSubnetConfigs = list()
19
20 serviceInfoContainer = ServiceInfoContainer()
21 serviceInfoContainer.initialize()
22
23 for domainName, domain in serviceInfoContainer.all():
24 subnetConfig = {
25 "dns_search": [domainName],
26 "dns_servers": [domain["dnsServer"]["address"]],
27 "ntp_servers": [domain["ntpServer"]["address"]],
Wei-Yu Chenc7d68312021-09-14 17:12:34 +080028 "tftpd_server": domain["dhcpServer"]["address"],
29 "range": domain["dhcprange"][0],
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +080030 "subnet": domain["subnet"],
Wei-Yu Chenc7d68312021-09-14 17:12:34 +080031 "routers": domain["router"],
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +080032 "hosts": list(),
33 }
34
35 for address, host in domain["hosts"].items():
36 subnetConfig["hosts"].append(
37 {
38 "ip_addr": getIPaddress(address),
39 "mac_addr": host["macaddr"],
40 "name": host["hostname"],
41 }
42 )
43
44 subnetConfig["hosts"] = sorted(
45 subnetConfig["hosts"], key=lambda x: int(x["ip_addr"].split(".")[-1])
46 )
47 dhcpSubnetConfigs.append(subnetConfig)
48
49 return dhcpSubnetConfigs
50
51
52def dnsFowardZoneConfigGenerator():
53 def getDomainNameByIP(ip_address):
54 """
55 getDomainNameByIP will return the corresponding domain name of an IP address
56 In the ntpServer, dhcpServer, dnsServer we only have the IP addresses of them,
57 But we can use the dhcp subnet configuration to find the FQDN
58 """
59 dhcpSubnetConfigs = dhcpSubnetConfigGenerator()
60
61 for domain in dhcpSubnetConfigs:
62 domainName = domain["dns_search"][0]
63 for host in domain["hosts"]:
64 if ip_address == host["ip_addr"]:
65 return f"{host['name']}.{domainName}."
Zack Williamsdac2be42021-08-19 16:14:31 -070066 return ""
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +080067
68 dnsForwardZoneConfigs = dict()
69
70 serviceInfoContainer = ServiceInfoContainer()
71 serviceInfoContainer.initialize()
72
73 for domainName, domain in serviceInfoContainer.all():
74 forwardZoneConfig = {
75 "cname": dict(),
76 "a": dict(),
77 "ns": list(),
78 "srv": dict(),
79 "txt": dict(),
80 }
81
82 # Get the services set to this Tenant network
83 ntpServer = domain["ntpServer"] or None
84 dhcpServer = domain["dhcpServer"] or None
85 dnsServer = domain["dnsServer"] or None
86
87 # If service exists, set the FQDN to CNAME records
88 if ntpServer:
Wei-Yu Chenc7d68312021-09-14 17:12:34 +080089 forwardZoneConfig["cname"]["ntp"] = getDomainNameByIP(ntpServer["address"])
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +080090 if dhcpServer:
Zack Williamsdac2be42021-08-19 16:14:31 -070091 forwardZoneConfig["cname"]["tftp"] = getDomainNameByIP(
92 dhcpServer["address"]
93 )
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +080094 if dnsServer:
Wei-Yu Chenc7d68312021-09-14 17:12:34 +080095 forwardZoneConfig["cname"]["ns"] = getDomainNameByIP(dnsServer["address"])
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +080096 forwardZoneConfig["ns"].append(getDomainNameByIP(dnsServer["address"]))
97
98 for address, host in domain["hosts"].items():
99 # Add exist IP address into dnsReverseZoneConfigs,
100 hostname = host["hostname"]
101 forwardZoneConfig["a"][hostname] = address
102
103 for address in netaddr.IPSet(domain["dhcprange"]):
104 # If address exists in ServiceInfoContainer's host dictionary,
105 # Use the pre-generated hostname as A record's name
106 hostname = "dhcp%03d" % address.words[-1]
107 forwardZoneConfig["a"][hostname] = str(address)
108
109 dnsForwardZoneConfigs[domainName] = forwardZoneConfig
110
111 return dnsForwardZoneConfigs
112
113
114def dnsReverseZoneConfigGenerator():
115 def canonicalize_rfc1918_prefix(prefix):
116 """
117 RFC1918 prefixes need to be expanded to their widest canonical range to
118 group all reverse lookup domains together for reverse DNS with NSD/Unbound.
119 """
120
121 pnet = netaddr.IPNetwork(prefix)
122 (o1, o2, o3, o4) = pnet.network.words # Split ipv4 octets
123 cidr_plen = pnet.prefixlen
124
125 if o1 == 10:
126 o2 = o3 = o4 = 0
127 cidr_plen = 8
Zack Williamsdac2be42021-08-19 16:14:31 -0700128 elif (o1 == 172 and 16 <= o2 <= 31) or (o1 == 192 and o2 == 168):
Wei-Yu Chenbd495ba2021-08-31 19:46:35 +0800129 o3 = o4 = 0
130 cidr_plen = 16
131
132 return "%s/%d" % (".".join(map(str, [o1, o2, o3, o4])), cidr_plen)
133
134 serviceInfoContainer = ServiceInfoContainer()
135 serviceInfoContainer.initialize()
136
137 # dnsReverseZoneConfigs contains all reverse zone records.
138 dnsReverseZoneConfigs = dict()
139 widedomain = None
140
141 for domainName, domain in serviceInfoContainer.all():
142
143 # Expand network range to group all tenant domains
144 widedomain = widedomain or canonicalize_rfc1918_prefix(domain["subnet"])
145
146 # Create the basic structure of reverse zone config
147 # {"10.0.0.0/8": {"ns": list(), "ptr": dict()}}
148 dnsReverseZoneConfigs.setdefault(widedomain, dict())
149 dnsReverseZoneConfigs[widedomain].setdefault("ns", list())
150 dnsReverseZoneConfigs[widedomain].setdefault("ptr", dict())
151
152 # Get the DNS services set to this Tenant network
153 dnsServer = domain["dnsServer"]["name"] if domain["dnsServer"] else None
154
155 # If service exists, set the FQDN to CNAME records
156 if dnsServer:
157 dnsReverseZoneConfigs[widedomain]["ns"].append(f"{domainName}.{dnsServer}.")
158
159 for address, host in domain["hosts"].items():
160 # Add exist IP address into dnsReverseZoneConfigs,
161 hostname = host["hostname"]
162 dnsReverseZoneConfigs[widedomain]["ptr"][
163 address
164 ] = f"{hostname}.{domainName}."
165
166 for address in netaddr.IPSet(domain["dhcprange"]):
167 # Add DHCP range IP address into dnsReverseZoneConfigs,
168 # Use the pre-generated hostname as A record's name
169 hostname = "dhcp%03d" % address.words[3]
170 dnsReverseZoneConfigs[widedomain]["ptr"][
171 str(address)
172 ] = f"{hostname}.{domainName}."
173
174 dnsReverseZoneConfigs[widedomain]["ptr"] = dict(
175 sorted(
176 dnsReverseZoneConfigs[widedomain]["ptr"].items(),
177 key=lambda x: (int(x[0].split(".")[-2]), int(x[0].split(".")[-1])),
178 )
179 )
180
181 return dnsReverseZoneConfigs