Merge ~ack/maas:1902425-again-2.8 into maas:2.8
- Git
- lp:~ack/maas
- 1902425-again-2.8
- Merge into 2.8
Proposed by
Alberto Donato
Status: | Merged |
---|---|
Approved by: | Alberto Donato |
Approved revision: | 54a6395ec631e1b8612b27a986bc7f07436ea9b6 |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~ack/maas:1902425-again-2.8 |
Merge into: | maas:2.8 |
Diff against target: |
413 lines (+154/-62) 4 files modified
src/maasserver/models/interface.py (+7/-20) src/maasserver/models/node.py (+10/-6) src/maasserver/models/tests/test_interface.py (+12/-22) src/maasserver/models/tests/test_node.py (+125/-14) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Lander | Approve | ||
MAAS Maintainers | Pending | ||
Review via email: mp+400090@code.launchpad.net |
Commit message
LP: #1902425 - always create neighbour entries for IPs in use.
Rework Interface.
This also changes IP claim checks to always create a neighbour entry when the IP is found to be in use (even if the rack interface doesn't have neighbour discovery enabled).
Description of the change
To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b 1902425-again-2.8 lp:~ack/maas/+git/maas into -b 2.8 lp:~maas-committers/maas
STATUS: SUCCESS
COMMIT: 54a6395ec631e1b
review:
Approve
There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/models/interface.py b/src/maasserver/models/interface.py |
2 | index 6415344..de96c89 100644 |
3 | --- a/src/maasserver/models/interface.py |
4 | +++ b/src/maasserver/models/interface.py |
5 | @@ -111,7 +111,7 @@ class InterfaceQueriesMixin(MAASQueriesMixin): |
6 | specifiers, |
7 | specifier_types=specifier_types, |
8 | separator=separator, |
9 | - **kwargs |
10 | + **kwargs, |
11 | ) |
12 | |
13 | def _add_interface_id_query(self, current_q, op, item): |
14 | @@ -1522,20 +1522,10 @@ class Interface(CleanSave, TimestampedModel): |
15 | % (self.get_log_string(), vid, vlan.fabric.get_name()) |
16 | ) |
17 | |
18 | - def update_neighbour(self, neighbour_json: dict): |
19 | - """Updates the neighbour table for this interface. |
20 | - |
21 | - Input is expected to be the neighbour JSON from the controller. |
22 | - """ |
23 | - # Circular imports |
24 | + def update_neighbour(self, ip, mac, time, vid=None): |
25 | + """Updates the neighbour table for this interface.""" |
26 | from maasserver.models.neighbour import Neighbour |
27 | |
28 | - if self.neighbour_discovery_state is False: |
29 | - return None |
30 | - ip = neighbour_json["ip"] |
31 | - mac = neighbour_json["mac"] |
32 | - time = neighbour_json["time"] |
33 | - vid = neighbour_json.get("vid", None) |
34 | deleted = Neighbour.objects.delete_and_log_obsolete_neighbours( |
35 | ip, mac, interface=self, vid=vid |
36 | ) |
37 | @@ -1552,13 +1542,10 @@ class Interface(CleanSave, TimestampedModel): |
38 | # generated a log statement about this neighbour. |
39 | if not deleted: |
40 | maaslog.info( |
41 | - "%s: New MAC, IP binding observed%s: %s, %s" |
42 | - % ( |
43 | - self.get_log_string(), |
44 | - Neighbour.objects.get_vid_log_snippet(vid), |
45 | - mac, |
46 | - ip, |
47 | - ) |
48 | + f"{self.get_log_string()}: " |
49 | + "New MAC, IP binding " |
50 | + f"observed{Neighbour.objects.get_vid_log_snippet(vid)}: " |
51 | + f"{mac}, {ip}" |
52 | ) |
53 | else: |
54 | neighbour.time = time |
55 | diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py |
56 | index 3c521be..c8df076 100644 |
57 | --- a/src/maasserver/models/node.py |
58 | +++ b/src/maasserver/models/node.py |
59 | @@ -4413,11 +4413,9 @@ class Node(CleanSave, TimestampedModel): |
60 | rack_interface = rack_interface.order_by("id") |
61 | rack_interface = rack_interface.first() |
62 | rack_interface.update_neighbour( |
63 | - { |
64 | - "ip": ip_obj.ip, |
65 | - "mac": ip_result.get("mac_address"), |
66 | - "time": time.time(), |
67 | - } |
68 | + ip_obj.ip, |
69 | + ip_result.get("mac_address"), |
70 | + time.time(), |
71 | ) |
72 | ip_obj.ip = None |
73 | ip_obj.temp_expires_on = None |
74 | @@ -6728,8 +6726,14 @@ class Controller(Node): |
75 | for neighbour in neighbours: |
76 | interface = interfaces.get(neighbour["interface"], None) |
77 | if interface is not None: |
78 | - interface.update_neighbour(neighbour) |
79 | vid = neighbour.get("vid", None) |
80 | + if interface.neighbour_discovery_state: |
81 | + interface.update_neighbour( |
82 | + neighbour["ip"], |
83 | + neighbour["mac"], |
84 | + neighbour["time"], |
85 | + vid=vid, |
86 | + ) |
87 | if vid is not None: |
88 | interface.report_vid(vid) |
89 | |
90 | diff --git a/src/maasserver/models/tests/test_interface.py b/src/maasserver/models/tests/test_interface.py |
91 | index 7c9ee39..71a5553 100644 |
92 | --- a/src/maasserver/models/tests/test_interface.py |
93 | +++ b/src/maasserver/models/tests/test_interface.py |
94 | @@ -1364,7 +1364,7 @@ class InterfaceTest(MAASServerTestCase): |
95 | self.assertEquals(0, interface.link_speed) |
96 | |
97 | |
98 | -class InterfaceUpdateNeighbourTest(MAASServerTestCase): |
99 | +class TestInterfaceUpdateNeighbour(MAASServerTestCase): |
100 | """Tests for `Interface.update_neighbour`.""" |
101 | |
102 | def make_neighbour_json(self, ip=None, mac=None, time=None, **kwargs): |
103 | @@ -1385,22 +1385,15 @@ class InterfaceUpdateNeighbourTest(MAASServerTestCase): |
104 | vid = None |
105 | return {"ip": ip, "mac": mac, "time": time, "vid": vid} |
106 | |
107 | - def test_ignores_updates_if_neighbour_discovery_state_is_false(self): |
108 | + def test_adds_new_neighbour(self): |
109 | iface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
110 | - iface.update_neighbour(self.make_neighbour_json()) |
111 | - self.assertThat(Neighbour.objects.count(), Equals(0)) |
112 | - |
113 | - def test_adds_new_neighbour_if_neighbour_discovery_state_is_true(self): |
114 | - iface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
115 | - iface.neighbour_discovery_state = True |
116 | - iface.update_neighbour(self.make_neighbour_json()) |
117 | + iface.update_neighbour(**self.make_neighbour_json()) |
118 | self.assertThat(Neighbour.objects.count(), Equals(1)) |
119 | |
120 | def test_updates_existing_neighbour(self): |
121 | iface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
122 | - iface.neighbour_discovery_state = True |
123 | json = self.make_neighbour_json() |
124 | - iface.update_neighbour(json) |
125 | + iface.update_neighbour(**json) |
126 | neighbour = get_one(Neighbour.objects.all()) |
127 | # Pretend this was updated one day ago. |
128 | yesterday = datetime.datetime.now() - datetime.timedelta(days=1) |
129 | @@ -1411,7 +1404,7 @@ class InterfaceUpdateNeighbourTest(MAASServerTestCase): |
130 | Equals(int(yesterday.timestamp())), |
131 | ) |
132 | json["time"] += 1 |
133 | - iface.update_neighbour(json) |
134 | + iface.update_neighbour(**json) |
135 | neighbour = reload_object(neighbour) |
136 | self.assertThat(Neighbour.objects.count(), Equals(1)) |
137 | self.assertThat(neighbour.time, Equals(json["time"])) |
138 | @@ -1423,13 +1416,12 @@ class InterfaceUpdateNeighbourTest(MAASServerTestCase): |
139 | |
140 | def test_replaces_obsolete_neighbour(self): |
141 | iface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
142 | - iface.neighbour_discovery_state = True |
143 | json = self.make_neighbour_json() |
144 | - iface.update_neighbour(json) |
145 | + iface.update_neighbour(**json) |
146 | # Have a different MAC address claim ownership of the IP. |
147 | json["time"] += 1 |
148 | json["mac"] = factory.make_mac_address() |
149 | - iface.update_neighbour(json) |
150 | + iface.update_neighbour(**json) |
151 | self.assertThat(Neighbour.objects.count(), Equals(1)) |
152 | self.assertThat( |
153 | list(Neighbour.objects.all())[0].mac_address, Equals(json["mac"]) |
154 | @@ -1440,10 +1432,8 @@ class InterfaceUpdateNeighbourTest(MAASServerTestCase): |
155 | |
156 | def test_logs_new_binding(self): |
157 | iface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
158 | - iface.neighbour_discovery_state = True |
159 | - json = self.make_neighbour_json() |
160 | with FakeLogger("maas.interface") as maaslog: |
161 | - iface.update_neighbour(json) |
162 | + iface.update_neighbour(**self.make_neighbour_json()) |
163 | self.assertDocTestMatches( |
164 | "...: New MAC, IP binding observed...", maaslog.output |
165 | ) |
166 | @@ -1452,18 +1442,18 @@ class InterfaceUpdateNeighbourTest(MAASServerTestCase): |
167 | iface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
168 | iface.neighbour_discovery_state = True |
169 | json = self.make_neighbour_json() |
170 | - iface.update_neighbour(json) |
171 | + iface.update_neighbour(**json) |
172 | # Have a different MAC address claim ownership of the IP. |
173 | json["time"] += 1 |
174 | json["mac"] = factory.make_mac_address() |
175 | with FakeLogger("maas.neighbour") as maaslog: |
176 | - iface.update_neighbour(json) |
177 | + iface.update_neighbour(**json) |
178 | self.assertDocTestMatches( |
179 | "...: IP address...moved from...to...", maaslog.output |
180 | ) |
181 | |
182 | |
183 | -class InterfaceUpdateMDNSEntryTest(MAASServerTestCase): |
184 | +class TestInterfaceUpdateMDNSEntry(MAASServerTestCase): |
185 | """Tests for `Interface.update_mdns_entry`.""" |
186 | |
187 | def make_mdns_entry_json(self, ip=None, hostname=None): |
188 | @@ -1478,7 +1468,7 @@ class InterfaceUpdateMDNSEntryTest(MAASServerTestCase): |
189 | |
190 | def test_ignores_updates_if_mdns_discovery_state_is_false(self): |
191 | iface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
192 | - iface.update_neighbour(self.make_mdns_entry_json()) |
193 | + iface.update_mdns_entry(self.make_mdns_entry_json()) |
194 | self.assertThat(MDNS.objects.count(), Equals(0)) |
195 | |
196 | def test_adds_new_entry_if_mdns_discovery_state_is_true(self): |
197 | diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py |
198 | index b9ca303..583478c 100644 |
199 | --- a/src/maasserver/models/tests/test_node.py |
200 | +++ b/src/maasserver/models/tests/test_node.py |
201 | @@ -80,6 +80,7 @@ from maasserver.exceptions import ( |
202 | StaticIPAddressExhaustion, |
203 | ) |
204 | from maasserver.models import ( |
205 | + BMCRoutableRackControllerRelationship, |
206 | BondInterface, |
207 | BootResource, |
208 | BridgeInterface, |
209 | @@ -87,11 +88,13 @@ from maasserver.models import ( |
210 | Controller, |
211 | Device, |
212 | Domain, |
213 | + Event, |
214 | EventType, |
215 | Fabric, |
216 | Interface, |
217 | LicenseKey, |
218 | Machine, |
219 | + Neighbour, |
220 | Node, |
221 | ) |
222 | from maasserver.models import ( |
223 | @@ -101,6 +104,7 @@ from maasserver.models import ( |
224 | RAID, |
225 | RegionController, |
226 | RegionRackRPCConnection, |
227 | + ResourcePool, |
228 | Service, |
229 | StaticIPAddress, |
230 | Subnet, |
231 | @@ -109,12 +113,10 @@ from maasserver.models import ( |
232 | VLANInterface, |
233 | VolumeGroup, |
234 | ) |
235 | -from maasserver.models import Bcache |
236 | +from maasserver.models import Bcache, BMC |
237 | from maasserver.models import bmc as bmc_module |
238 | from maasserver.models import node as node_module |
239 | -from maasserver.models.bmc import BMC, BMCRoutableRackControllerRelationship |
240 | from maasserver.models.config import NetworkDiscoveryConfig |
241 | -from maasserver.models.event import Event |
242 | import maasserver.models.interface as interface_module |
243 | from maasserver.models.node import ( |
244 | DefaultGateways, |
245 | @@ -123,7 +125,6 @@ from maasserver.models.node import ( |
246 | PowerInfo, |
247 | ) |
248 | from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE |
249 | -from maasserver.models.resourcepool import ResourcePool |
250 | from maasserver.models.signals import power as node_query |
251 | from maasserver.models.timestampedmodel import now |
252 | from maasserver.node_status import ( |
253 | @@ -8593,6 +8594,74 @@ class TestNode_Start(MAASTransactionServerTestCase): |
254 | self.assertThat(auto_ip.ip, Equals(third_ip.ip)) |
255 | self.assertThat(auto_ip.temp_expires_on, Is(None)) |
256 | |
257 | + def test_claims_auto_ip_addresses_skips_used_ip_discovery_disabled(self): |
258 | + user = factory.make_User() |
259 | + node = self.make_acquired_node_with_interface( |
260 | + user, power_type="manual" |
261 | + ) |
262 | + node_interface = node.get_boot_interface() |
263 | + [auto_ip] = node_interface.ip_addresses.filter( |
264 | + alloc_type=IPADDRESS_TYPE.AUTO |
265 | + ) |
266 | + |
267 | + # Create a rack controller that has an interface on the same subnet |
268 | + # as the node. Don't enable neighbour discovery |
269 | + rack = factory.make_RackController() |
270 | + rack.interface_set.all().delete() |
271 | + rackif = factory.make_Interface(vlan=node_interface.vlan, node=rack) |
272 | + rackif_ip = factory.pick_ip_in_Subnet(auto_ip.subnet) |
273 | + rackif.link_subnet( |
274 | + INTERFACE_LINK_TYPE.STATIC, auto_ip.subnet, rackif_ip |
275 | + ) |
276 | + |
277 | + # Mock the rack controller connected to the region controller. |
278 | + client = Mock() |
279 | + client.ident = rack.system_id |
280 | + self.patch(node_module, "getAllClients").return_value = [client] |
281 | + |
282 | + # Must be executed in a transaction as `allocate_new` uses savepoints. |
283 | + with transaction.atomic(): |
284 | + # Get two IPs and remove them so they're unknown |
285 | + ip = StaticIPAddress.objects.allocate_new( |
286 | + subnet=auto_ip.subnet, alloc_type=IPADDRESS_TYPE.AUTO |
287 | + ) |
288 | + ip1 = ip.ip |
289 | + ip.delete() |
290 | + ip = StaticIPAddress.objects.allocate_new( |
291 | + subnet=auto_ip.subnet, |
292 | + alloc_type=IPADDRESS_TYPE.AUTO, |
293 | + exclude_addresses=[ip1], |
294 | + ) |
295 | + ip2 = ip.ip |
296 | + ip.delete() |
297 | + |
298 | + client.side_effect = [ |
299 | + defer.succeed( |
300 | + { |
301 | + "ip_addresses": [ |
302 | + { |
303 | + "ip_address": ip1, |
304 | + "used": True, |
305 | + "mac_address": factory.make_mac_address(), |
306 | + } |
307 | + ] |
308 | + } |
309 | + ), |
310 | + defer.succeed( |
311 | + {"ip_addresses": [{"ip_address": ip2, "used": False}]} |
312 | + ), |
313 | + ] |
314 | + |
315 | + with post_commit_hooks: |
316 | + node.start(user) |
317 | + |
318 | + auto_ip = reload_object(auto_ip) |
319 | + self.assertThat(auto_ip.ip, Equals(ip2)) |
320 | + self.assertThat(auto_ip.temp_expires_on, Is(None)) |
321 | + self.assertCountEqual( |
322 | + [ip1], Neighbour.objects.values_list("ip", flat=True) |
323 | + ) |
324 | + |
325 | def test_claims_auto_ip_addresses_retries_on_failure_from_rack(self): |
326 | user = factory.make_User() |
327 | node = self.make_acquired_node_with_interface( |
328 | @@ -9790,21 +9859,51 @@ class TestControllerUpdateDiscoveryState(MAASServerTestCase): |
329 | class TestReportNeighbours(MAASServerTestCase): |
330 | """Tests for `Controller.report_neighbours().""" |
331 | |
332 | - def test_calls_update_neighbour_for_each_neighbour(self): |
333 | + def test_no_update_neighbours_calls_if_discovery_disabled(self): |
334 | rack = factory.make_RackController() |
335 | factory.make_Interface(name="eth0", node=rack) |
336 | - factory.make_Interface(name="eth1", node=rack) |
337 | update_neighbour = self.patch( |
338 | interface_module.Interface, "update_neighbour" |
339 | ) |
340 | neighbours = [ |
341 | - {"interface": "eth0", "mac": factory.make_mac_address()}, |
342 | - {"interface": "eth1", "mac": factory.make_mac_address()}, |
343 | + { |
344 | + "interface": "eth0", |
345 | + "mac": factory.make_mac_address(), |
346 | + "ip": factory.make_ipv4_address(), |
347 | + "time": datetime.now(), |
348 | + } |
349 | + ] |
350 | + rack.report_neighbours(neighbours) |
351 | + update_neighbour.assert_not_called() |
352 | + |
353 | + def test_calls_update_neighbour_for_each_neighbour(self): |
354 | + rack = factory.make_RackController() |
355 | + if1 = factory.make_Interface(name="eth0", node=rack) |
356 | + if1.neighbour_discovery_state = True |
357 | + if1.save() |
358 | + if2 = factory.make_Interface(name="eth1", node=rack) |
359 | + if2.neighbour_discovery_state = True |
360 | + if2.save() |
361 | + update_neighbour = self.patch( |
362 | + interface_module.Interface, "update_neighbour" |
363 | + ) |
364 | + neighbours = [ |
365 | + { |
366 | + "interface": "eth0", |
367 | + "mac": factory.make_mac_address(), |
368 | + "ip": factory.make_ipv4_address(), |
369 | + "time": datetime.now(), |
370 | + }, |
371 | + { |
372 | + "interface": "eth1", |
373 | + "mac": factory.make_mac_address(), |
374 | + "ip": factory.make_ipv4_address(), |
375 | + "time": datetime.now(), |
376 | + }, |
377 | ] |
378 | rack.report_neighbours(neighbours) |
379 | - self.assertThat( |
380 | - update_neighbour, |
381 | - MockCallsMatch(*[call(neighbour) for neighbour in neighbours]), |
382 | + update_neighbour.assert_has_calls( |
383 | + [call(n["ip"], n["mac"], n["time"], vid=None) for n in neighbours] |
384 | ) |
385 | |
386 | def test_calls_report_vid_for_each_vid(self): |
387 | @@ -9815,11 +9914,23 @@ class TestReportNeighbours(MAASServerTestCase): |
388 | self.patch(interface_module.Interface, "update_neighbour") |
389 | report_vid = self.patch(interface_module.Interface, "report_vid") |
390 | neighbours = [ |
391 | - {"interface": "eth0", "mac": factory.make_mac_address(), "vid": 3}, |
392 | - {"interface": "eth1", "mac": factory.make_mac_address(), "vid": 7}, |
393 | + { |
394 | + "interface": "eth0", |
395 | + "ip": factory.make_ipv4_address(), |
396 | + "time": datetime.now(), |
397 | + "mac": factory.make_mac_address(), |
398 | + "vid": 3, |
399 | + }, |
400 | + { |
401 | + "interface": "eth1", |
402 | + "ip": factory.make_ipv4_address(), |
403 | + "time": datetime.now(), |
404 | + "mac": factory.make_mac_address(), |
405 | + "vid": 7, |
406 | + }, |
407 | ] |
408 | rack.report_neighbours(neighbours) |
409 | - self.assertThat(report_vid, MockCallsMatch(call(3), call(7))) |
410 | + report_vid.assert_has_calls([call(3), call(7)]) |
411 | |
412 | |
413 | class TestReportMDNSEntries(MAASServerTestCase): |
UNIT TESTS
-b 1902425-again-2.8 lp:~ack/maas/+git/maas into -b 2.8 lp:~maas-committers/maas
STATUS: FAILED maas-ci. internal: 8080/job/ maas/job/ branch- tester/ 9590/console 6ccc1512f0033ca 2dce511d2c
LOG: http://
COMMIT: be9256f379f35aa