Merge ~mpontillo/maas:allow-deferred-fabric-creation into maas:master

Proposed by Mike Pontillo
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)
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.)

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote :

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):
    raise TypeError('expected create_fabrics to be a bool; received %s' % type(create_fabrics).__name__)

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

LANDING
-b allow-deferred-fabric-creation lp:~mpontillo/maas into -b master lp:~maas-committers/maas

STATUS: FAILED MERGE
LOG: http://ci-maas-jenkins.internal:8080/job/maas/job/branch-lander/14376/console

Revision history for this message
Mike Pontillo (mpontillo) wrote :

Not sure why this failed to merge. There were no conflicts. Trying again.

Revision history for this message
MAAS Lander (maas-lander) wrote :

LANDING
-b allow-deferred-fabric-creation lp:~mpontillo/maas into -b master lp:~maas-committers/maas

STATUS: FAILED BUILD
LOG: http://ci-maas-jenkins.internal:8080/job/maas/job/branch-tester/89/console

Revision history for this message
Mike Pontillo (mpontillo) wrote :

Strange failure:

    http://paste.ubuntu.com/25077309/

I wasn't able to replicate this:

$ bin/test.region metadataserver.tests.test_api:TestCommissioningAPI.test_signal_stores_multiple_files{,,,,,,,,,}{,,,,,,,,,}
....................................................................................................
----------------------------------------------------------------------
Ran 100 tests in 24.142s

Revision history for this message
MAAS Lander (maas-lander) wrote :

LANDING
-b allow-deferred-fabric-creation lp:~mpontillo/maas into -b master lp:~maas-committers/maas

STATUS: FAILED MERGE
LOG: http://ci-maas-jenkins.internal:8080/job/maas/job/branch-lander/14440/console

Revision history for this message
Mike Pontillo (mpontillo) wrote :

Another weird lander failure.

http://paste.ubuntu.com/25077511/

Revision history for this message
MAAS Lander (maas-lander) wrote :

LANDING
-b allow-deferred-fabric-creation lp:~mpontillo/maas into -b master lp:~maas-committers/maas

STATUS: FAILED MERGE
LOG: http://ci-maas-jenkins.internal:8080/job/maas/job/branch-lander/14455/console

Revision history for this message
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.

Revision history for this message
Mike Pontillo (mpontillo) wrote :

The problem was double-quotes in the commit message. Changing them to single quotes fixed the issue.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
2index 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 = {}
184diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
185index 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(
602diff --git a/src/maasserver/rpc/rackcontrollers.py b/src/maasserver/rpc/rackcontrollers.py
603index 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
626diff --git a/src/maasserver/rpc/regionservice.py b/src/maasserver/rpc/regionservice.py
627index 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(
758diff --git a/src/provisioningserver/rpc/region.py b/src/provisioningserver/rpc/region.py
759index 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

Subscribers

People subscribed via source and target branches