Merge ~mpontillo/maas:allow-deferred-fabric-creation into maas:master
- Git
- lp:~mpontillo/maas
- allow-deferred-fabric-creation
- Merge into master
Status: | Merged |
---|---|
Approved by: | Mike Pontillo |
Approved revision: | c1894207469aa94ad8fa17a6af19a6d80ab155cb |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~mpontillo/maas:allow-deferred-fabric-creation |
Merge into: | maas:master |
Diff against target: |
770 lines (+212/-121) 5 files modified
src/maasserver/models/node.py (+64/-33) src/maasserver/models/tests/test_node.py (+83/-48) src/maasserver/rpc/rackcontrollers.py (+3/-3) src/maasserver/rpc/regionservice.py (+61/-36) src/provisioningserver/rpc/region.py (+1/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Review via email: mp+326903@code.launchpad.net |
Commit message
Refactor rack registration to allow deferred fabric creation.
This allows update_interfaces() to be called in two modes: one which
creates all fabrics immediately (the current behavior), and the other
which only creates 'disconnected' physical interfaces and skips child
interface creation and deletion.
This work is necessary so that in the future, a rack can register,
perform beaconing, and update its interfaces with any additional
topology data the beacons can provide.
Also updates all test scenarios for update_interfaces() to make sure
they work with the old 'one phase' approach and the new 'two phase'
approach. (That is, the interfaces are created in exactly the same
way even if fabric creation is deferred.)
Description of the change
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b allow-deferred-
STATUS: FAILED MERGE
LOG: http://
Mike Pontillo (mpontillo) wrote : | # |
Not sure why this failed to merge. There were no conflicts. Trying again.
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b allow-deferred-
STATUS: FAILED BUILD
LOG: http://
Mike Pontillo (mpontillo) wrote : | # |
Strange failure:
http://
I wasn't able to replicate this:
$ bin/test.region metadataserver.
.......
-------
Ran 100 tests in 24.142s
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b allow-deferred-
STATUS: FAILED MERGE
LOG: http://
Mike Pontillo (mpontillo) wrote : | # |
Another weird lander failure.
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b allow-deferred-
STATUS: FAILED MERGE
LOG: http://
Mike Pontillo (mpontillo) wrote : | # |
Looks like the lander is messing up the git command line when trying to merge this branch:
[master] + git commit -a -m Refactor rack registration to allow deferred fabric creation.
[master]
[master] This allows update_interfaces() to be called in two modes: one which
[master] creates all fabrics immediately (the current behavior), and the other
[master] which only creates disconnected physical interfaces and skips child
[master] interface creation and deletion.
[master]
[master] This work is necessary so that in the future, a rack can register,
[master] perform beaconing, and update its interfaces with any additional
[master] topology data the beacons can provide.
[master]
[master] Also updates all test scenarios for update_interfaces() to make sure
[master] they work with the old one phase approach and the new two phase
[master] approach. (That is, the interfaces are created in exactly the same
[master] way even if fabric creation is deferred.)
[master] fatal: Paths with -a does not make sense.
Mike Pontillo (mpontillo) wrote : | # |
The problem was double-quotes in the commit message. Changing them to single quotes fixed the issue.
Preview Diff
1 | diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py |
2 | index 3166bee..74028a2 100644 |
3 | --- a/src/maasserver/models/node.py |
4 | +++ b/src/maasserver/models/node.py |
5 | @@ -1312,7 +1312,7 @@ class Node(CleanSave, TimestampedModel): |
6 | """Mark a node as being deployed.""" |
7 | # Avoid circular dependencies |
8 | from metadataserver.models import ScriptSet |
9 | - if self.on_network() is False: |
10 | + if not self.on_network(): |
11 | raise ValidationError( |
12 | {"network": |
13 | ["Node must be configured to use a network"]}) |
14 | @@ -4108,7 +4108,7 @@ class Controller(Node): |
15 | machine must have power information.""" |
16 | return self.status == NODE_STATUS.DEPLOYED and self.bmc is not None |
17 | |
18 | - def _update_interface(self, name, config): |
19 | + def _update_interface(self, name, config, create_fabrics=True): |
20 | """Update a interface. |
21 | |
22 | :param name: Name of the interface. |
23 | @@ -4116,7 +4116,11 @@ class Controller(Node): |
24 | /etc/network/interfaces on the rack controller. |
25 | """ |
26 | if config["type"] == "physical": |
27 | - return self._update_physical_interface(name, config) |
28 | + return self._update_physical_interface( |
29 | + name, config, create_fabrics=create_fabrics) |
30 | + elif not create_fabrics: |
31 | + # Defer child interface creation until fabrics are known. |
32 | + return None |
33 | elif config["type"] == "vlan": |
34 | return self._update_vlan_interface(name, config) |
35 | elif config["type"] == "bond": |
36 | @@ -4128,7 +4132,7 @@ class Controller(Node): |
37 | "Unkwown interface type '%s' for '%s'." % ( |
38 | config["type"], name)) |
39 | |
40 | - def _update_physical_interface(self, name, config): |
41 | + def _update_physical_interface(self, name, config, create_fabrics=True): |
42 | """Update a physical interface. |
43 | |
44 | :param name: Name of the interface. |
45 | @@ -4137,57 +4141,75 @@ class Controller(Node): |
46 | """ |
47 | new_vlan = None |
48 | mac_address = config["mac_address"] |
49 | + update_fields = set() |
50 | + is_enabled = config['enabled'] |
51 | interface, created = PhysicalInterface.objects.get_or_create( |
52 | mac_address=mac_address, defaults={ |
53 | "node": self, |
54 | "name": name, |
55 | - "enabled": config["enabled"], |
56 | + "enabled": is_enabled, |
57 | }) |
58 | - if created: |
59 | - # Make sure that the VLAN on the interface is correct. When |
60 | - # links exists on this interface we place it into the correct |
61 | - # VLAN. If it cannot be determined and its a new interface it |
62 | - # gets placed on its own fabric. |
63 | - connected_to_subnets = self._get_connected_subnets( |
64 | - config["links"]) |
65 | - if len(connected_to_subnets) == 0: |
66 | - # Not connected to any known subnets. We add it to its own |
67 | - # fabric unless this is the very first interface on this |
68 | - # rack controller. The first interface always goes on |
69 | - # the default VLAN on the default fabric. |
70 | - if Interface.objects.filter(node=self).count() > 1: |
71 | - new_fabric = Fabric.objects.create() |
72 | - new_vlan = new_fabric.get_default_vlan() |
73 | - interface.vlan = new_vlan |
74 | - interface.save() |
75 | - else: |
76 | - interface.vlan = ( |
77 | - Fabric.objects.get_default_fabric().get_default_vlan()) |
78 | - interface.save() |
79 | - else: |
80 | + if create_fabrics and interface.vlan is None: |
81 | + new_vlan = self._update_vlan_for_interface( |
82 | + interface, config, new_vlan) |
83 | + if not created: |
84 | if interface.node.id != self.id: |
85 | # MAC address was on a different node. We need to move |
86 | # it to its new owner. In the process we delete all of its |
87 | # current links because they are completely wrong. |
88 | interface.ip_addresses.all().delete() |
89 | interface.node = self |
90 | + update_fields.add('node') |
91 | interface.name = name |
92 | - interface.enabled = config["enabled"] |
93 | - interface.save() |
94 | + update_fields.add('name') |
95 | + if interface.enabled != is_enabled: |
96 | + interface.enabled = is_enabled |
97 | + update_fields.add('enabled') |
98 | |
99 | # Update all the IP address on this interface. Fix the VLAN the |
100 | # interface belongs to so its the same as the links. |
101 | + if create_fabrics: |
102 | + self._update_physical_links( |
103 | + interface, config, new_vlan, update_fields) |
104 | + if len(update_fields) > 0: |
105 | + interface.save(update_fields=list(update_fields)) |
106 | + return interface |
107 | + |
108 | + def _update_physical_links(self, interface, config, new_vlan, |
109 | + update_fields): |
110 | update_ip_addresses = self._update_links(interface, config["links"]) |
111 | linked_vlan = self._get_first_sticky_vlan_from_ip_addresses( |
112 | update_ip_addresses) |
113 | if linked_vlan is not None: |
114 | interface.vlan = linked_vlan |
115 | - interface.save() |
116 | + update_fields.add('vlan') |
117 | if new_vlan is not None and linked_vlan.id != new_vlan.id: |
118 | # Create a new VLAN for this interface and it was not used as |
119 | # a link re-assigned the VLAN this interface is connected to. |
120 | new_vlan.fabric.delete() |
121 | - return interface |
122 | + |
123 | + def _update_vlan_for_interface(self, interface, config, new_vlan): |
124 | + # Make sure that the VLAN on the interface is correct. When |
125 | + # links exists on this interface we place it into the correct |
126 | + # VLAN. If it cannot be determined and its a new interface it |
127 | + # gets placed on its own fabric. |
128 | + connected_to_subnets = self._get_connected_subnets( |
129 | + config["links"]) |
130 | + if len(connected_to_subnets) == 0: |
131 | + # If the default VLAN on the default fabric has no interfaces |
132 | + # associated with it, the first interface will be placed there |
133 | + # (rather than creating a new fabric). |
134 | + default_vlan = VLAN.objects.get_default_vlan() |
135 | + interfaces_on_default_vlan = Interface.objects.filter( |
136 | + vlan=default_vlan).count() |
137 | + if interfaces_on_default_vlan == 0: |
138 | + new_vlan = default_vlan |
139 | + else: |
140 | + new_fabric = Fabric.objects.create() |
141 | + new_vlan = new_fabric.get_default_vlan() |
142 | + interface.vlan = new_vlan |
143 | + interface.save() |
144 | + return new_vlan |
145 | |
146 | def _get_or_create_vlan_interface(self, name, vlan, parent): |
147 | """Wrapper to get_or_create for VLAN interfaces. |
148 | @@ -4598,11 +4620,14 @@ class Controller(Node): |
149 | for interface in interfaces |
150 | } |
151 | |
152 | - def update_interfaces(self, interfaces): |
153 | + def update_interfaces(self, interfaces, create_fabrics=True): |
154 | """Update the interfaces attached to the controller. |
155 | |
156 | :param interfaces: Interfaces dictionary that was parsed from |
157 | /etc/network/interfaces on the controller. |
158 | + :param create_fabrics: If True, creates fabrics associated with each |
159 | + VLAN. Otherwise, creates the interfaces but does not create any |
160 | + links or VLANs. |
161 | """ |
162 | # Get all of the current interfaces on this controller. |
163 | current_interfaces = { |
164 | @@ -4634,12 +4659,18 @@ class Controller(Node): |
165 | # Note: the interface that comes back from this call may be None, |
166 | # if we decided not to model an interface based on what the rack |
167 | # sent. |
168 | - interface = self._update_interface(name, settings) |
169 | + interface = self._update_interface( |
170 | + name, settings, create_fabrics=create_fabrics) |
171 | if interface is not None: |
172 | interface.update_discovery_state(discovery_mode, settings) |
173 | if interface is not None and interface.id in current_interfaces: |
174 | del current_interfaces[interface.id] |
175 | |
176 | + if not create_fabrics: |
177 | + # This could be an existing rack controller re-registering, |
178 | + # so don't delete interfaces during this phase. |
179 | + return |
180 | + |
181 | # Remove all the interfaces that no longer exist. We do this in reverse |
182 | # order so the child is deleted before the parent. |
183 | deletion_order = {} |
184 | diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py |
185 | index c07d74d..bbd7915 100644 |
186 | --- a/src/maasserver/models/tests/test_node.py |
187 | +++ b/src/maasserver/models/tests/test_node.py |
188 | @@ -6693,15 +6693,36 @@ class TestReportMDNSEntries(MAASServerTestCase): |
189 | class TestUpdateInterfaces(MAASServerTestCase): |
190 | |
191 | scenarios = ( |
192 | - ("rack", dict(node_type=NODE_TYPE.RACK_CONTROLLER)), |
193 | - ("region", dict(node_type=NODE_TYPE.REGION_CONTROLLER)), |
194 | + ("rack", dict( |
195 | + node_type=NODE_TYPE.RACK_CONTROLLER, |
196 | + two_phase=False)), |
197 | + ("region", dict( |
198 | + node_type=NODE_TYPE.REGION_CONTROLLER, |
199 | + two_phase=False)), |
200 | ("region+rack", dict( |
201 | - node_type=NODE_TYPE.REGION_AND_RACK_CONTROLLER)), |
202 | + node_type=NODE_TYPE.REGION_AND_RACK_CONTROLLER, |
203 | + two_phase=False)), |
204 | + ("rack_two_phase", dict( |
205 | + node_type=NODE_TYPE.RACK_CONTROLLER, |
206 | + two_phase=True)), |
207 | + ("region_two_phase", dict( |
208 | + node_type=NODE_TYPE.REGION_CONTROLLER, |
209 | + two_phase=True)), |
210 | + ("region+rack_two_phase", dict( |
211 | + node_type=NODE_TYPE.REGION_AND_RACK_CONTROLLER, |
212 | + two_phase=True)), |
213 | ) |
214 | |
215 | def create_empty_controller(self): |
216 | return factory.make_Node(node_type=self.node_type).as_self() |
217 | |
218 | + def update_interfaces(self, controller, interfaces): |
219 | + if not self.two_phase: |
220 | + controller.update_interfaces(interfaces) |
221 | + else: |
222 | + controller.update_interfaces(interfaces, create_fabrics=False) |
223 | + controller.update_interfaces(interfaces, create_fabrics=True) |
224 | + |
225 | def test__order_of_calls_to_update_interface_is_always_the_same(self): |
226 | controller = self.create_empty_controller() |
227 | interfaces = { |
228 | @@ -6741,18 +6762,32 @@ class TestUpdateInterfaces(MAASServerTestCase): |
229 | "enabled": True, |
230 | }, |
231 | } |
232 | - expected_call_order = [ |
233 | - call("eth0", interfaces["eth0"]), |
234 | - call("eth1", interfaces["eth1"]), |
235 | - call("eth2", interfaces["eth2"]), |
236 | - call("bond0", interfaces["bond0"]), |
237 | - call("bond0.10", interfaces["bond0.10"]), |
238 | - ] |
239 | + if not self.two_phase: |
240 | + expected_call_order = [ |
241 | + call("eth0", interfaces["eth0"], create_fabrics=True), |
242 | + call("eth1", interfaces["eth1"], create_fabrics=True), |
243 | + call("eth2", interfaces["eth2"], create_fabrics=True), |
244 | + call("bond0", interfaces["bond0"], create_fabrics=True), |
245 | + call("bond0.10", interfaces["bond0.10"], create_fabrics=True), |
246 | + ] |
247 | + else: |
248 | + expected_call_order = [ |
249 | + call("eth0", interfaces["eth0"], create_fabrics=False), |
250 | + call("eth1", interfaces["eth1"], create_fabrics=False), |
251 | + call("eth2", interfaces["eth2"], create_fabrics=False), |
252 | + call("bond0", interfaces["bond0"], create_fabrics=False), |
253 | + call("bond0.10", interfaces["bond0.10"], create_fabrics=False), |
254 | + call("eth0", interfaces["eth0"], create_fabrics=True), |
255 | + call("eth1", interfaces["eth1"], create_fabrics=True), |
256 | + call("eth2", interfaces["eth2"], create_fabrics=True), |
257 | + call("bond0", interfaces["bond0"], create_fabrics=True), |
258 | + call("bond0.10", interfaces["bond0.10"], create_fabrics=True), |
259 | + ] |
260 | # Perform multiple times to make sure the call order is always |
261 | # the same. |
262 | for _ in range(5): |
263 | mock_update_interface = self.patch(controller, "_update_interface") |
264 | - controller.update_interfaces(interfaces) |
265 | + self.update_interfaces(controller, interfaces) |
266 | self.assertThat( |
267 | mock_update_interface, MockCallsMatch(*expected_call_order)) |
268 | |
269 | @@ -6774,7 +6809,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
270 | "enabled": False, |
271 | }, |
272 | } |
273 | - controller.update_interfaces(interfaces) |
274 | + self.update_interfaces(controller, interfaces) |
275 | eth0 = Interface.objects.get(name="eth0", node=controller) |
276 | self.assertThat( |
277 | eth0, MatchesStructure.byEquality( |
278 | @@ -6847,7 +6882,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
279 | |
280 | def _test_vlans_with_alternate_naming_conventions( |
281 | self, controller, interfaces): |
282 | - controller.update_interfaces(interfaces) |
283 | + self.update_interfaces(controller, interfaces) |
284 | eth0 = Interface.objects.get(name="eth0", node=controller) |
285 | self.assertThat( |
286 | eth0, MatchesStructure.byEquality( |
287 | @@ -6886,7 +6921,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
288 | enabled=True, |
289 | )) |
290 | self.assertThat(list(eth0_0102.parents.all()), Equals([eth0])) |
291 | - controller.update_interfaces(interfaces) |
292 | + self.update_interfaces(controller, interfaces) |
293 | eth0 = Interface.objects.get(name="eth0", node=controller) |
294 | self.assertThat( |
295 | eth0, MatchesStructure.byEquality( |
296 | @@ -6957,7 +6992,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
297 | "monitored": False |
298 | } |
299 | } |
300 | - controller.update_interfaces(interfaces) |
301 | + self.update_interfaces(controller, interfaces) |
302 | eth0 = Interface.objects.get(name="eth0", node=controller) |
303 | self.assertThat( |
304 | eth0, MatchesStructure.byEquality( |
305 | @@ -7002,7 +7037,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
306 | "monitored": False |
307 | }, |
308 | } |
309 | - controller.update_interfaces(interfaces) |
310 | + self.update_interfaces(controller, interfaces) |
311 | # Disable the interfaces so that we can make sure neighbour discovery |
312 | # is properly disabled on update. |
313 | interfaces = { |
314 | @@ -7024,7 +7059,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
315 | "monitored": False |
316 | }, |
317 | } |
318 | - controller.update_interfaces(interfaces) |
319 | + self.update_interfaces(controller, interfaces) |
320 | eth0 = Interface.objects.get(name="eth0", node=controller) |
321 | self.assertThat( |
322 | eth0, MatchesStructure.byEquality( |
323 | @@ -7065,7 +7100,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
324 | "enabled": True, |
325 | }, |
326 | } |
327 | - controller.update_interfaces(interfaces) |
328 | + self.update_interfaces(controller, interfaces) |
329 | eth0 = Interface.objects.get(name="eth0", node=controller) |
330 | default_vlan = Fabric.objects.get_default_fabric().get_default_vlan() |
331 | self.assertThat( |
332 | @@ -7109,7 +7144,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
333 | "enabled": True, |
334 | }, |
335 | } |
336 | - controller.update_interfaces(interfaces) |
337 | + self.update_interfaces(controller, interfaces) |
338 | eth0 = Interface.objects.get(name="eth0", node=controller) |
339 | default_vlan = Fabric.objects.get_default_fabric().get_default_vlan() |
340 | self.assertThat( |
341 | @@ -7163,7 +7198,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
342 | "enabled": True, |
343 | }, |
344 | } |
345 | - controller.update_interfaces(interfaces) |
346 | + self.update_interfaces(controller, interfaces) |
347 | eth0 = Interface.objects.get(name="eth0", node=controller) |
348 | default_vlan = Fabric.objects.get_default_fabric().get_default_vlan() |
349 | self.assertThat( |
350 | @@ -7210,7 +7245,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
351 | "enabled": True, |
352 | }, |
353 | } |
354 | - controller.update_interfaces(interfaces) |
355 | + self.update_interfaces(controller, interfaces) |
356 | eth0 = Interface.objects.get(name="eth0", node=controller) |
357 | self.assertThat( |
358 | eth0, MatchesStructure.byEquality( |
359 | @@ -7255,7 +7290,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
360 | "enabled": True, |
361 | }, |
362 | } |
363 | - controller.update_interfaces(interfaces) |
364 | + self.update_interfaces(controller, interfaces) |
365 | eth0 = Interface.objects.get(name="eth0", node=controller) |
366 | self.assertThat( |
367 | eth0, MatchesStructure.byEquality( |
368 | @@ -7306,7 +7341,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
369 | "enabled": True, |
370 | }, |
371 | } |
372 | - controller.update_interfaces(interfaces) |
373 | + self.update_interfaces(controller, interfaces) |
374 | eth0 = Interface.objects.get(name="eth0", node=controller) |
375 | self.assertThat( |
376 | eth0, MatchesStructure.byEquality( |
377 | @@ -7354,7 +7389,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
378 | "enabled": True, |
379 | }, |
380 | } |
381 | - controller.update_interfaces(interfaces) |
382 | + self.update_interfaces(controller, interfaces) |
383 | self.assertThat(controller.interface_set.count(), Equals(1)) |
384 | self.assertThat( |
385 | reload_object(interface), MatchesStructure.byEquality( |
386 | @@ -7396,7 +7431,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
387 | "enabled": True, |
388 | }, |
389 | } |
390 | - controller.update_interfaces(interfaces) |
391 | + self.update_interfaces(controller, interfaces) |
392 | self.assertThat(controller.interface_set.count(), Equals(1)) |
393 | self.assertThat( |
394 | reload_object(interface), MatchesStructure.byEquality( |
395 | @@ -7444,7 +7479,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
396 | "enabled": True, |
397 | }, |
398 | } |
399 | - controller.update_interfaces(interfaces) |
400 | + self.update_interfaces(controller, interfaces) |
401 | self.assertThat(controller.interface_set.count(), Equals(1)) |
402 | self.assertThat( |
403 | reload_object(interface), MatchesStructure.byEquality( |
404 | @@ -7497,7 +7532,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
405 | "enabled": True, |
406 | "vid": vid_on_fabric, |
407 | } |
408 | - controller.update_interfaces(interfaces) |
409 | + self.update_interfaces(controller, interfaces) |
410 | self.assertThat(controller.interface_set.count(), Equals(2)) |
411 | self.assertThat( |
412 | reload_object(interface), MatchesStructure.byEquality( |
413 | @@ -7564,7 +7599,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
414 | "enabled": True, |
415 | "vid": vid_on_fabric, |
416 | } |
417 | - controller.update_interfaces(interfaces) |
418 | + self.update_interfaces(controller, interfaces) |
419 | self.assertThat(controller.interface_set.count(), Equals(2)) |
420 | self.assertThat( |
421 | reload_object(interface), MatchesStructure.byEquality( |
422 | @@ -7647,7 +7682,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
423 | "vid": vid_on_fabric, |
424 | } |
425 | maaslog = self.patch(node_module, "maaslog") |
426 | - controller.update_interfaces(interfaces) |
427 | + self.update_interfaces(controller, interfaces) |
428 | self.assertThat(controller.interface_set.count(), Equals(2)) |
429 | self.assertThat( |
430 | reload_object(interface), MatchesStructure.byEquality( |
431 | @@ -7709,7 +7744,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
432 | "enabled": True, |
433 | "vid": vid_on_fabric, |
434 | } |
435 | - controller.update_interfaces(interfaces) |
436 | + self.update_interfaces(controller, interfaces) |
437 | self.assertThat(controller.interface_set.count(), Equals(2)) |
438 | self.assertThat( |
439 | reload_object(interface), MatchesStructure.byEquality( |
440 | @@ -7760,7 +7795,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
441 | "enabled": True, |
442 | "vid": new_vlan.vid, |
443 | } |
444 | - controller.update_interfaces(interfaces) |
445 | + self.update_interfaces(controller, interfaces) |
446 | self.assertThat(controller.interface_set.count(), Equals(2)) |
447 | self.assertThat( |
448 | reload_object(interface), MatchesStructure.byEquality( |
449 | @@ -7825,7 +7860,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
450 | "vid": new_vlan.vid, |
451 | } |
452 | maaslog = self.patch(node_module, "maaslog") |
453 | - controller.update_interfaces(interfaces) |
454 | + self.update_interfaces(controller, interfaces) |
455 | self.assertThat(controller.interface_set.count(), Equals(2)) |
456 | self.assertThat( |
457 | reload_object(interface), MatchesStructure.byEquality( |
458 | @@ -7891,7 +7926,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
459 | "enabled": True, |
460 | }, |
461 | } |
462 | - controller.update_interfaces(interfaces) |
463 | + self.update_interfaces(controller, interfaces) |
464 | self.assertThat(controller.interface_set.count(), Equals(3)) |
465 | bond_interface = BondInterface.objects.get( |
466 | node=controller, mac_address=interfaces["bond0"]["mac_address"]) |
467 | @@ -7938,7 +7973,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
468 | "enabled": True, |
469 | }, |
470 | } |
471 | - controller.update_interfaces(interfaces) |
472 | + self.update_interfaces(controller, interfaces) |
473 | self.assertThat(controller.interface_set.count(), Equals(3)) |
474 | bond_interface = BridgeInterface.objects.get( |
475 | node=controller, mac_address=interfaces["br0"]["mac_address"]) |
476 | @@ -7989,7 +8024,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
477 | "enabled": True, |
478 | }, |
479 | } |
480 | - controller.update_interfaces(interfaces) |
481 | + self.update_interfaces(controller, interfaces) |
482 | self.assertThat(controller.interface_set.count(), Equals(3)) |
483 | self.assertThat( |
484 | reload_object(bond0), MatchesStructure.byEquality( |
485 | @@ -8038,7 +8073,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
486 | "enabled": True, |
487 | }, |
488 | } |
489 | - controller.update_interfaces(interfaces) |
490 | + self.update_interfaces(controller, interfaces) |
491 | self.assertThat(controller.interface_set.count(), Equals(3)) |
492 | self.assertThat( |
493 | reload_object(br0), MatchesStructure.byEquality( |
494 | @@ -8093,7 +8128,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
495 | "enabled": True, |
496 | }, |
497 | } |
498 | - controller.update_interfaces(interfaces) |
499 | + self.update_interfaces(controller, interfaces) |
500 | self.assertThat(controller.interface_set.count(), Equals(3)) |
501 | self.assertThat( |
502 | reload_object(eth0), MatchesStructure.byEquality( |
503 | @@ -8166,7 +8201,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
504 | "enabled": True, |
505 | }, |
506 | } |
507 | - controller.update_interfaces(interfaces) |
508 | + self.update_interfaces(controller, interfaces) |
509 | self.assertThat(controller.interface_set.count(), Equals(3)) |
510 | self.assertThat( |
511 | reload_object(eth0), MatchesStructure.byEquality( |
512 | @@ -8239,7 +8274,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
513 | "enabled": True, |
514 | }, |
515 | } |
516 | - controller.update_interfaces(interfaces) |
517 | + self.update_interfaces(controller, interfaces) |
518 | self.assertThat(reload_object(eth0), Not(Is(None))) |
519 | self.assertThat(reload_object(eth1), Is(None)) |
520 | self.assertThat(reload_object(bond0), Not(Is(None))) |
521 | @@ -8270,7 +8305,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
522 | "enabled": True, |
523 | }, |
524 | } |
525 | - controller.update_interfaces(interfaces) |
526 | + self.update_interfaces(controller, interfaces) |
527 | self.assertThat(reload_object(eth0), Not(Is(None))) |
528 | self.assertThat(reload_object(eth1), Is(None)) |
529 | self.assertThat(reload_object(br0), Not(Is(None))) |
530 | @@ -8294,7 +8329,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
531 | "enabled": True, |
532 | }, |
533 | } |
534 | - controller.update_interfaces(interfaces) |
535 | + self.update_interfaces(controller, interfaces) |
536 | self.assertThat(reload_object(eth0), Not(Is(None))) |
537 | self.assertThat(reload_object(eth1), Is(None)) |
538 | self.assertThat(reload_object(bond0), Is(None)) |
539 | @@ -8318,7 +8353,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
540 | "enabled": True, |
541 | }, |
542 | } |
543 | - controller.update_interfaces(interfaces) |
544 | + self.update_interfaces(controller, interfaces) |
545 | self.assertThat(reload_object(eth0), Not(Is(None))) |
546 | self.assertThat(reload_object(eth1), Is(None)) |
547 | self.assertThat(reload_object(br0), Is(None)) |
548 | @@ -8371,7 +8406,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
549 | "vid": bond0_vlan.vid, |
550 | "enabled": True, |
551 | } |
552 | - controller.update_interfaces(interfaces) |
553 | + self.update_interfaces(controller, interfaces) |
554 | eth0 = PhysicalInterface.objects.get( |
555 | node=controller, mac_address=interfaces["eth0"]["mac_address"]) |
556 | self.assertThat( |
557 | @@ -8482,7 +8517,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
558 | "vid": br0_vlan.vid, |
559 | "enabled": True, |
560 | } |
561 | - controller.update_interfaces(interfaces) |
562 | + self.update_interfaces(controller, interfaces) |
563 | eth0 = PhysicalInterface.objects.get( |
564 | node=controller, mac_address=interfaces["eth0"]["mac_address"]) |
565 | self.assertThat( |
566 | @@ -8813,7 +8848,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
567 | "enabled": True, |
568 | }, |
569 | } |
570 | - controller.update_interfaces(interfaces) |
571 | + self.update_interfaces(controller, interfaces) |
572 | eth0 = PhysicalInterface.objects.get( |
573 | node=controller, mac_address=interfaces["eth0"]["mac_address"]) |
574 | self.assertThat( |
575 | @@ -9048,7 +9083,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
576 | "enabled": True, |
577 | }, |
578 | } |
579 | - controller.update_interfaces(interfaces) |
580 | + self.update_interfaces(controller, interfaces) |
581 | eth0 = PhysicalInterface.objects.get( |
582 | node=controller, mac_address=interfaces["eth0"]["mac_address"]) |
583 | self.assertThat( |
584 | @@ -9122,7 +9157,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
585 | 'type': 'physical' |
586 | } |
587 | } |
588 | - controller.update_interfaces(interfaces) |
589 | + self.update_interfaces(controller, interfaces) |
590 | eth0 = get_one( |
591 | PhysicalInterface.objects.filter(node=controller, name='eth0')) |
592 | br0 = get_one( |
593 | @@ -9153,7 +9188,7 @@ class TestUpdateInterfaces(MAASServerTestCase): |
594 | 'type': 'physical' |
595 | }, |
596 | } |
597 | - controller.update_interfaces(interfaces) |
598 | + self.update_interfaces(controller, interfaces) |
599 | eth0 = get_one( |
600 | PhysicalInterface.objects.filter(node=controller, name='eth0')) |
601 | br0 = get_one( |
602 | diff --git a/src/maasserver/rpc/rackcontrollers.py b/src/maasserver/rpc/rackcontrollers.py |
603 | index 9c49532..2d8e30a 100644 |
604 | --- a/src/maasserver/rpc/rackcontrollers.py |
605 | +++ b/src/maasserver/rpc/rackcontrollers.py |
606 | @@ -76,7 +76,7 @@ def handle_upgrade(rack_controller, nodegroup_uuid): |
607 | @transactional |
608 | def register( |
609 | system_id=None, hostname='', interfaces=None, |
610 | - url=None, is_loopback=None): |
611 | + url=None, is_loopback=None, create_fabrics=True): |
612 | """Register a new rack controller if not already registered. |
613 | |
614 | Attempt to see if the rack controller was already registered as a node. |
615 | @@ -156,8 +156,8 @@ def register( |
616 | rackcontroller.owner = worker_user.get_worker_user() |
617 | update_fields.append("owner") |
618 | rackcontroller.save(update_fields=update_fields) |
619 | - # Update networking information every time we see a rack. |
620 | - rackcontroller.update_interfaces(interfaces) |
621 | + # Update interfaces, if requested. |
622 | + rackcontroller.update_interfaces(interfaces, create_fabrics=create_fabrics) |
623 | return rackcontroller |
624 | |
625 | |
626 | diff --git a/src/maasserver/rpc/regionservice.py b/src/maasserver/rpc/regionservice.py |
627 | index b622cfb..8d394cc 100644 |
628 | --- a/src/maasserver/rpc/regionservice.py |
629 | +++ b/src/maasserver/rpc/regionservice.py |
630 | @@ -531,6 +531,27 @@ def getRegionID(): |
631 | lambda advertising: advertising.region_id) |
632 | |
633 | |
634 | +@inlineCallbacks |
635 | +def isLoopbackURL(url): |
636 | + """Checks if the specified URL refers to a loopback address. |
637 | + |
638 | + :return: True if the URL refers to the loopback interface, otherwise False. |
639 | + """ |
640 | + if url is not None: |
641 | + if url.hostname is not None: |
642 | + is_loopback = yield deferToThread( |
643 | + resolves_to_loopback_address, url.hostname) |
644 | + else: |
645 | + # Empty URL == localhost. |
646 | + is_loopback = True |
647 | + else: |
648 | + # We need to pass is_loopback in, but it is only checked if url |
649 | + # is not None. None is the "I don't know and you won't ask" |
650 | + # state for this boolean. |
651 | + is_loopback = None |
652 | + return is_loopback |
653 | + |
654 | + |
655 | @implementer(IConnection) |
656 | class RegionServer(Region): |
657 | """The RPC protocol supported by a region controller, server version. |
658 | @@ -551,6 +572,33 @@ class RegionServer(Region): |
659 | hostIsRemote = False |
660 | |
661 | @inlineCallbacks |
662 | + def initResponder(self, rack_controller): |
663 | + """Set up local connection identifiers for this RPC connection. |
664 | + |
665 | + Sets up connection identifiers, and adds the connection into the |
666 | + service and the database. |
667 | + |
668 | + :param rack_controller: A RackController model object representing the |
669 | + remote rack controller. |
670 | + """ |
671 | + self.ident = rack_controller.system_id |
672 | + self.factory.service._addConnectionFor(self.ident, self) |
673 | + # A local rack is treated differently to one that's remote. |
674 | + self.host = self.transport.getHost() |
675 | + self.hostIsRemote = isinstance( |
676 | + self.host, (IPv4Address, IPv6Address)) |
677 | + # Only register the connection into the database when it's a valid |
678 | + # IPv4 or IPv6. Only time it is not an IPv4 or IPv6 address is |
679 | + # when mocking a connection. |
680 | + if self.hostIsRemote: |
681 | + advertising = yield ( |
682 | + self.factory.service.advertiser.advertising.get()) |
683 | + process = yield deferToDatabase(advertising.getRegionProcess) |
684 | + yield deferToDatabase( |
685 | + advertising.registerConnection, |
686 | + process, rack_controller, self.host) |
687 | + |
688 | + @inlineCallbacks |
689 | def authenticateCluster(self): |
690 | """Authenticate the cluster.""" |
691 | secret = yield get_shared_secret() |
692 | @@ -565,52 +613,29 @@ class RegionServer(Region): |
693 | @inlineCallbacks |
694 | def register( |
695 | self, system_id, hostname, interfaces, url, nodegroup_uuid=None): |
696 | + result = yield self._register( |
697 | + system_id, hostname, interfaces, url, nodegroup_uuid) |
698 | + return result |
699 | |
700 | + @inlineCallbacks |
701 | + def _register( |
702 | + self, system_id, hostname, interfaces, url, nodegroup_uuid=None, |
703 | + create_fabrics=True): |
704 | try: |
705 | # Register, which includes updating interfaces. |
706 | - if url is not None: |
707 | - if url.hostname is not None: |
708 | - is_loopback = yield deferToThread( |
709 | - resolves_to_loopback_address, url.hostname) |
710 | - else: |
711 | - # Empty URL == localhost. |
712 | - is_loopback = True |
713 | - else: |
714 | - # We need to pass is_loopback in, but it is only checked if url |
715 | - # is not None. None is the "I don't know and you won't ask" |
716 | - # state for this boolean. |
717 | - is_loopback = None |
718 | + is_loopback = yield isLoopbackURL(url) |
719 | rack_controller = yield deferToDatabase( |
720 | rackcontrollers.register, system_id=system_id, |
721 | hostname=hostname, interfaces=interfaces, url=url, |
722 | - is_loopback=is_loopback) |
723 | + is_loopback=is_loopback, create_fabrics=create_fabrics) |
724 | |
725 | # Check for upgrade. |
726 | if nodegroup_uuid is not None: |
727 | yield deferToDatabase( |
728 | - rackcontrollers.handle_upgrade, |
729 | - rack_controller, nodegroup_uuid) |
730 | - |
731 | - # Set the identifier and add the connection into the service |
732 | - # and into the database. |
733 | - self.ident = rack_controller.system_id |
734 | - self.factory.service._addConnectionFor(self.ident, self) |
735 | - |
736 | - # A local rack is treated differently to one that's remote. |
737 | - self.host = self.transport.getHost() |
738 | - self.hostIsRemote = isinstance( |
739 | - self.host, (IPv4Address, IPv6Address)) |
740 | - |
741 | - # Only register the connection into the database when it's a valid |
742 | - # IPv4 or IPv6. Only time it is not an IPv4 or IPv6 address is |
743 | - # when mocking a connection. |
744 | - if self.hostIsRemote: |
745 | - advertising = yield ( |
746 | - self.factory.service.advertiser.advertising.get()) |
747 | - process = yield deferToDatabase(advertising.getRegionProcess) |
748 | - yield deferToDatabase( |
749 | - advertising.registerConnection, |
750 | - process, rack_controller, self.host) |
751 | + rackcontrollers.handle_upgrade, rack_controller, |
752 | + nodegroup_uuid) |
753 | + |
754 | + yield self.initResponder(rack_controller) |
755 | |
756 | # Rack controller is now registered. Log this status. |
757 | log.msg( |
758 | diff --git a/src/provisioningserver/rpc/region.py b/src/provisioningserver/rpc/region.py |
759 | index 974bba1..23bcdc7 100644 |
760 | --- a/src/provisioningserver/rpc/region.py |
761 | +++ b/src/provisioningserver/rpc/region.py |
762 | @@ -61,7 +61,7 @@ from twisted.protocols import amp |
763 | class RegisterRackController(amp.Command): |
764 | """Register a rack controller with the region controller. |
765 | |
766 | - This is the second step of the Authenticate, Register, Commision |
767 | + This is the second step of the Authenticate, Register, Commission |
768 | process. |
769 | |
770 | :since: 2.0 |
Looks like a clean refactor. Only comments is on the why you check for a boolean value in if statements. Just a plain if would be better. If your worried about the type then I would do a check for the type at the start of the function:
if not isinstance( create_ fabrics, bool): fabrics) .__name_ _)
raise TypeError('expected create_fabrics to be a bool; received %s' % type(create_