Merge lp:~lamont/maas/bug-1599223-2.0 into lp:maas/2.0
- bug-1599223-2.0
- Merge into 2.0
Proposed by
LaMont Jones
Status: | Merged |
---|---|
Approved by: | LaMont Jones |
Approved revision: | no longer in the source branch. |
Merged at revision: | 5150 |
Proposed branch: | lp:~lamont/maas/bug-1599223-2.0 |
Merge into: | lp:maas/2.0 |
Diff against target: |
696 lines (+237/-135) 6 files modified
src/maasserver/dns/tests/test_zonegenerator.py (+11/-22) src/maasserver/dns/zonegenerator.py (+12/-38) src/maasserver/models/dnsresource.py (+6/-3) src/maasserver/models/staticipaddress.py (+102/-36) src/maasserver/models/tests/test_dnsresource.py (+2/-2) src/maasserver/models/tests/test_staticipaddress.py (+104/-34) |
To merge this branch: | bzr merge lp:~lamont/maas/bug-1599223-2.0 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
LaMont Jones (community) | Approve | ||
Review via email: mp+299586@code.launchpad.net |
Commit message
Refactor get_hostname_
When generating reverse DNS, we have to consider all of the forward zones, not just our subnet.
Description of the change
Refactor get_hostname_
When generating reverse DNS, we have to consider all of the forward zones, not just our subnet.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/maasserver/dns/tests/test_zonegenerator.py' | |||
2 | --- src/maasserver/dns/tests/test_zonegenerator.py 2016-05-12 19:07:37 +0000 | |||
3 | +++ src/maasserver/dns/tests/test_zonegenerator.py 2016-07-08 20:46:18 +0000 | |||
4 | @@ -371,10 +371,15 @@ | |||
5 | 371 | forward_zone("henry"), | 371 | forward_zone("henry"), |
6 | 372 | reverse_zone(default_domain, "10/29"), | 372 | reverse_zone(default_domain, "10/29"), |
7 | 373 | reverse_zone(default_domain, "10/24"))) | 373 | reverse_zone(default_domain, "10/24"))) |
10 | 374 | self.assertEqual( | 374 | self.assertEqual({ |
11 | 375 | {node.hostname: HostnameIPMapping( | 375 | node.hostname: HostnameIPMapping( |
12 | 376 | node.system_id, default_ttl, | 376 | node.system_id, default_ttl, |
14 | 377 | {'%s' % boot_ip.ip}, node.node_type)}, zones[0]._mapping) | 377 | {'%s' % boot_ip.ip}, node.node_type), |
15 | 378 | "%s.%s" % (interfaces[0].name, node.hostname): | ||
16 | 379 | HostnameIPMapping( | ||
17 | 380 | node.system_id, default_ttl, | ||
18 | 381 | {'%s' % sip.ip}, node.node_type)}, | ||
19 | 382 | zones[0]._mapping) | ||
20 | 378 | self.assertEqual( | 383 | self.assertEqual( |
21 | 379 | {dnsdata.dnsresource.name: HostnameRRsetMapping( | 384 | {dnsdata.dnsresource.name: HostnameRRsetMapping( |
22 | 380 | None, | 385 | None, |
23 | @@ -384,9 +389,7 @@ | |||
24 | 384 | node.fqdn: HostnameIPMapping( | 389 | node.fqdn: HostnameIPMapping( |
25 | 385 | node.system_id, 30, {'%s' % boot_ip.ip}, node.node_type), | 390 | node.system_id, 30, {'%s' % boot_ip.ip}, node.node_type), |
26 | 386 | '%s.%s' % (interfaces[0].name, node.fqdn): HostnameIPMapping( | 391 | '%s.%s' % (interfaces[0].name, node.fqdn): HostnameIPMapping( |
30 | 387 | None, default_ttl, {'%s' % sip.ip}, None), | 392 | None, default_ttl, {'%s' % sip.ip}, None)}, |
28 | 388 | '%s.%s' % (boot_iface.name, node.fqdn): HostnameIPMapping( | ||
29 | 389 | None, default_ttl, {'%s' % boot_ip.ip}, None)}, | ||
31 | 390 | zones[1]._mapping) | 393 | zones[1]._mapping) |
32 | 391 | 394 | ||
33 | 392 | def rfc2317_network(self, network): | 395 | def rfc2317_network(self, network): |
34 | @@ -517,8 +520,6 @@ | |||
35 | 517 | node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)} | 520 | node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)} |
36 | 518 | expected_reverse = { | 521 | expected_reverse = { |
37 | 519 | node.fqdn: HostnameIPMapping( | 522 | node.fqdn: HostnameIPMapping( |
38 | 520 | node.system_id, domain.ttl, {boot_ip.ip}, node.node_type), | ||
39 | 521 | "%s.%s" % (boot_iface.name, node.fqdn): HostnameIPMapping( | ||
40 | 522 | node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)} | 523 | node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)} |
41 | 523 | zones = ZoneGenerator( | 524 | zones = ZoneGenerator( |
42 | 524 | domain, subnet, default_ttl=global_ttl, | 525 | domain, subnet, default_ttl=global_ttl, |
43 | @@ -545,10 +546,6 @@ | |||
44 | 545 | expected_reverse = { | 546 | expected_reverse = { |
45 | 546 | node.fqdn: HostnameIPMapping( | 547 | node.fqdn: HostnameIPMapping( |
46 | 547 | node.system_id, node.address_ttl, {boot_ip.ip}, | 548 | node.system_id, node.address_ttl, {boot_ip.ip}, |
47 | 548 | node.node_type), | ||
48 | 549 | "%s.%s" % (boot_iface.name, node.fqdn): | ||
49 | 550 | HostnameIPMapping( | ||
50 | 551 | node.system_id, node.address_ttl, {boot_ip.ip}, | ||
51 | 552 | node.node_type)} | 549 | node.node_type)} |
52 | 553 | zones = ZoneGenerator( | 550 | zones = ZoneGenerator( |
53 | 554 | domain, subnet, default_ttl=global_ttl, | 551 | domain, subnet, default_ttl=global_ttl, |
54 | @@ -581,11 +578,7 @@ | |||
55 | 581 | node.system_id, node.address_ttl, ips, node.node_type)} | 578 | node.system_id, node.address_ttl, ips, node.node_type)} |
56 | 582 | expected_reverse = { | 579 | expected_reverse = { |
57 | 583 | node.fqdn: HostnameIPMapping( | 580 | node.fqdn: HostnameIPMapping( |
63 | 584 | node.system_id, node.address_ttl, ips, node.node_type), | 581 | node.system_id, node.address_ttl, ips, node.node_type)} |
59 | 585 | "%s.%s" % (boot_iface.name, node.fqdn): | ||
60 | 586 | HostnameIPMapping( | ||
61 | 587 | node.system_id, node.address_ttl, {boot_ip.ip}, | ||
62 | 588 | node.node_type)} | ||
64 | 589 | zones = ZoneGenerator( | 582 | zones = ZoneGenerator( |
65 | 590 | domain, subnet, default_ttl=global_ttl, | 583 | domain, subnet, default_ttl=global_ttl, |
66 | 591 | serial=random.randint(0, 65535)).as_list() | 584 | serial=random.randint(0, 65535)).as_list() |
67 | @@ -621,11 +614,7 @@ | |||
68 | 621 | node.fqdn: HostnameIPMapping( | 614 | node.fqdn: HostnameIPMapping( |
69 | 622 | node.system_id, node.address_ttl, node_ips, node.node_type), | 615 | node.system_id, node.address_ttl, node_ips, node.node_type), |
70 | 623 | dnsrr.fqdn: HostnameIPMapping( | 616 | dnsrr.fqdn: HostnameIPMapping( |
76 | 624 | None, dnsrr.address_ttl, dnsrr_ips, None), | 617 | None, dnsrr.address_ttl, dnsrr_ips, None)} |
72 | 625 | "%s.%s" % (boot_iface.name, node.fqdn): | ||
73 | 626 | HostnameIPMapping( | ||
74 | 627 | node.system_id, node.address_ttl, {boot_ip.ip}, | ||
75 | 628 | node.node_type)} | ||
77 | 629 | zones = ZoneGenerator( | 618 | zones = ZoneGenerator( |
78 | 630 | domain, subnet, default_ttl=global_ttl, | 619 | domain, subnet, default_ttl=global_ttl, |
79 | 631 | serial=random.randint(0, 65535)).as_list() | 620 | serial=random.randint(0, 65535)).as_list() |
80 | 632 | 621 | ||
81 | === modified file 'src/maasserver/dns/zonegenerator.py' | |||
82 | --- src/maasserver/dns/zonegenerator.py 2016-04-21 19:13:47 +0000 | |||
83 | +++ src/maasserver/dns/zonegenerator.py 2016-07-08 20:46:18 +0000 | |||
84 | @@ -22,11 +22,7 @@ | |||
85 | 22 | from maasserver.models.dnsdata import DNSData | 22 | from maasserver.models.dnsdata import DNSData |
86 | 23 | from maasserver.models.dnsresource import separate_fqdn | 23 | from maasserver.models.dnsresource import separate_fqdn |
87 | 24 | from maasserver.models.domain import Domain | 24 | from maasserver.models.domain import Domain |
93 | 25 | from maasserver.models.node import Node | 25 | from maasserver.models.staticipaddress import StaticIPAddress |
89 | 26 | from maasserver.models.staticipaddress import ( | ||
90 | 27 | HostnameIPMapping, | ||
91 | 28 | StaticIPAddress, | ||
92 | 29 | ) | ||
94 | 30 | from maasserver.models.subnet import Subnet | 26 | from maasserver.models.subnet import Subnet |
95 | 31 | from maasserver.server_address import get_maas_facing_server_address | 27 | from maasserver.server_address import get_maas_facing_server_address |
96 | 32 | from netaddr import ( | 28 | from netaddr import ( |
97 | @@ -255,12 +251,16 @@ | |||
98 | 255 | IPNetwork("%s/124" % network.network).network) | 251 | IPNetwork("%s/124" % network.network).network) |
99 | 256 | rfc2317_glue.setdefault(basenet, set()).add(network) | 252 | rfc2317_glue.setdefault(basenet, set()).add(network) |
100 | 257 | 253 | ||
101 | 254 | # Since get_hostname_ip_mapping(Subnet) ignores Subnet.id, so we can | ||
102 | 255 | # just do it once and be happy. LP#1600259 | ||
103 | 256 | if len(subnets): | ||
104 | 257 | mappings['reverse'] = mappings[Subnet.objects.first()] | ||
105 | 258 | |||
106 | 258 | # For each of the zones that we are generating (one or more per | 259 | # For each of the zones that we are generating (one or more per |
107 | 259 | # subnet), compile the zone from: | 260 | # subnet), compile the zone from: |
108 | 260 | # 1. Dynamic ranges on this subnet. | 261 | # 1. Dynamic ranges on this subnet. |
109 | 261 | # 2. Node: ip mapping(subnet), including DNSResource records for | 262 | # 2. Node: ip mapping(subnet), including DNSResource records for |
110 | 262 | # StaticIPAddresses in this subnet. | 263 | # StaticIPAddresses in this subnet. |
111 | 263 | # 3. Interfaces on any node that have IP addresses in this subnet. | ||
112 | 264 | # All of this needs to be done smallest to largest so that we can | 264 | # All of this needs to be done smallest to largest so that we can |
113 | 265 | # correctly gather the rfc2317 glue that we need. Failure to sort | 265 | # correctly gather the rfc2317 glue that we need. Failure to sort |
114 | 266 | # means that we wind up grabbing (and deleting) the rfc2317 glue info | 266 | # means that we wind up grabbing (and deleting) the rfc2317 glue info |
115 | @@ -283,38 +283,12 @@ | |||
116 | 283 | for ip_range in subnet.get_dynamic_ranges() | 283 | for ip_range in subnet.get_dynamic_ranges() |
117 | 284 | ] | 284 | ] |
118 | 285 | 285 | ||
151 | 286 | # 2. Start with the map of all of the nodes on this subnet, | 286 | # 2. Start with the map of all of the nodes, including all |
152 | 287 | # including all DNSResource-associated addresses. | 287 | # DNSResource-associated addresses. We will prune this to just |
153 | 288 | mapping = mappings[subnet] | 288 | # entries for the subnet when we actually generate the zonefile. |
154 | 289 | 289 | # If we get here, then we have subnets, so we noticed that above | |
155 | 290 | # 3. Add all of the interface named records. | 290 | # and created mappings['reverse']. LP#1600259 |
156 | 291 | # 2015-12-18 lamont N.B., these are not found in the forward zone, | 291 | mapping = mappings['reverse'] |
125 | 292 | # on purpose. If someone eventually calls this a bug, we can | ||
126 | 293 | # revisit the size increase this would create in the forward zone. | ||
127 | 294 | # This will also include any discovered addresses on such | ||
128 | 295 | # interfaces. | ||
129 | 296 | nodes = Node.objects.filter( | ||
130 | 297 | interface__ip_addresses__subnet_id=subnet.id, | ||
131 | 298 | interface__ip_addresses__ip__isnull=False).prefetch_related( | ||
132 | 299 | "interface_set__ip_addresses") | ||
133 | 300 | for node in nodes: | ||
134 | 301 | if node.address_ttl is not None: | ||
135 | 302 | ttl = node.address_ttl | ||
136 | 303 | elif node.domain.ttl is not None: | ||
137 | 304 | ttl = node.domain.ttl | ||
138 | 305 | else: | ||
139 | 306 | ttl = default_ttl | ||
140 | 307 | for iface in node.interface_set.all(): | ||
141 | 308 | ips_in_subnet = { | ||
142 | 309 | ip.ip | ||
143 | 310 | for ip in iface.ip_addresses.all() | ||
144 | 311 | if (ip.ip is not None and ip.subnet_id == subnet.id)} | ||
145 | 312 | if len(ips_in_subnet) > 0: | ||
146 | 313 | iface_map = HostnameIPMapping( | ||
147 | 314 | node.system_id, ttl, ips_in_subnet, node.node_type) | ||
148 | 315 | mapping.update({ | ||
149 | 316 | "%s.%s" % (iface.name, iface.node.fqdn): iface_map | ||
150 | 317 | }) | ||
157 | 318 | 292 | ||
158 | 319 | # Use the default_domain as the name for the NS host in the reverse | 293 | # Use the default_domain as the name for the NS host in the reverse |
159 | 320 | # zones. If this network is actually a parent rfc2317 glue | 294 | # zones. If this network is actually a parent rfc2317 glue |
160 | 321 | 295 | ||
161 | === modified file 'src/maasserver/models/dnsresource.py' | |||
162 | --- src/maasserver/models/dnsresource.py 2016-05-11 19:01:48 +0000 | |||
163 | +++ src/maasserver/models/dnsresource.py 2016-07-08 20:46:18 +0000 | |||
164 | @@ -75,14 +75,17 @@ | |||
165 | 75 | """ | 75 | """ |
166 | 76 | if fqdn is None or fqdn == '' or fqdn == '@': | 76 | if fqdn is None or fqdn == '' or fqdn == '@': |
167 | 77 | return (fqdn, None) | 77 | return (fqdn, None) |
170 | 78 | elif Domain.objects.filter(name=fqdn).exists(): | 78 | if domainname is not None: |
171 | 79 | if domainname == fqdn or domainname is None: | 79 | if domainname == fqdn: |
172 | 80 | return ('@', fqdn) | 80 | return ('@', fqdn) |
173 | 81 | else: | 81 | else: |
174 | 82 | # strip off the passed in ".$domainname" from the fqdn. | 82 | # strip off the passed in ".$domainname" from the fqdn. |
175 | 83 | name = fqdn[:-len(domainname) - 1] | 83 | name = fqdn[:-len(domainname) - 1] |
176 | 84 | return (name, domainname) | 84 | return (name, domainname) |
178 | 85 | elif rrtype == 'SRV': | 85 | else: |
179 | 86 | if Domain.objects.filter(name=fqdn).exists(): | ||
180 | 87 | return ('@', fqdn) | ||
181 | 88 | if rrtype == 'SRV': | ||
182 | 86 | spec = SRV_LHS | 89 | spec = SRV_LHS |
183 | 87 | else: | 90 | else: |
184 | 88 | spec = LABEL | 91 | spec = LABEL |
185 | 89 | 92 | ||
186 | === modified file 'src/maasserver/models/staticipaddress.py' | |||
187 | --- src/maasserver/models/staticipaddress.py 2016-05-26 17:50:49 +0000 | |||
188 | +++ src/maasserver/models/staticipaddress.py 2016-07-08 20:46:18 +0000 | |||
189 | @@ -51,7 +51,6 @@ | |||
190 | 51 | from maasserver.models.domain import Domain | 51 | from maasserver.models.domain import Domain |
191 | 52 | from maasserver.models.subnet import Subnet | 52 | from maasserver.models.subnet import Subnet |
192 | 53 | from maasserver.models.timestampedmodel import TimestampedModel | 53 | from maasserver.models.timestampedmodel import TimestampedModel |
193 | 54 | from maasserver.utils import strip_domain | ||
194 | 55 | from maasserver.utils.dns import get_ip_based_hostname | 54 | from maasserver.utils.dns import get_ip_based_hostname |
195 | 56 | from maasserver.utils.orm import ( | 55 | from maasserver.utils.orm import ( |
196 | 57 | make_serialization_failure, | 56 | make_serialization_failure, |
197 | @@ -226,8 +225,10 @@ | |||
198 | 226 | qs = self.filter( | 225 | qs = self.filter( |
199 | 227 | Q(alloc_type=IPADDRESS_TYPE.USER_RESERVED) | | 226 | Q(alloc_type=IPADDRESS_TYPE.USER_RESERVED) | |
200 | 228 | Q(dnsresource__isnull=False)) | 227 | Q(dnsresource__isnull=False)) |
201 | 228 | # If this is a subnet, we need to ignore subnet.id, as per | ||
202 | 229 | # get_hostname_ip_mapping(). LP#1600259 | ||
203 | 229 | if isinstance(domain_or_subnet, Subnet): | 230 | if isinstance(domain_or_subnet, Subnet): |
205 | 230 | qs = qs.filter(subnet_id=domain_or_subnet.id) | 231 | pass |
206 | 231 | elif isinstance(domain_or_subnet, Domain): | 232 | elif isinstance(domain_or_subnet, Domain): |
207 | 232 | qs = qs.filter(dnsresource__domain_id=domain_or_subnet.id) | 233 | qs = qs.filter(dnsresource__domain_id=domain_or_subnet.id) |
208 | 233 | qs = qs.prefetch_related("dnsresource_set") | 234 | qs = qs.prefetch_related("dnsresource_set") |
209 | @@ -307,20 +308,6 @@ | |||
210 | 307 | # return the IPs for the oldest Interface address. | 308 | # return the IPs for the oldest Interface address. |
211 | 308 | # | 309 | # |
212 | 309 | # For nodes that have disable_ipv4 set, leave out any IPv4 address. | 310 | # For nodes that have disable_ipv4 set, leave out any IPv4 address. |
213 | 310 | if isinstance(domain_or_subnet, Subnet): | ||
214 | 311 | search_term = "staticip.subnet_id" | ||
215 | 312 | elif isinstance(domain_or_subnet, Domain): | ||
216 | 313 | # The model has nodes in the parent domain, but they actually live | ||
217 | 314 | # in the child domain. And the parent needs the glue. So we | ||
218 | 315 | # return such nodes addresses in _BOTH_ the parent and the child | ||
219 | 316 | # domains. domain2.name will be non-null if this host's fqdn is the | ||
220 | 317 | # name of a domain in MAAS. | ||
221 | 318 | # n.b. The SQL says: WHERE ... $search_term = %s ... | ||
222 | 319 | # we overload that to accomplish our objectives. | ||
223 | 320 | search_term = """domain2.name IS NOT NULL OR node.domain_id""" | ||
224 | 321 | else: | ||
225 | 322 | raise ValueError('bad object passed to get_hostname_ip_mapping') | ||
226 | 323 | |||
227 | 324 | default_ttl = "%d" % Config.objects.get_config('default_dns_ttl') | 311 | default_ttl = "%d" % Config.objects.get_config('default_dns_ttl') |
228 | 325 | if raw_ttl: | 312 | if raw_ttl: |
229 | 326 | ttl_clause = """node.address_ttl""" | 313 | ttl_clause = """node.address_ttl""" |
230 | @@ -332,11 +319,11 @@ | |||
231 | 332 | %s)""" % default_ttl | 319 | %s)""" % default_ttl |
232 | 333 | sql_query = """ | 320 | sql_query = """ |
233 | 334 | SELECT DISTINCT ON (node.hostname, is_boot, family(staticip.ip)) | 321 | SELECT DISTINCT ON (node.hostname, is_boot, family(staticip.ip)) |
235 | 335 | node.hostname, | 322 | CONCAT(node.hostname, '.', domain.name) AS fqdn, |
236 | 336 | node.system_id, | 323 | node.system_id, |
237 | 337 | node.node_type, | 324 | node.node_type, |
238 | 338 | """ + ttl_clause + """ AS ttl, | 325 | """ + ttl_clause + """ AS ttl, |
240 | 339 | domain.name, staticip.ip, | 326 | staticip.ip, |
241 | 340 | COALESCE( | 327 | COALESCE( |
242 | 341 | node.boot_interface_id IS NOT NULL AND | 328 | node.boot_interface_id IS NOT NULL AND |
243 | 342 | ( | 329 | ( |
244 | @@ -359,22 +346,42 @@ | |||
245 | 359 | link.interface_id = interface.id | 346 | link.interface_id = interface.id |
246 | 360 | JOIN maasserver_staticipaddress AS staticip ON | 347 | JOIN maasserver_staticipaddress AS staticip ON |
247 | 361 | staticip.id = link.staticipaddress_id | 348 | staticip.id = link.staticipaddress_id |
248 | 349 | """ | ||
249 | 350 | if isinstance(domain_or_subnet, Domain): | ||
250 | 351 | # The model has nodes in the parent domain, but they actually live | ||
251 | 352 | # in the child domain. And the parent needs the glue. So we | ||
252 | 353 | # return such nodes addresses in _BOTH_ the parent and the child | ||
253 | 354 | # domains. domain2.name will be non-null if this host's fqdn is the | ||
254 | 355 | # name of a domain in MAAS. | ||
255 | 356 | sql_query += """ | ||
256 | 362 | LEFT JOIN maasserver_domain as domain2 ON | 357 | LEFT JOIN maasserver_domain as domain2 ON |
257 | 363 | /* Pick up another copy of domain looking for instances of | 358 | /* Pick up another copy of domain looking for instances of |
258 | 364 | * nodes a the top of a domain. | 359 | * nodes a the top of a domain. |
259 | 365 | */ | 360 | */ |
260 | 366 | domain2.name = CONCAT(node.hostname, '.', domain.name) | 361 | domain2.name = CONCAT(node.hostname, '.', domain.name) |
261 | 367 | WHERE | 362 | WHERE |
262 | 363 | (domain2.name IS NOT NULL OR node.domain_id = %s) AND | ||
263 | 364 | """ | ||
264 | 365 | query_parms = [domain_or_subnet.id, ] | ||
265 | 366 | else: | ||
266 | 367 | # For subnets, we need ALL the names, so that we can correctly | ||
267 | 368 | # identify which ones should have the FQDN. dns/zonegenerator.py | ||
268 | 369 | # optimizes based on this, and only calls once with a subnet, | ||
269 | 370 | # expecting to get all the subnets back in one table. | ||
270 | 371 | sql_query += """ | ||
271 | 372 | WHERE | ||
272 | 373 | """ | ||
273 | 374 | query_parms = [] | ||
274 | 375 | sql_query += """ | ||
275 | 368 | staticip.ip IS NOT NULL AND | 376 | staticip.ip IS NOT NULL AND |
276 | 369 | host(staticip.ip) != '' AND | 377 | host(staticip.ip) != '' AND |
277 | 370 | (""" + search_term + """ = %s) AND | ||
278 | 371 | ( | 378 | ( |
279 | 372 | node.disable_ipv4 IS FALSE OR | 379 | node.disable_ipv4 IS FALSE OR |
280 | 373 | family(staticip.ip) <> 4 | 380 | family(staticip.ip) <> 4 |
281 | 374 | ) | 381 | ) |
282 | 375 | ORDER BY | 382 | ORDER BY |
283 | 376 | node.hostname, | 383 | node.hostname, |
285 | 377 | is_boot, | 384 | is_boot DESC, |
286 | 378 | family(staticip.ip), | 385 | family(staticip.ip), |
287 | 379 | CASE | 386 | CASE |
288 | 380 | WHEN interface.type = 'bond' AND | 387 | WHEN interface.type = 'bond' AND |
289 | @@ -407,33 +414,92 @@ | |||
290 | 407 | END, | 414 | END, |
291 | 408 | interface.id | 415 | interface.id |
292 | 409 | """ | 416 | """ |
293 | 417 | iface_sql_query = """ | ||
294 | 418 | SELECT | ||
295 | 419 | CONCAT(node.hostname, '.', domain.name) AS fqdn, | ||
296 | 420 | node.system_id, | ||
297 | 421 | node.node_type, | ||
298 | 422 | """ + ttl_clause + """ AS ttl, | ||
299 | 423 | staticip.ip, | ||
300 | 424 | interface.name | ||
301 | 425 | FROM | ||
302 | 426 | maasserver_interface AS interface | ||
303 | 427 | JOIN maasserver_node AS node ON | ||
304 | 428 | node.id = interface.node_id | ||
305 | 429 | JOIN maasserver_domain as domain ON | ||
306 | 430 | domain.id = node.domain_id | ||
307 | 431 | JOIN maasserver_interface_ip_addresses AS link ON | ||
308 | 432 | link.interface_id = interface.id | ||
309 | 433 | JOIN maasserver_staticipaddress AS staticip ON | ||
310 | 434 | staticip.id = link.staticipaddress_id | ||
311 | 435 | """ | ||
312 | 436 | if isinstance(domain_or_subnet, Domain): | ||
313 | 437 | # This logic is similar to the logic in sql_query above. | ||
314 | 438 | iface_sql_query += """ | ||
315 | 439 | LEFT JOIN maasserver_domain as domain2 ON | ||
316 | 440 | /* Pick up another copy of domain looking for instances of | ||
317 | 441 | * the name as the top of a domain. | ||
318 | 442 | */ | ||
319 | 443 | domain2.name = CONCAT( | ||
320 | 444 | interface.name, '.', node.hostname, '.', domain.name) | ||
321 | 445 | WHERE | ||
322 | 446 | (domain2.name IS NOT NULL OR node.domain_id = %s) AND | ||
323 | 447 | """ | ||
324 | 448 | else: | ||
325 | 449 | # For subnets, we need ALL the names, so that we can correctly | ||
326 | 450 | # identify which ones should have the FQDN. dns/zonegenerator.py | ||
327 | 451 | # optimizes based on this, and only calls once with a subnet, | ||
328 | 452 | # expecting to get all the subnets back in one table. | ||
329 | 453 | iface_sql_query += """ | ||
330 | 454 | WHERE | ||
331 | 455 | """ | ||
332 | 456 | iface_sql_query += """ | ||
333 | 457 | staticip.ip IS NOT NULL AND | ||
334 | 458 | host(staticip.ip) != '' AND | ||
335 | 459 | ( | ||
336 | 460 | node.disable_ipv4 IS FALSE OR | ||
337 | 461 | family(staticip.ip) <> 4 | ||
338 | 462 | ) | ||
339 | 463 | ORDER BY | ||
340 | 464 | node.hostname, | ||
341 | 465 | interface.id | ||
342 | 466 | """ | ||
343 | 410 | # We get user reserved et al mappings first, so that we can overwrite | 467 | # We get user reserved et al mappings first, so that we can overwrite |
344 | 411 | # TTL as we process the return from the SQL horror above. | 468 | # TTL as we process the return from the SQL horror above. |
345 | 412 | mapping = self._get_user_reserved_mappings(domain_or_subnet) | 469 | mapping = self._get_user_reserved_mappings(domain_or_subnet) |
346 | 470 | # All of the mappings that we got mean that we will only want to add | ||
347 | 471 | # addresses for the boot interface (is_boot == True). | ||
348 | 413 | iface_is_boot = defaultdict(bool, { | 472 | iface_is_boot = defaultdict(bool, { |
349 | 414 | hostname: True for hostname in mapping.keys() | 473 | hostname: True for hostname in mapping.keys() |
350 | 415 | }) | 474 | }) |
352 | 416 | cursor.execute(sql_query, (domain_or_subnet.id,)) | 475 | cursor.execute(sql_query, query_parms) |
353 | 417 | # The records from the query provide, for each hostname (after | 476 | # The records from the query provide, for each hostname (after |
354 | 418 | # stripping domain), the boot and non-boot interface ip address in ipv4 | 477 | # stripping domain), the boot and non-boot interface ip address in ipv4 |
355 | 419 | # and ipv6. Our task: if there are boot interace IPs, they win. If | 478 | # and ipv6. Our task: if there are boot interace IPs, they win. If |
356 | 420 | # there are none, then whatever we got wins. The ORDER BY means that | 479 | # there are none, then whatever we got wins. The ORDER BY means that |
358 | 421 | # we will see all of the non-boot interfaces before we see any boot | 480 | # we will see all of the boot interfaces before we see any non-boot |
359 | 422 | # interface IPs. See Bug#1584850 | 481 | # interface IPs. See Bug#1584850 |
374 | 423 | for (node_name, system_id, node_type, | 482 | for (fqdn, system_id, node_type, ttl, |
375 | 424 | ttl, domain_name, ip, is_boot) in cursor.fetchall(): | 483 | ip, is_boot) in cursor.fetchall(): |
376 | 425 | hostname = "%s.%s" % (strip_domain(node_name), domain_name) | 484 | mapping[fqdn].node_type = node_type |
377 | 426 | mapping[hostname].node_type = node_type | 485 | mapping[fqdn].system_id = system_id |
378 | 427 | mapping[hostname].system_id = system_id | 486 | mapping[fqdn].ttl = ttl |
379 | 428 | mapping[hostname].ttl = ttl | 487 | if is_boot: |
380 | 429 | # If this is a boot interface, and we have previously only found | 488 | iface_is_boot[fqdn] = True |
381 | 430 | # non-boot interface IPs, discard them. See also Bug#1584850. | 489 | # If we have an IP on the right interface type, save it. |
382 | 431 | if is_boot and not iface_is_boot[hostname]: | 490 | if is_boot == iface_is_boot[fqdn]: |
383 | 432 | mapping[hostname].ips = set() | 491 | mapping[fqdn].ips.add(ip) |
384 | 433 | iface_is_boot[hostname] = True | 492 | # Next, get all the addresses, on all the interfaces, and add the ones |
385 | 434 | # At this point, if the interface type is correct, keep the IP. | 493 | # that are not already present on the FQDN as $IFACE.$FQDN. |
386 | 435 | if is_boot == iface_is_boot[hostname]: | 494 | cursor.execute(iface_sql_query, (domain_or_subnet.id,)) |
387 | 436 | mapping[hostname].ips.add(ip) | 495 | for (fqdn, system_id, node_type, ttl, |
388 | 496 | ip, iface_name) in cursor.fetchall(): | ||
389 | 497 | if ip not in mapping[fqdn].ips: | ||
390 | 498 | name = "%s.%s" % (iface_name, fqdn) | ||
391 | 499 | mapping[name].node_type = node_type | ||
392 | 500 | mapping[name].system_id = system_id | ||
393 | 501 | mapping[name].ttl = ttl | ||
394 | 502 | mapping[name].ips.add(ip) | ||
395 | 437 | return mapping | 503 | return mapping |
396 | 438 | 504 | ||
397 | 439 | def filter_by_ip_family(self, family): | 505 | def filter_by_ip_family(self, family): |
398 | 440 | 506 | ||
399 | === modified file 'src/maasserver/models/tests/test_dnsresource.py' | |||
400 | --- src/maasserver/models/tests/test_dnsresource.py 2016-04-22 01:56:28 +0000 | |||
401 | +++ src/maasserver/models/tests/test_dnsresource.py 2016-07-08 20:46:18 +0000 | |||
402 | @@ -131,9 +131,9 @@ | |||
403 | 131 | parent = "%s.%s" % ( | 131 | parent = "%s.%s" % ( |
404 | 132 | factory.make_name("b"), | 132 | factory.make_name("b"), |
405 | 133 | factory.make_name("c")) | 133 | factory.make_name("c")) |
407 | 134 | label = factory.make_name("a") | 134 | label = "%s.%s" % (factory.make_name("a"), factory.make_name("d")) |
408 | 135 | name = "%s.%s" % (label, parent) | 135 | name = "%s.%s" % (label, parent) |
410 | 136 | factory.make_Domain(name=name) | 136 | factory.make_Domain(name=parent) |
411 | 137 | self.assertEqual( | 137 | self.assertEqual( |
412 | 138 | (label, parent), separate_fqdn(name, domainname=parent)) | 138 | (label, parent), separate_fqdn(name, domainname=parent)) |
413 | 139 | 139 | ||
414 | 140 | 140 | ||
415 | === modified file 'src/maasserver/models/tests/test_staticipaddress.py' | |||
416 | --- src/maasserver/models/tests/test_staticipaddress.py 2016-05-26 17:50:49 +0000 | |||
417 | +++ src/maasserver/models/tests/test_staticipaddress.py 2016-07-08 20:46:18 +0000 | |||
418 | @@ -33,6 +33,7 @@ | |||
419 | 33 | HostnameIPMapping, | 33 | HostnameIPMapping, |
420 | 34 | StaticIPAddress, | 34 | StaticIPAddress, |
421 | 35 | ) | 35 | ) |
422 | 36 | from maasserver.models.subnet import Subnet | ||
423 | 36 | from maasserver.testing.factory import factory | 37 | from maasserver.testing.factory import factory |
424 | 37 | from maasserver.testing.testcase import ( | 38 | from maasserver.testing.testcase import ( |
425 | 38 | MAASServerTestCase, | 39 | MAASServerTestCase, |
426 | @@ -357,23 +358,54 @@ | |||
427 | 357 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain) | 358 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain) |
428 | 358 | self.assertEqual(expected_mapping, mapping) | 359 | self.assertEqual(expected_mapping, mapping) |
429 | 359 | 360 | ||
431 | 360 | def test_get_hostname_ip_mapping_returns_fqdn(self): | 361 | def test_get_hostname_ip_mapping_returns_all_mappings_for_subnet(self): |
432 | 362 | domain = Domain.objects.get_default_domain() | ||
433 | 363 | expected_mapping = {} | ||
434 | 364 | for _ in range(3): | ||
435 | 365 | node = factory.make_Node(interface=True, disable_ipv4=False) | ||
436 | 366 | boot_interface = node.get_boot_interface() | ||
437 | 367 | subnet = factory.make_Subnet() | ||
438 | 368 | staticip = factory.make_StaticIPAddress( | ||
439 | 369 | alloc_type=IPADDRESS_TYPE.STICKY, | ||
440 | 370 | ip=factory.pick_ip_in_Subnet(subnet), | ||
441 | 371 | subnet=subnet, interface=boot_interface) | ||
442 | 372 | full_hostname = "%s.%s" % (node.hostname, domain.name) | ||
443 | 373 | expected_mapping[full_hostname] = HostnameIPMapping( | ||
444 | 374 | node.system_id, 30, {staticip.ip}, node.node_type) | ||
445 | 375 | # See also LP#1600259. It doesn't matter what subnet is passed in, you | ||
446 | 376 | # get all of them. | ||
447 | 377 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | ||
448 | 378 | Subnet.objects.first()) | ||
449 | 379 | self.assertEqual(expected_mapping, mapping) | ||
450 | 380 | |||
451 | 381 | def test_get_hostname_ip_mapping_returns_fqdn_and_other(self): | ||
452 | 361 | hostname = factory.make_name('hostname') | 382 | hostname = factory.make_name('hostname') |
453 | 362 | domainname = factory.make_name('domain') | 383 | domainname = factory.make_name('domain') |
454 | 363 | factory.make_Domain(name=domainname) | 384 | factory.make_Domain(name=domainname) |
455 | 364 | full_hostname = "%s.%s" % (hostname, domainname) | 385 | full_hostname = "%s.%s" % (hostname, domainname) |
456 | 365 | subnet = factory.make_Subnet() | 386 | subnet = factory.make_Subnet() |
457 | 366 | node = factory.make_Node_with_Interface_on_Subnet( | 387 | node = factory.make_Node_with_Interface_on_Subnet( |
459 | 367 | interface=True, hostname=full_hostname, | 388 | interface=True, hostname=full_hostname, interface_count=3, |
460 | 368 | subnet=subnet, disable_ipv4=False) | 389 | subnet=subnet, disable_ipv4=False) |
461 | 369 | boot_interface = node.get_boot_interface() | 390 | boot_interface = node.get_boot_interface() |
462 | 370 | staticip = factory.make_StaticIPAddress( | 391 | staticip = factory.make_StaticIPAddress( |
463 | 371 | alloc_type=IPADDRESS_TYPE.STICKY, | 392 | alloc_type=IPADDRESS_TYPE.STICKY, |
464 | 372 | ip=factory.pick_ip_in_Subnet(subnet), | 393 | ip=factory.pick_ip_in_Subnet(subnet), |
465 | 373 | subnet=subnet, interface=boot_interface) | 394 | subnet=subnet, interface=boot_interface) |
466 | 395 | ifaces = node.interface_set.exclude(interface=boot_interface) | ||
467 | 396 | sip2 = factory.make_StaticIPAddress( | ||
468 | 397 | alloc_type=IPADDRESS_TYPE.STICKY, | ||
469 | 398 | ip=factory.pick_ip_in_Subnet(subnet), | ||
470 | 399 | subnet=subnet, interface=ifaces[0]) | ||
471 | 374 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(subnet) | 400 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(subnet) |
474 | 375 | self.assertEqual({full_hostname: HostnameIPMapping( | 401 | self.assertEqual({ |
475 | 376 | node.system_id, 30, {staticip.ip}, node.node_type)}, mapping) | 402 | full_hostname: |
476 | 403 | HostnameIPMapping( | ||
477 | 404 | node.system_id, 30, {staticip.ip}, node.node_type), | ||
478 | 405 | "%s.%s" % (ifaces[0].name, full_hostname): | ||
479 | 406 | HostnameIPMapping( | ||
480 | 407 | node.system_id, 30, {sip2.ip}, node.node_type), | ||
481 | 408 | }, mapping) | ||
482 | 377 | 409 | ||
483 | 378 | def make_mapping(self, node, raw_ttl=False): | 410 | def make_mapping(self, node, raw_ttl=False): |
484 | 379 | if raw_ttl or node.address_ttl is not None: | 411 | if raw_ttl or node.address_ttl is not None: |
485 | @@ -481,12 +513,16 @@ | |||
486 | 481 | ip=factory.pick_ip_in_Subnet(subnet), | 513 | ip=factory.pick_ip_in_Subnet(subnet), |
487 | 482 | subnet=subnet, interface=boot_interface) | 514 | subnet=subnet, interface=boot_interface) |
488 | 483 | newer_nic = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) | 515 | newer_nic = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
490 | 484 | factory.make_StaticIPAddress( | 516 | newer_ip = factory.make_StaticIPAddress( |
491 | 485 | alloc_type=IPADDRESS_TYPE.STICKY, interface=newer_nic) | 517 | alloc_type=IPADDRESS_TYPE.STICKY, interface=newer_nic) |
492 | 486 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | 518 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
493 | 487 | node.domain) | 519 | node.domain) |
496 | 488 | self.assertEqual({node.fqdn: HostnameIPMapping( | 520 | expected_mapping = { |
497 | 489 | node.system_id, 30, {staticip.ip}, node.node_type)}, mapping) | 521 | node.fqdn: HostnameIPMapping( |
498 | 522 | node.system_id, 30, {staticip.ip}, node.node_type), | ||
499 | 523 | "%s.%s" % (newer_nic.name, node.fqdn): HostnameIPMapping( | ||
500 | 524 | node.system_id, 30, {newer_ip.ip}, node.node_type)} | ||
501 | 525 | self.assertEqual(expected_mapping, mapping) | ||
502 | 490 | 526 | ||
503 | 491 | def test_get_hostname_ip_mapping_picks_sticky_over_auto(self): | 527 | def test_get_hostname_ip_mapping_picks_sticky_over_auto(self): |
504 | 492 | subnet = factory.make_Subnet( | 528 | subnet = factory.make_Subnet( |
505 | @@ -500,12 +536,16 @@ | |||
506 | 500 | ip=factory.pick_ip_in_Subnet(subnet), | 536 | ip=factory.pick_ip_in_Subnet(subnet), |
507 | 501 | subnet=subnet, interface=boot_interface) | 537 | subnet=subnet, interface=boot_interface) |
508 | 502 | nic = node.get_boot_interface() | 538 | nic = node.get_boot_interface() |
510 | 503 | factory.make_StaticIPAddress( | 539 | auto_ip = factory.make_StaticIPAddress( |
511 | 504 | alloc_type=IPADDRESS_TYPE.AUTO, interface=nic) | 540 | alloc_type=IPADDRESS_TYPE.AUTO, interface=nic) |
512 | 505 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | 541 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
513 | 506 | node.domain) | 542 | node.domain) |
516 | 507 | self.assertEqual({node.fqdn: HostnameIPMapping( | 543 | expected_mapping = { |
517 | 508 | node.system_id, 30, {staticip.ip}, node.node_type)}, mapping) | 544 | node.fqdn: HostnameIPMapping( |
518 | 545 | node.system_id, 30, {staticip.ip}, node.node_type), | ||
519 | 546 | "%s.%s" % (nic.name, node.fqdn): HostnameIPMapping( | ||
520 | 547 | node.system_id, 30, {auto_ip.ip}, node.node_type)} | ||
521 | 548 | self.assertEqual(expected_mapping, mapping) | ||
522 | 509 | 549 | ||
523 | 510 | def test_get_hostname_ip_mapping_combines_IPv4_and_IPv6_addresses(self): | 550 | def test_get_hostname_ip_mapping_combines_IPv4_and_IPv6_addresses(self): |
524 | 511 | node = factory.make_Node(interface=True, disable_ipv4=False) | 551 | node = factory.make_Node(interface=True, disable_ipv4=False) |
525 | @@ -584,13 +624,17 @@ | |||
526 | 584 | staticip = factory.make_StaticIPAddress( | 624 | staticip = factory.make_StaticIPAddress( |
527 | 585 | alloc_type=IPADDRESS_TYPE.AUTO, interface=iface, | 625 | alloc_type=IPADDRESS_TYPE.AUTO, interface=iface, |
528 | 586 | subnet=subnet) | 626 | subnet=subnet) |
530 | 587 | factory.make_StaticIPAddress( | 627 | discovered = factory.make_StaticIPAddress( |
531 | 588 | alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=iface, | 628 | alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=iface, |
532 | 589 | subnet=subnet) | 629 | subnet=subnet) |
533 | 590 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | 630 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
534 | 591 | node.domain) | 631 | node.domain) |
537 | 592 | self.assertEqual({node.fqdn: HostnameIPMapping( | 632 | expected_mapping = { |
538 | 593 | node.system_id, 30, {staticip.ip}, node.node_type)}, mapping) | 633 | node.fqdn: HostnameIPMapping( |
539 | 634 | node.system_id, 30, {staticip.ip}, node.node_type), | ||
540 | 635 | "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping( | ||
541 | 636 | node.system_id, 30, {discovered.ip}, node.node_type)} | ||
542 | 637 | self.assertEqual(expected_mapping, mapping) | ||
543 | 594 | 638 | ||
544 | 595 | def test_get_hostname_ip_mapping_prefers_bond_with_no_boot_interface(self): | 639 | def test_get_hostname_ip_mapping_prefers_bond_with_no_boot_interface(self): |
545 | 596 | subnet = factory.make_Subnet( | 640 | subnet = factory.make_Subnet( |
546 | @@ -605,7 +649,7 @@ | |||
547 | 605 | iface3 = factory.make_Interface(node=node) | 649 | iface3 = factory.make_Interface(node=node) |
548 | 606 | bondif = factory.make_Interface( | 650 | bondif = factory.make_Interface( |
549 | 607 | INTERFACE_TYPE.BOND, node=node, parents=[iface2, iface3]) | 651 | INTERFACE_TYPE.BOND, node=node, parents=[iface2, iface3]) |
551 | 608 | factory.make_StaticIPAddress( | 652 | iface_ip = factory.make_StaticIPAddress( |
552 | 609 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, | 653 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, |
553 | 610 | subnet=subnet) | 654 | subnet=subnet) |
554 | 611 | factory.make_StaticIPAddress( | 655 | factory.make_StaticIPAddress( |
555 | @@ -619,8 +663,12 @@ | |||
556 | 619 | subnet=subnet) | 663 | subnet=subnet) |
557 | 620 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | 664 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
558 | 621 | node.domain) | 665 | node.domain) |
561 | 622 | self.assertEqual({node.fqdn: HostnameIPMapping( | 666 | expected_mapping = { |
562 | 623 | node.system_id, 30, {bond_staticip.ip}, node.node_type)}, mapping) | 667 | node.fqdn: HostnameIPMapping( |
563 | 668 | node.system_id, 30, {bond_staticip.ip}, node.node_type), | ||
564 | 669 | "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping( | ||
565 | 670 | node.system_id, 30, {iface_ip.ip}, node.node_type)} | ||
566 | 671 | self.assertEqual(expected_mapping, mapping) | ||
567 | 624 | 672 | ||
568 | 625 | def test_get_hostname_ip_mapping_prefers_bond_with_boot_interface(self): | 673 | def test_get_hostname_ip_mapping_prefers_bond_with_boot_interface(self): |
569 | 626 | subnet = factory.make_Subnet( | 674 | subnet = factory.make_Subnet( |
570 | @@ -643,8 +691,11 @@ | |||
571 | 643 | subnet=subnet) | 691 | subnet=subnet) |
572 | 644 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | 692 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
573 | 645 | node.domain) | 693 | node.domain) |
576 | 646 | self.assertEqual({node.fqdn: HostnameIPMapping( | 694 | expected_mapping = { |
577 | 647 | node.system_id, 30, {bond_staticip.ip}, node.node_type)}, mapping) | 695 | node.fqdn: HostnameIPMapping( |
578 | 696 | node.system_id, 30, {bond_staticip.ip}, node.node_type), | ||
579 | 697 | } | ||
580 | 698 | self.assertEqual(expected_mapping, mapping) | ||
581 | 648 | 699 | ||
582 | 649 | def test_get_hostname_ip_mapping_ignores_bond_without_boot_interface(self): | 700 | def test_get_hostname_ip_mapping_ignores_bond_without_boot_interface(self): |
583 | 650 | subnet = factory.make_Subnet( | 701 | subnet = factory.make_Subnet( |
584 | @@ -666,13 +717,17 @@ | |||
585 | 666 | factory.make_StaticIPAddress( | 717 | factory.make_StaticIPAddress( |
586 | 667 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface3, | 718 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface3, |
587 | 668 | subnet=subnet) | 719 | subnet=subnet) |
589 | 669 | factory.make_StaticIPAddress( | 720 | bondip = factory.make_StaticIPAddress( |
590 | 670 | alloc_type=IPADDRESS_TYPE.STICKY, interface=bondif, | 721 | alloc_type=IPADDRESS_TYPE.STICKY, interface=bondif, |
591 | 671 | subnet=subnet) | 722 | subnet=subnet) |
592 | 672 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | 723 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
593 | 673 | node.domain) | 724 | node.domain) |
596 | 674 | self.assertEqual({node.fqdn: HostnameIPMapping( | 725 | expected_mapping = { |
597 | 675 | node.system_id, 30, {boot_staticip.ip}, node.node_type)}, mapping) | 726 | node.fqdn: HostnameIPMapping( |
598 | 727 | node.system_id, 30, {boot_staticip.ip}, node.node_type), | ||
599 | 728 | "%s.%s" % (bondif.name, node.fqdn): HostnameIPMapping( | ||
600 | 729 | node.system_id, 30, {bondip.ip}, node.node_type)} | ||
601 | 730 | self.assertEqual(expected_mapping, mapping) | ||
602 | 676 | 731 | ||
603 | 677 | def test_get_hostname_ip_mapping_prefers_boot_interface(self): | 732 | def test_get_hostname_ip_mapping_prefers_boot_interface(self): |
604 | 678 | subnet = factory.make_Subnet( | 733 | subnet = factory.make_Subnet( |
605 | @@ -681,7 +736,7 @@ | |||
606 | 681 | hostname=factory.make_name('host'), subnet=subnet, | 736 | hostname=factory.make_name('host'), subnet=subnet, |
607 | 682 | disable_ipv4=False) | 737 | disable_ipv4=False) |
608 | 683 | iface = node.get_boot_interface() | 738 | iface = node.get_boot_interface() |
610 | 684 | factory.make_StaticIPAddress( | 739 | iface_ip = factory.make_StaticIPAddress( |
611 | 685 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, | 740 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, |
612 | 686 | subnet=subnet) | 741 | subnet=subnet) |
613 | 687 | new_boot_interface = factory.make_Interface( | 742 | new_boot_interface = factory.make_Interface( |
614 | @@ -694,8 +749,12 @@ | |||
615 | 694 | subnet=subnet) | 749 | subnet=subnet) |
616 | 695 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | 750 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
617 | 696 | node.domain) | 751 | node.domain) |
620 | 697 | self.assertEqual({node.fqdn: HostnameIPMapping( | 752 | expected_mapping = { |
621 | 698 | node.system_id, 30, {boot_sip.ip}, node.node_type)}, mapping) | 753 | node.fqdn: HostnameIPMapping( |
622 | 754 | node.system_id, 30, {boot_sip.ip}, node.node_type), | ||
623 | 755 | "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping( | ||
624 | 756 | node.system_id, 30, {iface_ip.ip}, node.node_type)} | ||
625 | 757 | self.assertEqual(expected_mapping, mapping) | ||
626 | 699 | 758 | ||
627 | 700 | def test_get_hostname_ip_mapping_prefers_boot_interface_to_alias(self): | 759 | def test_get_hostname_ip_mapping_prefers_boot_interface_to_alias(self): |
628 | 701 | subnet = factory.make_Subnet( | 760 | subnet = factory.make_Subnet( |
629 | @@ -704,7 +763,7 @@ | |||
630 | 704 | hostname=factory.make_name('host'), subnet=subnet, | 763 | hostname=factory.make_name('host'), subnet=subnet, |
631 | 705 | disable_ipv4=False) | 764 | disable_ipv4=False) |
632 | 706 | iface = node.get_boot_interface() | 765 | iface = node.get_boot_interface() |
634 | 707 | factory.make_StaticIPAddress( | 766 | iface_ip = factory.make_StaticIPAddress( |
635 | 708 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, | 767 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, |
636 | 709 | subnet=subnet) | 768 | subnet=subnet) |
637 | 710 | new_boot_interface = factory.make_Interface( | 769 | new_boot_interface = factory.make_Interface( |
638 | @@ -717,8 +776,12 @@ | |||
639 | 717 | subnet=subnet) | 776 | subnet=subnet) |
640 | 718 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | 777 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
641 | 719 | node.domain) | 778 | node.domain) |
644 | 720 | self.assertEqual({node.fqdn: HostnameIPMapping( | 779 | expected_mapping = { |
645 | 721 | node.system_id, 30, {boot_sip.ip}, node.node_type)}, mapping) | 780 | node.fqdn: HostnameIPMapping( |
646 | 781 | node.system_id, 30, {boot_sip.ip}, node.node_type), | ||
647 | 782 | "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping( | ||
648 | 783 | node.system_id, 30, {iface_ip.ip}, node.node_type)} | ||
649 | 784 | self.assertEqual(expected_mapping, mapping) | ||
650 | 722 | 785 | ||
651 | 723 | def test_get_hostname_ip_mapping_prefers_physical_interfaces_to_vlan(self): | 786 | def test_get_hostname_ip_mapping_prefers_physical_interfaces_to_vlan(self): |
652 | 724 | subnet = factory.make_Subnet( | 787 | subnet = factory.make_Subnet( |
653 | @@ -732,13 +795,17 @@ | |||
654 | 732 | phy_staticip = factory.make_StaticIPAddress( | 795 | phy_staticip = factory.make_StaticIPAddress( |
655 | 733 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, | 796 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, |
656 | 734 | subnet=subnet) | 797 | subnet=subnet) |
658 | 735 | factory.make_StaticIPAddress( | 798 | vlanip = factory.make_StaticIPAddress( |
659 | 736 | alloc_type=IPADDRESS_TYPE.STICKY, interface=vlanif, | 799 | alloc_type=IPADDRESS_TYPE.STICKY, interface=vlanif, |
660 | 737 | subnet=subnet) | 800 | subnet=subnet) |
661 | 738 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( | 801 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
662 | 739 | node.domain) | 802 | node.domain) |
665 | 740 | self.assertEqual({node.fqdn: HostnameIPMapping( | 803 | expected_mapping = { |
666 | 741 | node.system_id, 30, {phy_staticip.ip}, node.node_type)}, mapping) | 804 | node.fqdn: HostnameIPMapping( |
667 | 805 | node.system_id, 30, {phy_staticip.ip}, node.node_type), | ||
668 | 806 | "%s.%s" % (vlanif.name, node.fqdn): HostnameIPMapping( | ||
669 | 807 | node.system_id, 30, {vlanip.ip}, node.node_type)} | ||
670 | 808 | self.assertEqual(expected_mapping, mapping) | ||
671 | 742 | 809 | ||
672 | 743 | @skip("XXX: GavinPanella 2016-04-27 bug=1556188: Fails spuriously.") | 810 | @skip("XXX: GavinPanella 2016-04-27 bug=1556188: Fails spuriously.") |
673 | 744 | def test_get_hostname_ip_mapping_returns_domain_head_ips(self): | 811 | def test_get_hostname_ip_mapping_returns_domain_head_ips(self): |
674 | @@ -791,8 +858,9 @@ | |||
675 | 791 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain) | 858 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain) |
676 | 792 | expected_mapping = { | 859 | expected_mapping = { |
677 | 793 | node.fqdn: HostnameIPMapping( | 860 | node.fqdn: HostnameIPMapping( |
680 | 794 | node.system_id, 30, {sip0.ip}, node.node_type | 861 | node.system_id, 30, {sip0.ip}, node.node_type), |
681 | 795 | )} | 862 | "%s.%s" % (iface1.name, node.fqdn): HostnameIPMapping( |
682 | 863 | node.system_id, 30, {sip1.ip}, node.node_type)} | ||
683 | 796 | self.assertEqual(expected_mapping, mapping) | 864 | self.assertEqual(expected_mapping, mapping) |
684 | 797 | 865 | ||
685 | 798 | def test_get_hostname_ip_mapping_returns_correct_bond_ip(self): | 866 | def test_get_hostname_ip_mapping_returns_correct_bond_ip(self): |
686 | @@ -838,8 +906,10 @@ | |||
687 | 838 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain) | 906 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain) |
688 | 839 | expected_mapping = { | 907 | expected_mapping = { |
689 | 840 | node.fqdn: HostnameIPMapping( | 908 | node.fqdn: HostnameIPMapping( |
692 | 841 | node.system_id, 30, {bond_sip.ip}, node.node_type | 909 | node.system_id, 30, {bond_sip.ip}, node.node_type), |
693 | 842 | )} | 910 | "%s.%s" % (iface1.name, node.fqdn): HostnameIPMapping( |
694 | 911 | node.system_id, 30, {sip1.ip}, node.node_type), | ||
695 | 912 | } | ||
696 | 843 | self.assertEqual(expected_mapping, mapping) | 913 | self.assertEqual(expected_mapping, mapping) |
697 | 844 | 914 | ||
698 | 845 | 915 |
Simple backport from trunk to 2.0.