Merge ~bjornt/maas:bug-1988229-dhcp-snippet-relay into maas:master

Proposed by Björn Tillenius
Status: Merged
Approved by: Björn Tillenius
Approved revision: ae560acae5628832e7f855c69177add56ac7f357
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~bjornt/maas:bug-1988229-dhcp-snippet-relay
Merge into: maas:master
Diff against target: 266 lines (+132/-34)
6 files modified
src/maasserver/dhcp.py (+15/-13)
src/maasserver/models/dhcpsnippet.py (+4/-0)
src/maasserver/models/node.py (+10/-12)
src/maasserver/models/subnet.py (+6/-8)
src/maasserver/models/tests/test_dhcpsnippet.py (+14/-0)
src/maasserver/tests/test_dhcp.py (+83/-1)
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
MAAS Lander Approve
Review via email: mp+432311@code.launchpad.net

Commit message

LP #1988229: dhcp snippet create fails when dhcp subnet is relayed regression

Extract logic to make it easier to test, and make use of get_dhcp_vlan, which
knows about dhcp relaying.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug-1988229-dhcp-snippet-relay lp:~bjornt/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 1b2b5b2b54024701278262d834b6d2eb42e74ae8

review: Approve
Revision history for this message
Alberto Donato (ack) wrote :

+1

review: Approve
Revision history for this message
Björn Tillenius (bjornt) :
ae560ac... by Björn Tillenius

