Merge lp:~blake-rouse/maas/rack-nic-fabric-change into lp:~maas-committers/maas/trunk
- rack-nic-fabric-change
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Blake Rouse |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4725 |
Proposed branch: | lp:~blake-rouse/maas/rack-nic-fabric-change |
Merge into: | lp:~maas-committers/maas/trunk |
Prerequisite: | lp:~blake-rouse/maas/fix-1551378 |
Diff against target: |
484 lines (+273/-60) 6 files modified
src/maasserver/models/interface.py (+3/-2) src/maasserver/models/signals/interfaces.py (+89/-19) src/maasserver/models/signals/nodes.py (+5/-5) src/maasserver/models/signals/tests/test_interfaces.py (+101/-0) src/maasserver/models/tests/test_interface.py (+55/-23) src/maasserver/testing/factory.py (+20/-11) |
To merge this branch: | bzr merge lp:~blake-rouse/maas/rack-nic-fabric-change |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike Pontillo (community) | Approve | ||
Review via email: mp+287852@code.launchpad.net |
Commit message
Fix forms to only allow physical and bond interfaces to be on untagged vlans. Fix forms to only allow VLAN interface to be on the same fabric as its parent interface. Migrate subnets and child VLAN interfaces that are attached to a controllers interface when the interfaces fabric is changed.
Description of the change
Blake Rouse (blake-rouse) wrote : | # |
Mike Pontillo (mpontillo) wrote : | # |
I disagree with this change. We have received feedback from the field team that this set of assumptions is not working for customers.
First, the use of the term "untagged" is very misleading. It is a term to describe a *switch port*, not a term to describe an attribute of a VLAN. A switch can have many ports in a fabric, some of which are "untagged" in (for example) the VLAN with VID 10, some of which are untagged in (for example) the VLAN with VID 20. The first thing we should do is rename that. (perhaps "default" would be better, but it's more like "unknown" - it's *one of* the 4094 VLANs, but we aren't sure which one.)
Assuming we're using the correct terminology (and then, the idea that a single fabric only ever has one "untagged" VLAN is out of the way), think about the case of the multiple different VLANs being "untagged" on different switch ports. Users MUST be able to change the model so that physical interfaces can appear on ports with a known VID. (Otherwise, you're forced to model it as two separate fabrics - and therefore you cannot support the VLAN interface creation feature in MAAS.)
This also breaks HA in the case where an inter-switch link uses tagged VLANs to aggregate multiple racks, and expects each rack controller to demultiplex the VLAN tags.
I understand and agree with the vision that MAAS will work better if all the switch ports are configured the same. However, I am opposed to the idea that we should force the model to conform to that. (yes, it should be the default, well-supported way to do things in MAAS, but we should not prevent MAAS from working if it doesn't match reality.)
Blake Rouse (blake-rouse) wrote : | # |
I have removed the code that validations the untagged VLANs. Please give it another look. Thanks.
Mike Pontillo (mpontillo) wrote : | # |
I'm good with this branch now; thanks.
For the record, only validations I had a problem with in revision 4710 were these:
- "A physical interface can only belong to an untagged VLAN."
- "A bond interface can only belong to an untagged VLAN."
(Again, in my view, we have received direction that we are not modeling switches, therefore it would be inappropriate to enforce those restrictions.)
The following other validations are perfectly reasonable (necessary, even):
- "A VLAN interface can only belong to a tagged VLAN."
- "A VLAN interface can only belong to a tagged VLAN on the same fabric as its parent interface."
- "All [bond] (sic) parents must belong to the same VLAN."
Preview Diff
1 | === modified file 'src/maasserver/models/interface.py' |
2 | --- src/maasserver/models/interface.py 2016-02-25 16:46:49 +0000 |
3 | +++ src/maasserver/models/interface.py 2016-03-03 21:19:22 +0000 |
4 | @@ -714,11 +714,12 @@ |
5 | # Nothing to do, already has links. |
6 | return |
7 | else: |
8 | - # Use an associated subnet if it exists, else it will just be a |
9 | + # Use an associated subnet if it exists and its on the same VLAN |
10 | + # the interface is currently connected, else it will just be a |
11 | # LINK_UP without a subnet. |
12 | discovered_address = self.ip_addresses.filter( |
13 | alloc_type=IPADDRESS_TYPE.DISCOVERED, |
14 | - subnet__isnull=False).first() |
15 | + subnet__vlan=self.vlan).first() |
16 | if discovered_address is not None: |
17 | subnet = discovered_address.subnet |
18 | else: |
19 | |
20 | === modified file 'src/maasserver/models/signals/interfaces.py' |
21 | --- src/maasserver/models/signals/interfaces.py 2016-01-20 00:07:16 +0000 |
22 | +++ src/maasserver/models/signals/interfaces.py 2016-03-03 21:19:22 +0000 |
23 | @@ -7,19 +7,29 @@ |
24 | "signals", |
25 | ] |
26 | |
27 | +from django.db.models.signals import post_save |
28 | from maasserver.enum import ( |
29 | INTERFACE_TYPE, |
30 | IPADDRESS_TYPE, |
31 | + NODE_TYPE, |
32 | ) |
33 | from maasserver.models import ( |
34 | BondInterface, |
35 | Interface, |
36 | PhysicalInterface, |
37 | + VLAN, |
38 | VLANInterface, |
39 | ) |
40 | from maasserver.utils.signals import SignalsManager |
41 | |
42 | |
43 | +INTERFACE_CLASSES = [ |
44 | + Interface, |
45 | + PhysicalInterface, |
46 | + BondInterface, |
47 | + VLANInterface, |
48 | +] |
49 | + |
50 | signals = SignalsManager() |
51 | |
52 | |
53 | @@ -48,12 +58,10 @@ |
54 | ip_address, clearing_config=True) |
55 | |
56 | |
57 | -signals.watch_fields( |
58 | - interface_enabled_or_disabled, |
59 | - Interface, ['enabled'], delete=False) |
60 | -signals.watch_fields( |
61 | - interface_enabled_or_disabled, |
62 | - PhysicalInterface, ['enabled'], delete=False) |
63 | +for klass in INTERFACE_CLASSES: |
64 | + signals.watch_fields( |
65 | + interface_enabled_or_disabled, |
66 | + klass, ['enabled'], delete=False) |
67 | |
68 | |
69 | def interface_mtu_params_update(instance, old_values, **kwargs): |
70 | @@ -114,19 +122,81 @@ |
71 | parent.save() |
72 | |
73 | |
74 | -signals.watch_fields( |
75 | - interface_mtu_params_update, |
76 | - Interface, ['params'], delete=False) |
77 | -signals.watch_fields( |
78 | - interface_mtu_params_update, |
79 | - PhysicalInterface, ['params'], delete=False) |
80 | -signals.watch_fields( |
81 | - interface_mtu_params_update, |
82 | - BondInterface, ['params'], delete=False) |
83 | -signals.watch_fields( |
84 | - interface_mtu_params_update, |
85 | - VLANInterface, ['params'], delete=False) |
86 | - |
87 | +for klass in INTERFACE_CLASSES: |
88 | + signals.watch_fields( |
89 | + interface_mtu_params_update, |
90 | + klass, ['params'], delete=False) |
91 | + |
92 | + |
93 | +def update_bond_parents(sender, instance, created, **kwargs): |
94 | + """Update bond parents when interface created.""" |
95 | + if instance.type == INTERFACE_TYPE.BOND: |
96 | + for parent in instance.parents.all(): |
97 | + # Make sure the parent has not links as well, just to be sure. |
98 | + parent.clear_all_links(clearing_config=True) |
99 | + if parent.vlan != instance.vlan: |
100 | + parent.vlan = instance.vlan |
101 | + parent.save() |
102 | + |
103 | + |
104 | +for klass in INTERFACE_CLASSES: |
105 | + signals.watch( |
106 | + post_save, update_bond_parents, |
107 | + sender=klass) |
108 | + |
109 | + |
110 | +def interface_vlan_update(instance, old_values, **kwargs): |
111 | + """When an interfaces VLAN is changed we need to do the following. |
112 | + |
113 | + * If its a controller move all assigned subnets to the new VLAN. This |
114 | + is done because the subnets defined are discovered on the |
115 | + controller and an administrator cannot change them. |
116 | + * If its a machine or device then we need to remove all links if the |
117 | + VLAN is different. |
118 | + """ |
119 | + new_vlan_id = instance.vlan_id |
120 | + [old_vlan_id] = old_values |
121 | + if new_vlan_id == old_vlan_id: |
122 | + # Nothing changed do nothing. |
123 | + return |
124 | + if instance.node is None: |
125 | + # Not assigned to a node. Nothing to do. |
126 | + return |
127 | + |
128 | + new_vlan = instance.vlan |
129 | + if instance.node.node_type in ( |
130 | + NODE_TYPE.REGION_CONTROLLER, |
131 | + NODE_TYPE.RACK_CONTROLLER, |
132 | + NODE_TYPE.REGION_AND_RACK_CONTROLLER): |
133 | + # Interface VLAN was changed on a controller. Move all linked subnets |
134 | + # to that new VLAN. |
135 | + for ip_address in instance.ip_addresses.all(): |
136 | + if ip_address.subnet is not None: |
137 | + ip_address.subnet.vlan = new_vlan |
138 | + ip_address.subnet.save() |
139 | + |
140 | + # If any children are VLAN interfaces then we need to move those |
141 | + # VLANs into the same fabric as the parent. |
142 | + for rel in instance.children_relationships.all(): |
143 | + if rel.child.type == INTERFACE_TYPE.VLAN: |
144 | + new_child_vlan, _ = VLAN.objects.get_or_create( |
145 | + fabric=new_vlan.fabric, vid=rel.child.vlan.vid) |
146 | + rel.child.vlan = new_child_vlan |
147 | + rel.child.save() |
148 | + # No need to update the IP addresses here this function |
149 | + # will be called again because the child has been saved. |
150 | + |
151 | + else: |
152 | + # Interface VLAN was changed on a machine or device. Remove all its |
153 | + # links except the DISCOVERED ones. |
154 | + instance.ip_addresses.exclude( |
155 | + alloc_type=IPADDRESS_TYPE.DISCOVERED).delete() |
156 | + |
157 | + |
158 | +for klass in INTERFACE_CLASSES: |
159 | + signals.watch_fields( |
160 | + interface_vlan_update, |
161 | + klass, ['vlan_id'], delete=False) |
162 | |
163 | # Enable all signals by default. |
164 | signals.enable() |
165 | |
166 | === modified file 'src/maasserver/models/signals/nodes.py' |
167 | --- src/maasserver/models/signals/nodes.py 2016-02-26 21:50:26 +0000 |
168 | +++ src/maasserver/models/signals/nodes.py 2016-03-03 21:19:22 +0000 |
169 | @@ -43,10 +43,10 @@ |
170 | if owner_id_new != owner_id_old: |
171 | NodeKey.objects.clear_token_for_node(node) |
172 | |
173 | -for kclass in NODE_CLASSES: |
174 | +for klass in NODE_CLASSES: |
175 | signals.watch_fields( |
176 | clear_nodekey_when_owner_changes, |
177 | - kclass, ['owner_id'], delete=False) |
178 | + klass, ['owner_id'], delete=False) |
179 | |
180 | |
181 | def create_services_on_node_type_change(node, old_values, deleted=False): |
182 | @@ -62,13 +62,13 @@ |
183 | if created: |
184 | Service.objects.create_services_for(instance) |
185 | |
186 | -for kclass in NODE_CLASSES: |
187 | +for klass in NODE_CLASSES: |
188 | signals.watch_fields( |
189 | create_services_on_node_type_change, |
190 | - kclass, ['node_type'], delete=False) |
191 | + klass, ['node_type'], delete=False) |
192 | signals.watch( |
193 | post_save, create_services_on_create, |
194 | - sender=kclass) |
195 | + sender=klass) |
196 | |
197 | |
198 | # Enable all signals by default. |
199 | |
200 | === modified file 'src/maasserver/models/signals/tests/test_interfaces.py' |
201 | --- src/maasserver/models/signals/tests/test_interfaces.py 2016-02-26 18:39:26 +0000 |
202 | +++ src/maasserver/models/signals/tests/test_interfaces.py 2016-03-03 21:19:22 +0000 |
203 | @@ -10,6 +10,7 @@ |
204 | from maasserver.enum import ( |
205 | INTERFACE_TYPE, |
206 | IPADDRESS_TYPE, |
207 | + NODE_TYPE, |
208 | ) |
209 | from maasserver.testing.factory import factory |
210 | from maasserver.testing.testcase import MAASServerTestCase |
211 | @@ -123,3 +124,103 @@ |
212 | self.assertEqual({ |
213 | 'mtu': bond_mtu, |
214 | }, reload_object(physical3_interface).params) |
215 | + |
216 | + |
217 | +class TestUpdateBondParents(MAASServerTestCase): |
218 | + |
219 | + def test__updates_bond_parents(self): |
220 | + parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
221 | + parent2 = factory.make_Interface( |
222 | + INTERFACE_TYPE.PHYSICAL, node=parent1.node) |
223 | + bond = factory.make_Interface( |
224 | + INTERFACE_TYPE.BOND, parents=[parent1, parent2]) |
225 | + self.assertEqual(bond.vlan, reload_object(parent1).vlan) |
226 | + self.assertEqual(bond.vlan, reload_object(parent2).vlan) |
227 | + |
228 | + def test__update_bond_clears_parent_links(self): |
229 | + parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
230 | + parent2 = factory.make_Interface( |
231 | + INTERFACE_TYPE.PHYSICAL, node=parent1.node) |
232 | + static_ip = factory.make_StaticIPAddress(interface=parent1) |
233 | + factory.make_Interface( |
234 | + INTERFACE_TYPE.BOND, parents=[parent1, parent2]) |
235 | + self.assertIsNone(reload_object(static_ip)) |
236 | + |
237 | + |
238 | +class TestInterfaceVLANUpdateNotController(MAASServerTestCase): |
239 | + |
240 | + scenarios = ( |
241 | + ("machine", { |
242 | + "maker": factory.make_Node, |
243 | + }), |
244 | + ("device", { |
245 | + "maker": factory.make_Device, |
246 | + }), |
247 | + ) |
248 | + |
249 | + def test__removes_links(self): |
250 | + node = self.maker() |
251 | + interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
252 | + static_ip = factory.make_StaticIPAddress(interface=interface) |
253 | + discovered_ip = factory.make_StaticIPAddress( |
254 | + alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=interface) |
255 | + new_fabric = factory.make_Fabric() |
256 | + new_vlan = new_fabric.get_default_vlan() |
257 | + interface.vlan = new_vlan |
258 | + interface.save() |
259 | + self.assertIsNone(reload_object(static_ip)) |
260 | + self.assertIsNotNone(reload_object(discovered_ip)) |
261 | + |
262 | + |
263 | +class TestInterfaceVLANUpdateController(MAASServerTestCase): |
264 | + |
265 | + scenarios = ( |
266 | + ("region", { |
267 | + "maker": factory.make_RegionController, |
268 | + }), |
269 | + ("rack", { |
270 | + "maker": factory.make_RackController, |
271 | + }), |
272 | + ("region-rack", { |
273 | + "maker": lambda: factory.make_Node( |
274 | + node_type=NODE_TYPE.REGION_AND_RACK_CONTROLLER) |
275 | + }), |
276 | + ) |
277 | + |
278 | + def test__moves_link_subnets_to_same_vlan(self): |
279 | + node = self.maker() |
280 | + interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
281 | + subnet = factory.make_Subnet(vlan=interface.vlan) |
282 | + factory.make_StaticIPAddress( |
283 | + subnet=subnet, interface=interface) |
284 | + new_fabric = factory.make_Fabric() |
285 | + new_vlan = new_fabric.get_default_vlan() |
286 | + interface.vlan = new_vlan |
287 | + interface.save() |
288 | + self.assertEquals(new_vlan, reload_object(subnet).vlan) |
289 | + |
290 | + def test__moves_children_vlans_to_same_fabric(self): |
291 | + node = self.maker() |
292 | + parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
293 | + subnet = factory.make_Subnet(vlan=parent.vlan) |
294 | + factory.make_StaticIPAddress( |
295 | + subnet=subnet, interface=parent) |
296 | + old_vlan = factory.make_VLAN(fabric=parent.vlan.fabric) |
297 | + vlan_interface = factory.make_Interface( |
298 | + INTERFACE_TYPE.VLAN, vlan=old_vlan, parents=[parent]) |
299 | + vlan_subnet = factory.make_Subnet(vlan=old_vlan) |
300 | + factory.make_StaticIPAddress( |
301 | + subnet=vlan_subnet, interface=vlan_interface) |
302 | + new_fabric = factory.make_Fabric() |
303 | + new_vlan = new_fabric.get_default_vlan() |
304 | + parent.vlan = new_vlan |
305 | + parent.save() |
306 | + self.assertEquals(new_vlan, reload_object(subnet).vlan) |
307 | + vlan_interface = reload_object(vlan_interface) |
308 | + self.assertEquals( |
309 | + (new_fabric.id, old_vlan.vid), |
310 | + (vlan_interface.vlan.fabric.id, vlan_interface.vlan.vid)) |
311 | + vlan_subnet = reload_object(vlan_subnet) |
312 | + self.assertEquals( |
313 | + (new_fabric.id, old_vlan.vid), |
314 | + (vlan_subnet.vlan.fabric.id, vlan_subnet.vlan.vid)) |
315 | |
316 | === modified file 'src/maasserver/models/tests/test_interface.py' |
317 | --- src/maasserver/models/tests/test_interface.py 2016-02-27 02:31:24 +0000 |
318 | +++ src/maasserver/models/tests/test_interface.py 2016-03-03 21:19:22 +0000 |
319 | @@ -241,31 +241,51 @@ |
320 | ["id:%s" % iface1.id, "id:%s" % iface2.id]), [iface1, iface2]) |
321 | |
322 | def test__filter_by_specifiers_matches_vid(self): |
323 | - iface1 = factory.make_Interface() |
324 | - iface2 = factory.make_Interface() |
325 | - self.assertItemsEqual( |
326 | - Interface.objects.filter_by_specifiers( |
327 | - "vid:%s" % iface1.vlan.vid), [iface1]) |
328 | - self.assertItemsEqual( |
329 | - Interface.objects.filter_by_specifiers( |
330 | - "vid:%s" % iface2.vlan.vid), [iface2]) |
331 | - self.assertItemsEqual( |
332 | - Interface.objects.filter_by_specifiers( |
333 | - ["vid:%s" % iface1.vlan.vid, "vid:%s" % iface2.vlan.vid]), |
334 | + fabric1 = factory.make_Fabric() |
335 | + parent1 = factory.make_Interface( |
336 | + INTERFACE_TYPE.PHYSICAL, vlan=fabric1.get_default_vlan()) |
337 | + vlan1 = factory.make_VLAN(fabric=fabric1) |
338 | + iface1 = factory.make_Interface( |
339 | + INTERFACE_TYPE.VLAN, vlan=vlan1, parents=[parent1]) |
340 | + fabric2 = factory.make_Fabric() |
341 | + parent2 = factory.make_Interface( |
342 | + INTERFACE_TYPE.PHYSICAL, vlan=fabric2.get_default_vlan()) |
343 | + vlan2 = factory.make_VLAN(fabric=fabric2) |
344 | + iface2 = factory.make_Interface( |
345 | + INTERFACE_TYPE.VLAN, vlan=vlan2, parents=[parent2]) |
346 | + self.assertItemsEqual( |
347 | + Interface.objects.filter_by_specifiers( |
348 | + "vid:%s" % vlan1.vid), [iface1]) |
349 | + self.assertItemsEqual( |
350 | + Interface.objects.filter_by_specifiers( |
351 | + "vid:%s" % vlan2.vid), [iface2]) |
352 | + self.assertItemsEqual( |
353 | + Interface.objects.filter_by_specifiers( |
354 | + ["vid:%s" % vlan1.vid, "vid:%s" % vlan2.vid]), |
355 | [iface1, iface2]) |
356 | |
357 | def test__filter_by_specifiers_matches_vlan(self): |
358 | - iface1 = factory.make_Interface() |
359 | - iface2 = factory.make_Interface() |
360 | - self.assertItemsEqual( |
361 | - Interface.objects.filter_by_specifiers( |
362 | - "vlan:%s" % iface1.vlan.vid), [iface1]) |
363 | - self.assertItemsEqual( |
364 | - Interface.objects.filter_by_specifiers( |
365 | - "vlan:%s" % iface2.vlan.vid), [iface2]) |
366 | - self.assertItemsEqual( |
367 | - Interface.objects.filter_by_specifiers( |
368 | - ["vlan:%s" % iface1.vlan.vid, "vlan:%s" % iface2.vlan.vid]), |
369 | + fabric1 = factory.make_Fabric() |
370 | + parent1 = factory.make_Interface( |
371 | + INTERFACE_TYPE.PHYSICAL, vlan=fabric1.get_default_vlan()) |
372 | + vlan1 = factory.make_VLAN(fabric=fabric1) |
373 | + iface1 = factory.make_Interface( |
374 | + INTERFACE_TYPE.VLAN, vlan=vlan1, parents=[parent1]) |
375 | + fabric2 = factory.make_Fabric() |
376 | + parent2 = factory.make_Interface( |
377 | + INTERFACE_TYPE.PHYSICAL, vlan=fabric2.get_default_vlan()) |
378 | + vlan2 = factory.make_VLAN(fabric=fabric2) |
379 | + iface2 = factory.make_Interface( |
380 | + INTERFACE_TYPE.VLAN, vlan=vlan2, parents=[parent2]) |
381 | + self.assertItemsEqual( |
382 | + Interface.objects.filter_by_specifiers( |
383 | + "vlan:%s" % vlan1.vid), [iface1]) |
384 | + self.assertItemsEqual( |
385 | + Interface.objects.filter_by_specifiers( |
386 | + "vlan:%s" % vlan2.vid), [iface2]) |
387 | + self.assertItemsEqual( |
388 | + Interface.objects.filter_by_specifiers( |
389 | + ["vlan:%s" % vlan1.vid, "vlan:%s" % vlan2.vid]), |
390 | [iface1, iface2]) |
391 | |
392 | def test__filter_by_specifiers_matches_subnet_specifier(self): |
393 | @@ -1401,7 +1421,7 @@ |
394 | 1, interface.ip_addresses.count(), |
395 | "Should only have one IP address assigned.") |
396 | |
397 | - def test__creates_link_up_to_discovered_subnet(self): |
398 | + def test__creates_link_up_to_discovered_subnet_on_same_vlan(self): |
399 | interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
400 | subnet = factory.make_Subnet(vlan=interface.vlan) |
401 | factory.make_StaticIPAddress( |
402 | @@ -1413,6 +1433,18 @@ |
403 | self.assertIsNone(link_ip.ip) |
404 | self.assertEqual(subnet, link_ip.subnet) |
405 | |
406 | + def test__creates_link_up_to_no_subnet_when_on_different_vlan(self): |
407 | + interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
408 | + subnet = factory.make_Subnet() |
409 | + factory.make_StaticIPAddress( |
410 | + alloc_type=IPADDRESS_TYPE.DISCOVERED, ip="", |
411 | + subnet=subnet, interface=interface) |
412 | + interface.ensure_link_up() |
413 | + link_ip = interface.ip_addresses.filter( |
414 | + alloc_type=IPADDRESS_TYPE.STICKY).first() |
415 | + self.assertIsNone(link_ip.ip) |
416 | + self.assertIsNone(link_ip.subnet) |
417 | + |
418 | def test__creates_link_up_to_no_subnet(self): |
419 | interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) |
420 | interface.ensure_link_up() |
421 | |
422 | === modified file 'src/maasserver/testing/factory.py' |
423 | --- src/maasserver/testing/factory.py 2016-03-02 13:42:26 +0000 |
424 | +++ src/maasserver/testing/factory.py 2016-03-03 21:19:22 +0000 |
425 | @@ -413,14 +413,7 @@ |
426 | subnet=subnet, interface=rack_interface) |
427 | else: |
428 | ip_address = existing_static_ips[0] |
429 | - current_ip_addresses = [ |
430 | - sip.ip |
431 | - for sip in StaticIPAddress.objects.filter( |
432 | - subnet=ip_address.subnet) |
433 | - if sip.ip |
434 | - ] |
435 | - bmc_ip_address = self.pick_ip_in_Subnet( |
436 | - ip_address.subnet, but_not=current_ip_addresses) |
437 | + bmc_ip_address = self.pick_ip_in_Subnet(ip_address.subnet) |
438 | node.power_parameters = { |
439 | "power_address": "qemu+ssh://user@%s/system" % bmc_ip_address |
440 | } |
441 | @@ -610,8 +603,12 @@ |
442 | if vlan is None and subnet is not None: |
443 | vlan = subnet.vlan |
444 | if vlan is None: |
445 | + if fabric is None: |
446 | + fabric = factory.make_Fabric() |
447 | + vlan = fabric.get_default_vlan() |
448 | dhcp_on = with_dhcp_rack_primary or with_dhcp_rack_secondary |
449 | - vlan = self.make_VLAN(fabric=fabric, dhcp_on=dhcp_on) |
450 | + vlan.dhcp_on = dhcp_on |
451 | + vlan.save() |
452 | if subnet is None: |
453 | subnet = self.make_Subnet(vlan=vlan, cidr=cidr) |
454 | boot_interface = self.make_Interface( |
455 | @@ -661,7 +658,7 @@ |
456 | secondary_rack = vlan.secondary_rack |
457 | vlan.secondary_rack = secondary_rack |
458 | vlan.save() |
459 | - return node |
460 | + return reload_object(node) |
461 | |
462 | UNDEFINED = float('NaN') |
463 | |
464 | @@ -860,7 +857,19 @@ |
465 | if iftype is None: |
466 | iftype = INTERFACE_TYPE.PHYSICAL |
467 | if vlan is None: |
468 | - vlan = self.make_VLAN(fabric=fabric) |
469 | + if fabric is not None: |
470 | + if iftype == INTERFACE_TYPE.VLAN: |
471 | + vlan = self.make_VLAN(fabric=fabric) |
472 | + else: |
473 | + vlan = fabric.get_default_vlan() |
474 | + else: |
475 | + if iftype == INTERFACE_TYPE.VLAN and parents: |
476 | + vlan = self.make_VLAN(fabric=parents[0].vlan.fabric) |
477 | + elif iftype == INTERFACE_TYPE.BOND and parents: |
478 | + vlan = parents[0].vlan |
479 | + else: |
480 | + fabric = self.make_Fabric() |
481 | + vlan = fabric.get_default_vlan() |
482 | if (mac_address is None and |
483 | iftype in [ |
484 | INTERFACE_TYPE.PHYSICAL, |
This is only the backend part of this. I need to modify the API and UI to allow only the fabric to be changed on the controller interfaces. All other API's for controllers should not be allowed.