Rename parameter.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/dhcp.py b/src/maasserver/dhcp.py
2index ccbe8f3..32ad98a 100644
3--- a/src/maasserver/dhcp.py
4+++ b/src/maasserver/dhcp.py
5@@ -977,6 +977,13 @@ def get_racks_by_subnet(subnet):
6 return racks
7
8
9+def _get_dhcp_rackcontrollers(dhcp_snippet):
10+ if dhcp_snippet.subnet is not None:
11+ return get_racks_by_subnet(dhcp_snippet.subnet)
12+ elif dhcp_snippet.node is not None:
13+ return dhcp_snippet.node.get_boot_rack_controllers()
14+
15+
16 def validate_dhcp_config(test_dhcp_snippet=None):
17 """Validate a DHCPD config with uncommitted values.
18
19@@ -987,9 +994,6 @@ def validate_dhcp_config(test_dhcp_snippet=None):
20 :param test_dhcp_snippet: A DHCPSnippet which has not yet been committed to
21 the database and needs to be validated.
22 """
23- # XXX ltrager 2016-03-28 - This only tests the existing config with new
24- # DHCPSnippets but could be expanded to test changes to the config(e.g
25- # subnets, omapi_key, interfaces, etc) before they are commited.
26
27 def find_connected_rack(racks):
28 connected_racks = [client.ident for client in getAllClients()]
29@@ -1006,17 +1010,15 @@ def validate_dhcp_config(test_dhcp_snippet=None):
30 "no available rack controller connected."
31 )
32
33- rack_controller = None
34+ # XXX ltrager 2016-03-28 - This only tests the existing config with new
35+ # DHCPSnippets but could be expanded to test changes to the config(e.g
36+ # subnets, omapi_key, interfaces, etc) before they are commited.
37 # Test on the rack controller where the DHCPSnippet will be used
38- if test_dhcp_snippet is not None:
39- if test_dhcp_snippet.subnet is not None:
40- rack_controller = find_connected_rack(
41- get_racks_by_subnet(test_dhcp_snippet.subnet)
42- )
43- elif test_dhcp_snippet.node is not None:
44- rack_controller = find_connected_rack(
45- test_dhcp_snippet.node.get_boot_rack_controllers()
46- )
47+ rack_controller = None
48+ if test_dhcp_snippet is not None and not test_dhcp_snippet.is_global:
49+ rack_controller = find_connected_rack(
50+ _get_dhcp_rackcontrollers(test_dhcp_snippet)
51+ )
52 # If no rack controller is linked to the DHCPSnippet its a global DHCP
53 # snippet which we can test anywhere.
54 if rack_controller is None:
55diff --git a/src/maasserver/models/dhcpsnippet.py b/src/maasserver/models/dhcpsnippet.py
56index 7a401d7..054ccce 100644
57--- a/src/maasserver/models/dhcpsnippet.py
58+++ b/src/maasserver/models/dhcpsnippet.py
59@@ -93,6 +93,10 @@ class DHCPSnippet(CleanSave, TimestampedModel):
60 def __str__(self):
61 return self.name
62
63+ @property
64+ def is_global(self):
65+ return self.node_id is None and self.subnet_id is None
66+
67 def clean(self, *args, **kwargs):
68 super().clean(*args, **kwargs)
69 if self.node is not None and self.subnet is not None:
70diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
71index 06e80f8..ba2f710 100644
72--- a/src/maasserver/models/node.py
73+++ b/src/maasserver/models/node.py
74@@ -130,7 +130,7 @@ from maasserver.models.physicalblockdevice import PhysicalBlockDevice
75 from maasserver.models.resourcepool import ResourcePool
76 from maasserver.models.service import Service
77 from maasserver.models.staticipaddress import StaticIPAddress
78-from maasserver.models.subnet import Subnet
79+from maasserver.models.subnet import get_dhcp_vlan, Subnet
80 from maasserver.models.tag import Tag
81 from maasserver.models.timestampedmodel import now, TimestampedModel
82 from maasserver.models.vlan import VLAN
83@@ -5360,18 +5360,16 @@ class Node(CleanSave, TimestampedModel):
84 def get_boot_rack_controllers(self):
85 """Return the `RackController` that this node will boot from."""
86 boot_interface = self.get_boot_interface()
87- if (
88- boot_interface is None
89- or boot_interface.vlan is None
90- or not boot_interface.vlan.dhcp_on
91- ):
92+ if boot_interface is None:
93 return []
94- else:
95- racks = [
96- boot_interface.vlan.primary_rack,
97- boot_interface.vlan.secondary_rack,
98- ]
99- return [rack for rack in racks if rack is not None]
100+
101+ boot_vlan = get_dhcp_vlan(boot_interface.vlan)
102+ if boot_vlan is None:
103+ return []
104+ racks = [boot_vlan.primary_rack]
105+ if boot_vlan.secondary_rack:
106+ racks.append(boot_vlan.secondary_rack)
107+ return racks
108
109 def get_extra_macs(self):
110 """Get the MACs other that the one the node booted from."""
111diff --git a/src/maasserver/models/subnet.py b/src/maasserver/models/subnet.py
112index 924aefa..870d4eb 100644
113--- a/src/maasserver/models/subnet.py
114+++ b/src/maasserver/models/subnet.py
115@@ -1065,14 +1065,10 @@ def get_allocated_ips(subnets):
116 yield subnet, mapping[subnet.id]
117
118
119-def get_dhcp_vlan(subnet):
120- if subnet is None:
121+def get_dhcp_vlan(vlan):
122+ if vlan is None:
123 return None
124- dhcp_vlan = (
125- subnet.vlan
126- if subnet.vlan.relay_vlan_id is None
127- else subnet.vlan.relay_vlan
128- )
129+ dhcp_vlan = vlan if vlan.relay_vlan_id is None else vlan.relay_vlan
130 if not dhcp_vlan.dhcp_on or dhcp_vlan.primary_rack is None:
131 return None
132 return dhcp_vlan
133@@ -1104,7 +1100,9 @@ def get_boot_rackcontroller_ips(subnet):
134
135 from maasserver.models.staticipaddress import StaticIPAddress
136
137- dhcp_vlan = get_dhcp_vlan(subnet)
138+ dhcp_vlan = None
139+ if subnet is not None:
140+ dhcp_vlan = get_dhcp_vlan(subnet.vlan)
141 if dhcp_vlan is None:
142 return []
143
144diff --git a/src/maasserver/models/tests/test_dhcpsnippet.py b/src/maasserver/models/tests/test_dhcpsnippet.py
145index acefc80..465f445 100644
146--- a/src/maasserver/models/tests/test_dhcpsnippet.py
147+++ b/src/maasserver/models/tests/test_dhcpsnippet.py
148@@ -138,3 +138,17 @@ class TestDHCPSnippet(MAASServerTestCase):
149 VersionedTextFile.objects.get,
150 id=i,
151 )
152+
153+ def test_is_global(self):
154+ snippet = factory.make_DHCPSnippet(subnet=None, node=None)
155+ self.assertTrue(snippet.is_global)
156+
157+ def test_is_global_node(self):
158+ node = factory.make_Machine()
159+ snippet = factory.make_DHCPSnippet(subnet=None, node=node)
160+ self.assertFalse(snippet.is_global)
161+
162+ def test_is_global_subnet(self):
163+ subnet = factory.make_Subnet()
164+ snippet = factory.make_DHCPSnippet(subnet=subnet, node=None)
165+ self.assertFalse(snippet.is_global)
166diff --git a/src/maasserver/tests/test_dhcp.py b/src/maasserver/tests/test_dhcp.py
167index 58bee72..e9d0cfa 100644
168--- a/src/maasserver/tests/test_dhcp.py
169+++ b/src/maasserver/tests/test_dhcp.py
170@@ -25,7 +25,7 @@ from twisted.internet.threads import deferToThread
171
172 from maasserver import dhcp
173 from maasserver import server_address as server_address_module
174-from maasserver.dhcp import get_default_dns_servers
175+from maasserver.dhcp import _get_dhcp_rackcontrollers, get_default_dns_servers
176 from maasserver.enum import INTERFACE_TYPE, IPADDRESS_TYPE, SERVICE_STATUS
177 from maasserver.models import (
178 Config,
179@@ -2923,6 +2923,88 @@ class TestConfigureDHCP(MAASTransactionServerTestCase):
180 yield deferToDatabase(service_status_updated)
181
182
183+class TestGetDHCPRackcontroller(MAASTransactionServerTestCase):
184+ def create_dhcp_vlan(self, primary_address, secondary_adress=None):
185+ dhcp_vlan = factory.make_VLAN()
186+ dhcp_subnet = factory.make_Subnet(
187+ vlan=dhcp_vlan, cidr=IPNetwork(primary_address).cidr
188+ )
189+ primary_rack = factory.make_rack_with_interfaces(
190+ eth0=[primary_address]
191+ )
192+ secondary_rack = None
193+ if secondary_adress is not None:
194+ secondary_rack = factory.make_rack_with_interfaces(
195+ eth0=[secondary_adress]
196+ )
197+ dhcp_vlan.dhcp_on = True
198+ dhcp_vlan.primary_rack = primary_rack
199+ dhcp_vlan.secondary_rack = secondary_rack
200+ dhcp_vlan.save()
201+ return dhcp_subnet
202+
203+ def test_subnet(self):
204+ subnet = self.create_dhcp_vlan("10.10.10.2/24")
205+ self.create_dhcp_vlan("10.10.20.2/24")
206+ snippet = factory.make_DHCPSnippet(subnet=subnet, enabled=True)
207+ self.assertCountEqual(
208+ [subnet.vlan.primary_rack], _get_dhcp_rackcontrollers(snippet)
209+ )
210+
211+ def test_subnet_includes_secondary(self):
212+ subnet = self.create_dhcp_vlan("10.10.10.2/24", "10.10.10.3/24")
213+ self.create_dhcp_vlan("10.10.20.0/24")
214+ snippet = factory.make_DHCPSnippet(subnet=subnet, enabled=True)
215+ self.assertCountEqual(
216+ [subnet.vlan.primary_rack, subnet.vlan.secondary_rack],
217+ _get_dhcp_rackcontrollers(snippet),
218+ )
219+
220+ def test_subnet_relay(self):
221+ dhcp_subnet = self.create_dhcp_vlan("10.10.10.2/24")
222+ self.create_dhcp_vlan("10.10.20.2/24")
223+ relay_subnet = factory.make_Subnet(cidr="10.10.30.0/24")
224+ relay_subnet.vlan.relay_vlan = dhcp_subnet.vlan
225+ relay_subnet.vlan.save()
226+ snippet = factory.make_DHCPSnippet(subnet=relay_subnet, enabled=True)
227+ self.assertCountEqual(
228+ [dhcp_subnet.vlan.primary_rack], _get_dhcp_rackcontrollers(snippet)
229+ )
230+
231+ def test_node(self):
232+ subnet = self.create_dhcp_vlan("10.10.10.2/24")
233+ self.create_dhcp_vlan("10.10.20.0/24")
234+ node = factory.make_Machine_with_Interface_on_Subnet(subnet=subnet)
235+ snippet = factory.make_DHCPSnippet(node=node, enabled=True)
236+ self.assertCountEqual(
237+ [subnet.vlan.primary_rack], _get_dhcp_rackcontrollers(snippet)
238+ )
239+
240+ def test_node_includes_secondary(self):
241+ subnet = self.create_dhcp_vlan("10.10.10.2/24", "10.10.10.3/24")
242+ self.create_dhcp_vlan("10.10.20.0/24")
243+ node = factory.make_Machine_with_Interface_on_Subnet(subnet=subnet)
244+ snippet = factory.make_DHCPSnippet(node=node, enabled=True)
245+ self.assertCountEqual(
246+ [subnet.vlan.primary_rack, subnet.vlan.secondary_rack],
247+ _get_dhcp_rackcontrollers(snippet),
248+ )
249+
250+ def test_node_relay(self):
251+ dhcp_subnet = self.create_dhcp_vlan("10.10.10.2/24")
252+ self.create_dhcp_vlan("10.10.20.2/24")
253+ relay_subnet = factory.make_Subnet(cidr="10.10.30.0/24")
254+ relay_subnet.vlan.relay_vlan = dhcp_subnet.vlan
255+ relay_subnet.vlan.save()
256+ node = factory.make_Machine_with_Interface_on_Subnet(
257+ subnet=relay_subnet
258+ )
259+ snippet = factory.make_DHCPSnippet(node=node, enabled=True)
260+ self.assertCountEqual(
261+ [dhcp_subnet.vlan.primary_rack], _get_dhcp_rackcontrollers(snippet)
262+ )
263+
264+
265 class TestValidateDHCPConfig(MAASTransactionServerTestCase):
266 """Tests for `validate_dhcp_config`."""
267

Subscribers

People subscribed via source and target branches