Merge lp:~blake-rouse/maas/fix-1566336-1.9 into lp:maas/1.9

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 4552
Proposed branch: lp:~blake-rouse/maas/fix-1566336-1.9
Merge into: lp:maas/1.9
Diff against target: 1205 lines (+409/-138)
10 files modified
src/maasserver/api/tests/test_interfaces.py (+21/-13)
src/maasserver/forms_interface.py (+55/-12)
src/maasserver/models/interface.py (+3/-2)
src/maasserver/models/signals/interfaces.py (+58/-18)
src/maasserver/models/signals/tests/test_interfaces.py (+46/-0)
src/maasserver/models/tests/test_interface.py (+55/-23)
src/maasserver/models/tests/test_staticipaddress.py (+3/-0)
src/maasserver/testing/factory.py (+17/-4)
src/maasserver/tests/test_forms_interface.py (+137/-57)
src/maasserver/websockets/handlers/tests/test_node.py (+14/-9)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-1566336-1.9
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+291009@code.launchpad.net

Commit message

Remove links on parents when added to a bond. Only allow physical interface and bond interface to be attached to the untagged VLAN on a fabric.

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

Self-approving backport.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api/tests/test_interfaces.py'
--- src/maasserver/api/tests/test_interfaces.py 2015-12-04 21:23:28 +0000
+++ src/maasserver/api/tests/test_interfaces.py 2016-04-05 16:02:24 +0000
@@ -56,14 +56,14 @@
56def make_complex_interface(node, name=None):56def make_complex_interface(node, name=None):
57 """Makes interface with parents and children."""57 """Makes interface with parents and children."""
58 fabric = factory.make_Fabric()58 fabric = factory.make_Fabric()
59 vlan_5 = factory.make_VLAN(vid=5, fabric=fabric)59 untagged = fabric.get_default_vlan()
60 nic_0 = factory.make_Interface(60 nic_0 = factory.make_Interface(
61 INTERFACE_TYPE.PHYSICAL, vlan=vlan_5, node=node)61 INTERFACE_TYPE.PHYSICAL, vlan=untagged, node=node)
62 nic_1 = factory.make_Interface(62 nic_1 = factory.make_Interface(
63 INTERFACE_TYPE.PHYSICAL, vlan=vlan_5, node=node)63 INTERFACE_TYPE.PHYSICAL, vlan=untagged, node=node)
64 parents = [nic_0, nic_1]64 parents = [nic_0, nic_1]
65 bond_interface = factory.make_Interface(65 bond_interface = factory.make_Interface(
66 INTERFACE_TYPE.BOND, mac_address=nic_0.mac_address, vlan=vlan_5,66 INTERFACE_TYPE.BOND, mac_address=nic_0.mac_address, vlan=untagged,
67 parents=parents, name=name)67 parents=parents, name=name)
68 vlan_10 = factory.make_VLAN(vid=10, fabric=fabric)68 vlan_10 = factory.make_VLAN(vid=10, fabric=fabric)
69 vlan_nic_10 = factory.make_Interface(69 vlan_nic_10 = factory.make_Interface(
@@ -117,7 +117,8 @@
117 node = factory.make_Node(status=status)117 node = factory.make_Node(status=status)
118 mac = factory.make_mac_address()118 mac = factory.make_mac_address()
119 name = factory.make_name("eth")119 name = factory.make_name("eth")
120 vlan = factory.make_VLAN()120 fabric = factory.make_Fabric()
121 vlan = fabric.get_default_vlan()
121 tags = [122 tags = [
122 factory.make_name("tag")123 factory.make_name("tag")
123 for _ in range(3)124 for _ in range(3)
@@ -150,7 +151,8 @@
150 owner=self.logged_in_user, installable=False, parent=parent)151 owner=self.logged_in_user, installable=False, parent=parent)
151 mac = factory.make_mac_address()152 mac = factory.make_mac_address()
152 name = factory.make_name("eth")153 name = factory.make_name("eth")
153 vlan = factory.make_VLAN()154 fabric = factory.make_Fabric()
155 vlan = fabric.get_default_vlan()
154 tags = [156 tags = [
155 factory.make_name("tag")157 factory.make_name("tag")
156 for _ in range(3)158 for _ in range(3)
@@ -183,7 +185,8 @@
183 node = factory.make_Node(status=status)185 node = factory.make_Node(status=status)
184 mac = factory.make_mac_address()186 mac = factory.make_mac_address()
185 name = factory.make_name("eth")187 name = factory.make_name("eth")
186 vlan = factory.make_VLAN()188 fabric = factory.make_Fabric()
189 vlan = fabric.get_default_vlan()
187 tags = [190 tags = [
188 factory.make_name("tag")191 factory.make_name("tag")
189 for _ in range(3)192 for _ in range(3)
@@ -279,7 +282,8 @@
279 interface_on_other_node = factory.make_Interface(282 interface_on_other_node = factory.make_Interface(
280 INTERFACE_TYPE.PHYSICAL)283 INTERFACE_TYPE.PHYSICAL)
281 name = factory.make_name("eth")284 name = factory.make_name("eth")
282 vlan = factory.make_VLAN()285 fabric = factory.make_Fabric()
286 vlan = fabric.get_default_vlan()
283 uri = get_interfaces_uri(node)287 uri = get_interfaces_uri(node)
284 response = self.client.post(uri, {288 response = self.client.post(uri, {
285 "op": "create_physical",289 "op": "create_physical",
@@ -299,7 +303,8 @@
299 self.become_admin()303 self.become_admin()
300 for status in (NODE_STATUS.READY, NODE_STATUS.BROKEN):304 for status in (NODE_STATUS.READY, NODE_STATUS.BROKEN):
301 node = factory.make_Node(status=status)305 node = factory.make_Node(status=status)
302 vlan = factory.make_VLAN()306 fabric = factory.make_Fabric()
307 vlan = fabric.get_default_vlan()
303 parent_1_iface = factory.make_Interface(308 parent_1_iface = factory.make_Interface(
304 INTERFACE_TYPE.PHYSICAL, vlan=vlan, node=node)309 INTERFACE_TYPE.PHYSICAL, vlan=vlan, node=node)
305 parent_2_iface = factory.make_Interface(310 parent_2_iface = factory.make_Interface(
@@ -402,7 +407,7 @@
402 self.assertEqual(407 self.assertEqual(
403 httplib.CONFLICT, response.status_code, response.content)408 httplib.CONFLICT, response.status_code, response.content)
404409
405 def test_create_bond_requires_name_vlan_and_parents(self):410 def test_create_bond_requires_name_and_parents(self):
406 self.become_admin()411 self.become_admin()
407 node = factory.make_Node(status=NODE_STATUS.READY)412 node = factory.make_Node(status=NODE_STATUS.READY)
408 uri = get_interfaces_uri(node)413 uri = get_interfaces_uri(node)
@@ -415,7 +420,6 @@
415 self.assertEquals({420 self.assertEquals({
416 "mac_address": ["This field cannot be blank."],421 "mac_address": ["This field cannot be blank."],
417 "name": ["This field is required."],422 "name": ["This field is required."],
418 "vlan": ["This field is required."],
419 "parents": ["A Bond interface must have one or more parents."],423 "parents": ["A Bond interface must have one or more parents."],
420 }, json.loads(response.content))424 }, json.loads(response.content))
421425
@@ -634,7 +638,8 @@
634 interface = factory.make_Interface(638 interface = factory.make_Interface(
635 INTERFACE_TYPE.PHYSICAL, node=node)639 INTERFACE_TYPE.PHYSICAL, node=node)
636 new_name = factory.make_name("name")640 new_name = factory.make_name("name")
637 new_vlan = factory.make_VLAN()641 new_fabric = factory.make_Fabric()
642 new_vlan = new_fabric.get_default_vlan()
638 uri = get_interface_uri(interface)643 uri = get_interface_uri(interface)
639 response = self.client.put(uri, {644 response = self.client.put(uri, {
640 "name": new_name,645 "name": new_name,
@@ -653,7 +658,8 @@
653 interface = factory.make_Interface(658 interface = factory.make_Interface(
654 INTERFACE_TYPE.PHYSICAL, node=device)659 INTERFACE_TYPE.PHYSICAL, node=device)
655 new_name = factory.make_name("name")660 new_name = factory.make_name("name")
656 new_vlan = factory.make_VLAN()661 new_fabric = factory.make_Fabric()
662 new_vlan = new_fabric.get_default_vlan()
657 uri = get_interface_uri(interface)663 uri = get_interface_uri(interface)
658 response = self.client.put(uri, {664 response = self.client.put(uri, {
659 "name": new_name,665 "name": new_name,
@@ -688,9 +694,11 @@
688 node)694 node)
689 physical_interface = factory.make_Interface(695 physical_interface = factory.make_Interface(
690 INTERFACE_TYPE.PHYSICAL, node=node)696 INTERFACE_TYPE.PHYSICAL, node=node)
697 new_vlan = factory.make_VLAN(fabric=physical_interface.vlan.fabric)
691 uri = get_interface_uri(vlan_10)698 uri = get_interface_uri(vlan_10)
692 response = self.client.put(uri, {699 response = self.client.put(uri, {
693 "parent": physical_interface.id,700 "parent": physical_interface.id,
701 "vlan": new_vlan.id,
694 })702 })
695 self.assertEqual(703 self.assertEqual(
696 httplib.OK, response.status_code, response.content)704 httplib.OK, response.status_code, response.content)
697705
=== modified file 'src/maasserver/forms_interface.py'
--- src/maasserver/forms_interface.py 2015-10-31 23:55:19 +0000
+++ src/maasserver/forms_interface.py 2016-04-05 16:02:24 +0000
@@ -196,6 +196,13 @@
196 msg = "A physical interface cannot have parents."196 msg = "A physical interface cannot have parents."
197 raise ValidationError({'parents': [msg]})197 raise ValidationError({'parents': [msg]})
198198
199 def clean_vlan(self):
200 new_vlan = self.cleaned_data.get('vlan')
201 if new_vlan and new_vlan.fabric.get_default_vlan() != new_vlan:
202 raise ValidationError(
203 "A physical interface can only belong to an untagged VLAN.")
204 return new_vlan
205
199 def clean(self):206 def clean(self):
200 cleaned_data = super(PhysicalInterfaceForm, self).clean()207 cleaned_data = super(PhysicalInterfaceForm, self).clean()
201 new_name = cleaned_data.get('name')208 new_name = cleaned_data.get('name')
@@ -233,11 +240,25 @@
233 "in a bond.")240 "in a bond.")
234 return parents241 return parents
235242
243 def clean_vlan(self):
244 new_vlan = self.cleaned_data.get('vlan')
245 if new_vlan and new_vlan.fabric.get_default_vlan() == new_vlan:
246 raise ValidationError(
247 "A VLAN interface can only belong to a tagged VLAN.")
248 return new_vlan
249
236 def clean(self):250 def clean(self):
237 cleaned_data = super(VLANInterfaceForm, self).clean()251 cleaned_data = super(VLANInterfaceForm, self).clean()
238 if self.fields_ok(['vlan', 'parents']):252 if self.fields_ok(['vlan', 'parents']):
239 new_vlan = self.cleaned_data.get('vlan')253 new_vlan = self.cleaned_data.get('vlan')
240 if new_vlan:254 if new_vlan:
255 # VLAN needs to be the in the same fabric as the parent.
256 parent = self.cleaned_data.get('parents')[0]
257 if parent.vlan.fabric_id != new_vlan.fabric_id:
258 set_form_error(
259 self, "vlan",
260 "A VLAN interface can only belong to a tagged VLAN on "
261 "the same fabric as its parent interface.")
241 name = build_vlan_interface_name(262 name = build_vlan_interface_name(
242 self.cleaned_data.get('parents').first(), new_vlan)263 self.cleaned_data.get('parents').first(), new_vlan)
243 self.clean_interface_name_uniqueness(name)264 self.clean_interface_name_uniqueness(name)
@@ -281,6 +302,15 @@
281 'name',302 'name',
282 )303 )
283304
305 def __init__(self, *args, **kwargs):
306 super(BondInterfaceForm, self).__init__(*args, **kwargs)
307 # Allow VLAN to be blank when creating.
308 instance = kwargs.get("instance", None)
309 if instance is not None and instance.id is not None:
310 self.fields['vlan'].required = True
311 else:
312 self.fields['vlan'].required = False
313
284 def clean_parents(self):314 def clean_parents(self):
285 parents = self.get_clean_parents()315 parents = self.get_clean_parents()
286 if parents is None:316 if parents is None:
@@ -290,9 +320,16 @@
290 raise ValidationError({'parents': [msg]})320 raise ValidationError({'parents': [msg]})
291 return parents321 return parents
292322
323 def clean_vlan(self):
324 new_vlan = self.cleaned_data.get('vlan')
325 if new_vlan and new_vlan.fabric.get_default_vlan() != new_vlan:
326 raise ValidationError(
327 "A bond interface can only belong to an untagged VLAN.")
328 return new_vlan
329
293 def clean(self):330 def clean(self):
294 cleaned_data = super(BondInterfaceForm, self).clean()331 cleaned_data = super(BondInterfaceForm, self).clean()
295 if self.fields_ok(['parents']):332 if self.fields_ok(['vlan', 'parents']):
296 parents = self.cleaned_data.get('parents')333 parents = self.cleaned_data.get('parents')
297 # Set the mac_address if its missing and the interface is being334 # Set the mac_address if its missing and the interface is being
298 # created.335 # created.
@@ -333,6 +370,23 @@
333 self, 'parents',370 self, 'parents',
334 "%s is already in-use by another interface." % (371 "%s is already in-use by another interface." % (
335 ', '.join(sorted(parents_with_other_children))))372 ', '.join(sorted(parents_with_other_children))))
373
374 # When creating the bond set VLAN to the same as the parents
375 # and check that the parents all belong to the same VLAN.
376 if self.instance.id is None:
377 vlan = self.cleaned_data.get('vlan')
378 if vlan is None:
379 vlan = parents[0].vlan
380 self.cleaned_data['vlan'] = vlan
381 parent_vlans = {
382 parent.vlan
383 for parent in parents
384 }
385 if parent_vlans != set([vlan]):
386 set_form_error(
387 self, 'parents',
388 "All parents must belong to the same VLAN.")
389
336 return cleaned_data390 return cleaned_data
337391
338 def set_extra_parameters(self, interface, created):392 def set_extra_parameters(self, interface, created):
@@ -356,17 +410,6 @@
356 elif created:410 elif created:
357 interface.params[bond_field] = self.fields[bond_field].initial411 interface.params[bond_field] = self.fields[bond_field].initial
358412
359 def save(self, *args, **kwargs):
360 """Persist the interface into the database."""
361 created = self.instance.id is None
362 interface = super(BondInterfaceForm, self).save()
363 if created:
364 # Bond was created we remove all the links on the parent interfaces
365 # and ensure that the bond has atleast a LINK_UP.
366 for parent in interface.parents.all():
367 parent.clear_all_links(clearing_config=True)
368 return interface
369
370413
371INTERFACE_FORM_MAPPING = {414INTERFACE_FORM_MAPPING = {
372 INTERFACE_TYPE.PHYSICAL: PhysicalInterfaceForm,415 INTERFACE_TYPE.PHYSICAL: PhysicalInterfaceForm,
373416
=== modified file 'src/maasserver/models/interface.py'
--- src/maasserver/models/interface.py 2015-12-11 00:24:18 +0000
+++ src/maasserver/models/interface.py 2016-04-05 16:02:24 +0000
@@ -922,11 +922,12 @@
922 # Nothing to do, already has links.922 # Nothing to do, already has links.
923 return923 return
924 else:924 else:
925 # Use an associated subnet if it exists, else it will just be a925 # Use an associated subnet if it exists and its on the same VLAN
926 # the interface is currently connected, else it will just be a
926 # LINK_UP without a subnet.927 # LINK_UP without a subnet.
927 discovered_address = self.ip_addresses.filter(928 discovered_address = self.ip_addresses.filter(
928 alloc_type=IPADDRESS_TYPE.DISCOVERED,929 alloc_type=IPADDRESS_TYPE.DISCOVERED,
929 subnet__isnull=False).first()930 subnet__vlan=self.vlan).first()
930 if discovered_address is not None:931 if discovered_address is not None:
931 subnet = discovered_address.subnet932 subnet = discovered_address.subnet
932 else:933 else:
933934
=== modified file 'src/maasserver/models/signals/interfaces.py'
--- src/maasserver/models/signals/interfaces.py 2015-11-03 12:38:17 +0000
+++ src/maasserver/models/signals/interfaces.py 2016-04-05 16:02:24 +0000
@@ -14,6 +14,7 @@
14__metaclass__ = type14__metaclass__ = type
15__all__ = []15__all__ = []
1616
17from django.db.models.signals import post_save
17from maasserver.enum import (18from maasserver.enum import (
18 INTERFACE_TYPE,19 INTERFACE_TYPE,
19 IPADDRESS_TYPE,20 IPADDRESS_TYPE,
@@ -27,6 +28,14 @@
27from maasserver.utils.signals import connect_to_field_change28from maasserver.utils.signals import connect_to_field_change
2829
2930
31INTERFACE_CLASSES = [
32 Interface,
33 PhysicalInterface,
34 BondInterface,
35 VLANInterface,
36]
37
38
30def interface_enabled_or_disabled(instance, old_values, **kwargs):39def interface_enabled_or_disabled(instance, old_values, **kwargs):
31 """When an interface is enabled be sure at minimum a LINK_UP is created.40 """When an interface is enabled be sure at minimum a LINK_UP is created.
32 When an interface is disabled make sure that all its links are removed,41 When an interface is disabled make sure that all its links are removed,
@@ -52,12 +61,10 @@
52 ip_address, clearing_config=True)61 ip_address, clearing_config=True)
5362
5463
55connect_to_field_change(64for klass in INTERFACE_CLASSES:
56 interface_enabled_or_disabled,65 connect_to_field_change(
57 Interface, ['enabled'], delete=False)66 interface_enabled_or_disabled,
58connect_to_field_change(67 klass, ['enabled'], delete=False)
59 interface_enabled_or_disabled,
60 PhysicalInterface, ['enabled'], delete=False)
6168
6269
63def interface_mtu_params_update(instance, old_values, **kwargs):70def interface_mtu_params_update(instance, old_values, **kwargs):
@@ -118,15 +125,48 @@
118 parent.save()125 parent.save()
119126
120127
121connect_to_field_change(128for klass in INTERFACE_CLASSES:
122 interface_mtu_params_update,129 connect_to_field_change(
123 Interface, ['params'], delete=False)130 interface_mtu_params_update,
124connect_to_field_change(131 klass, ['params'], delete=False)
125 interface_mtu_params_update,132
126 PhysicalInterface, ['params'], delete=False)133
127connect_to_field_change(134def update_bond_parents(sender, instance, created, **kwargs):
128 interface_mtu_params_update,135 """Update bond parents when interface created."""
129 BondInterface, ['params'], delete=False)136 if instance.type == INTERFACE_TYPE.BOND:
130connect_to_field_change(137 for parent in instance.parents.all():
131 interface_mtu_params_update,138 # Make sure the parent has not links as well, just to be sure.
132 VLANInterface, ['params'], delete=False)139 parent.clear_all_links(clearing_config=True)
140 if parent.vlan != instance.vlan:
141 parent.vlan = instance.vlan
142 parent.save()
143
144
145for klass in INTERFACE_CLASSES:
146 post_save.connect(
147 update_bond_parents, sender=klass)
148
149
150def interface_vlan_update(instance, old_values, **kwargs):
151 """When an interfaces VLAN is changed we need to remove all
152 links if the VLAN is different.
153 """
154 new_vlan_id = instance.vlan_id
155 [old_vlan_id] = old_values
156 if new_vlan_id == old_vlan_id:
157 # Nothing changed do nothing.
158 return
159 if instance.node is None:
160 # Not assigned to a node. Nothing to do.
161 return
162
163 # Interface VLAN was changed on a machine or device. Remove all its
164 # links except the DISCOVERED ones.
165 instance.ip_addresses.exclude(
166 alloc_type=IPADDRESS_TYPE.DISCOVERED).delete()
167
168
169for klass in INTERFACE_CLASSES:
170 connect_to_field_change(
171 interface_vlan_update,
172 klass, ['vlan_id'], delete=False)
133173
=== modified file 'src/maasserver/models/signals/tests/test_interfaces.py'
--- src/maasserver/models/signals/tests/test_interfaces.py 2015-11-03 12:38:17 +0000
+++ src/maasserver/models/signals/tests/test_interfaces.py 2016-04-05 16:02:24 +0000
@@ -132,3 +132,49 @@
132 self.assertEquals({132 self.assertEquals({
133 'mtu': bond_mtu,133 'mtu': bond_mtu,
134 }, reload_object(physical3_interface).params)134 }, reload_object(physical3_interface).params)
135
136
137class TestUpdateBondParents(MAASServerTestCase):
138
139 def test__updates_bond_parents(self):
140 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
141 parent2 = factory.make_Interface(
142 INTERFACE_TYPE.PHYSICAL, node=parent1.node)
143 bond = factory.make_Interface(
144 INTERFACE_TYPE.BOND, parents=[parent1, parent2])
145 self.assertEqual(bond.vlan, reload_object(parent1).vlan)
146 self.assertEqual(bond.vlan, reload_object(parent2).vlan)
147
148 def test__update_bond_clears_parent_links(self):
149 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
150 parent2 = factory.make_Interface(
151 INTERFACE_TYPE.PHYSICAL, node=parent1.node)
152 static_ip = factory.make_StaticIPAddress(interface=parent1)
153 factory.make_Interface(
154 INTERFACE_TYPE.BOND, parents=[parent1, parent2])
155 self.assertIsNone(reload_object(static_ip))
156
157
158class TestInterfaceVLANUpdate(MAASServerTestCase):
159
160 scenarios = (
161 ("node", {
162 "maker": factory.make_Node,
163 }),
164 ("device", {
165 "maker": factory.make_Device,
166 }),
167 )
168
169 def test__removes_links(self):
170 node = self.maker()
171 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
172 static_ip = factory.make_StaticIPAddress(interface=interface)
173 discovered_ip = factory.make_StaticIPAddress(
174 alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=interface)
175 new_fabric = factory.make_Fabric()
176 new_vlan = new_fabric.get_default_vlan()
177 interface.vlan = new_vlan
178 interface.save()
179 self.assertIsNone(reload_object(static_ip))
180 self.assertIsNotNone(reload_object(discovered_ip))
135181
=== modified file 'src/maasserver/models/tests/test_interface.py'
--- src/maasserver/models/tests/test_interface.py 2015-12-11 00:24:23 +0000
+++ src/maasserver/models/tests/test_interface.py 2016-04-05 16:02:24 +0000
@@ -230,31 +230,51 @@
230 ["id:%s" % iface1.id, "id:%s" % iface2.id]), [iface1, iface2])230 ["id:%s" % iface1.id, "id:%s" % iface2.id]), [iface1, iface2])
231231
232 def test__filter_by_specifiers_matches_vid(self):232 def test__filter_by_specifiers_matches_vid(self):
233 iface1 = factory.make_Interface()233 fabric1 = factory.make_Fabric()
234 iface2 = factory.make_Interface()234 parent1 = factory.make_Interface(
235 self.assertItemsEqual(235 INTERFACE_TYPE.PHYSICAL, vlan=fabric1.get_default_vlan())
236 Interface.objects.filter_by_specifiers(236 vlan1 = factory.make_VLAN(fabric=fabric1)
237 "vid:%s" % iface1.vlan.vid), [iface1])237 iface1 = factory.make_Interface(
238 self.assertItemsEqual(238 INTERFACE_TYPE.VLAN, vlan=vlan1, parents=[parent1])
239 Interface.objects.filter_by_specifiers(239 fabric2 = factory.make_Fabric()
240 "vid:%s" % iface2.vlan.vid), [iface2])240 parent2 = factory.make_Interface(
241 self.assertItemsEqual(241 INTERFACE_TYPE.PHYSICAL, vlan=fabric2.get_default_vlan())
242 Interface.objects.filter_by_specifiers(242 vlan2 = factory.make_VLAN(fabric=fabric2)
243 ["vid:%s" % iface1.vlan.vid, "vid:%s" % iface2.vlan.vid]),243 iface2 = factory.make_Interface(
244 INTERFACE_TYPE.VLAN, vlan=vlan2, parents=[parent2])
245 self.assertItemsEqual(
246 Interface.objects.filter_by_specifiers(
247 "vid:%s" % vlan1.vid), [iface1])
248 self.assertItemsEqual(
249 Interface.objects.filter_by_specifiers(
250 "vid:%s" % vlan2.vid), [iface2])
251 self.assertItemsEqual(
252 Interface.objects.filter_by_specifiers(
253 ["vid:%s" % vlan1.vid, "vid:%s" % vlan2.vid]),
244 [iface1, iface2])254 [iface1, iface2])
245255
246 def test__filter_by_specifiers_matches_vlan(self):256 def test__filter_by_specifiers_matches_vlan(self):
247 iface1 = factory.make_Interface()257 fabric1 = factory.make_Fabric()
248 iface2 = factory.make_Interface()258 parent1 = factory.make_Interface(
249 self.assertItemsEqual(259 INTERFACE_TYPE.PHYSICAL, vlan=fabric1.get_default_vlan())
250 Interface.objects.filter_by_specifiers(260 vlan1 = factory.make_VLAN(fabric=fabric1)
251 "vlan:%s" % iface1.vlan.vid), [iface1])261 iface1 = factory.make_Interface(
252 self.assertItemsEqual(262 INTERFACE_TYPE.VLAN, vlan=vlan1, parents=[parent1])
253 Interface.objects.filter_by_specifiers(263 fabric2 = factory.make_Fabric()
254 "vlan:%s" % iface2.vlan.vid), [iface2])264 parent2 = factory.make_Interface(
255 self.assertItemsEqual(265 INTERFACE_TYPE.PHYSICAL, vlan=fabric2.get_default_vlan())
256 Interface.objects.filter_by_specifiers(266 vlan2 = factory.make_VLAN(fabric=fabric2)
257 ["vlan:%s" % iface1.vlan.vid, "vlan:%s" % iface2.vlan.vid]),267 iface2 = factory.make_Interface(
268 INTERFACE_TYPE.VLAN, vlan=vlan2, parents=[parent2])
269 self.assertItemsEqual(
270 Interface.objects.filter_by_specifiers(
271 "vlan:%s" % vlan1.vid), [iface1])
272 self.assertItemsEqual(
273 Interface.objects.filter_by_specifiers(
274 "vlan:%s" % vlan2.vid), [iface2])
275 self.assertItemsEqual(
276 Interface.objects.filter_by_specifiers(
277 ["vlan:%s" % vlan1.vid, "vlan:%s" % vlan2.vid]),
258 [iface1, iface2])278 [iface1, iface2])
259279
260 def test__filter_by_specifiers_matches_subnet_specifier(self):280 def test__filter_by_specifiers_matches_subnet_specifier(self):
@@ -1515,7 +1535,7 @@
1515 1, interface.ip_addresses.count(),1535 1, interface.ip_addresses.count(),
1516 "Should only have one IP address assigned.")1536 "Should only have one IP address assigned.")
15171537
1518 def test__creates_link_up_to_discovered_subnet(self):1538 def test__creates_link_up_to_discovered_subnet_on_same_vlan(self):
1519 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)1539 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
1520 subnet = factory.make_Subnet(vlan=interface.vlan)1540 subnet = factory.make_Subnet(vlan=interface.vlan)
1521 factory.make_StaticIPAddress(1541 factory.make_StaticIPAddress(
@@ -1527,6 +1547,18 @@
1527 self.assertIsNone(link_ip.ip)1547 self.assertIsNone(link_ip.ip)
1528 self.assertEquals(subnet, link_ip.subnet)1548 self.assertEquals(subnet, link_ip.subnet)
15291549
1550 def test__creates_link_up_to_no_subnet_when_on_different_vlan(self):
1551 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
1552 subnet = factory.make_Subnet()
1553 factory.make_StaticIPAddress(
1554 alloc_type=IPADDRESS_TYPE.DISCOVERED, ip="",
1555 subnet=subnet, interface=interface)
1556 interface.ensure_link_up()
1557 link_ip = interface.ip_addresses.filter(
1558 alloc_type=IPADDRESS_TYPE.STICKY).first()
1559 self.assertIsNone(link_ip.ip)
1560 self.assertIsNone(link_ip.subnet)
1561
1530 def test__creates_link_up_to_no_subnet(self):1562 def test__creates_link_up_to_no_subnet(self):
1531 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)1563 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
1532 interface.ensure_link_up()1564 interface.ensure_link_up()
15331565
=== modified file 'src/maasserver/models/tests/test_staticipaddress.py'
--- src/maasserver/models/tests/test_staticipaddress.py 2015-11-17 00:33:35 +0000
+++ src/maasserver/models/tests/test_staticipaddress.py 2016-04-05 16:02:24 +0000
@@ -805,6 +805,7 @@
805805
806 def test_get_hostname_ip_mapping_prefers_bond_with_no_boot_interface(self):806 def test_get_hostname_ip_mapping_prefers_bond_with_no_boot_interface(self):
807 self.patch_autospec(interface_module, "update_host_maps")807 self.patch_autospec(interface_module, "update_host_maps")
808 self.patch_autospec(interface_module, "remove_host_maps")
808 subnet = factory.make_Subnet(809 subnet = factory.make_Subnet(
809 cidr=unicode(factory.make_ipv4_network().cidr))810 cidr=unicode(factory.make_ipv4_network().cidr))
810 node = factory.make_Node_with_Interface_on_Subnet(811 node = factory.make_Node_with_Interface_on_Subnet(
@@ -835,6 +836,7 @@
835836
836 def test_get_hostname_ip_mapping_prefers_bond_with_boot_interface(self):837 def test_get_hostname_ip_mapping_prefers_bond_with_boot_interface(self):
837 self.patch_autospec(interface_module, "update_host_maps")838 self.patch_autospec(interface_module, "update_host_maps")
839 self.patch_autospec(interface_module, "remove_host_maps")
838 subnet = factory.make_Subnet(840 subnet = factory.make_Subnet(
839 cidr=unicode(factory.make_ipv4_network().cidr))841 cidr=unicode(factory.make_ipv4_network().cidr))
840 node = factory.make_Node_with_Interface_on_Subnet(842 node = factory.make_Node_with_Interface_on_Subnet(
@@ -859,6 +861,7 @@
859861
860 def test_get_hostname_ip_mapping_ignores_bond_without_boot_interface(self):862 def test_get_hostname_ip_mapping_ignores_bond_without_boot_interface(self):
861 self.patch_autospec(interface_module, "update_host_maps")863 self.patch_autospec(interface_module, "update_host_maps")
864 self.patch_autospec(interface_module, "remove_host_maps")
862 subnet = factory.make_Subnet(865 subnet = factory.make_Subnet(
863 cidr=unicode(factory.make_ipv4_network().cidr))866 cidr=unicode(factory.make_ipv4_network().cidr))
864 node = factory.make_Node_with_Interface_on_Subnet(867 node = factory.make_Node_with_Interface_on_Subnet(
865868
=== modified file 'src/maasserver/testing/factory.py'
--- src/maasserver/testing/factory.py 2015-12-11 00:24:23 +0000
+++ src/maasserver/testing/factory.py 2016-04-05 16:02:24 +0000
@@ -335,7 +335,6 @@
335 acquired = node.status in ALLOCATED_NODE_STATUSES335 acquired = node.status in ALLOCATED_NODE_STATUSES
336 self.make_Filesystem(336 self.make_Filesystem(
337 partition=root_partition, mount_point='/', acquired=acquired)337 partition=root_partition, mount_point='/', acquired=acquired)
338
339 # Update the 'updated'/'created' fields with a call to 'update'338 # Update the 'updated'/'created' fields with a call to 'update'
340 # preventing a call to save() from overriding the values.339 # preventing a call to save() from overriding the values.
341 if updated is not None:340 if updated is not None:
@@ -591,7 +590,9 @@
591 node = self.make_Node(590 node = self.make_Node(
592 nodegroup=nodegroup, fabric=fabric, **kwargs)591 nodegroup=nodegroup, fabric=fabric, **kwargs)
593 if vlan is None:592 if vlan is None:
594 vlan = self.make_VLAN(fabric=fabric)593 if fabric is None:
594 fabric = factory.make_Fabric()
595 vlan = fabric.get_default_vlan()
595 if subnet is None:596 if subnet is None:
596 subnet = self.make_Subnet(vlan=vlan, cidr=cidr)597 subnet = self.make_Subnet(vlan=vlan, cidr=cidr)
597 # Check if the subnet already has a managed interface.598 # Check if the subnet already has a managed interface.
@@ -630,7 +631,7 @@
630 self.make_StaticIPAddress(631 self.make_StaticIPAddress(
631 alloc_type=IPADDRESS_TYPE.STICKY, ip="",632 alloc_type=IPADDRESS_TYPE.STICKY, ip="",
632 subnet=subnet, interface=interface)633 subnet=subnet, interface=interface)
633 return node634 return reload_object(node)
634635
635 UNDEFINED = float('NaN')636 UNDEFINED = float('NaN')
636637
@@ -786,7 +787,19 @@
786 if iftype is None:787 if iftype is None:
787 iftype = INTERFACE_TYPE.PHYSICAL788 iftype = INTERFACE_TYPE.PHYSICAL
788 if vlan is None:789 if vlan is None:
789 vlan = self.make_VLAN(fabric=fabric)790 if fabric is not None:
791 if iftype == INTERFACE_TYPE.VLAN:
792 vlan = self.make_VLAN(fabric=fabric)
793 else:
794 vlan = fabric.get_default_vlan()
795 else:
796 if iftype == INTERFACE_TYPE.VLAN and parents:
797 vlan = self.make_VLAN(fabric=parents[0].vlan.fabric)
798 elif iftype == INTERFACE_TYPE.BOND and parents:
799 vlan = parents[0].vlan
800 else:
801 fabric = self.make_Fabric()
802 vlan = fabric.get_default_vlan()
790 if (mac_address is None and803 if (mac_address is None and
791 iftype in [804 iftype in [
792 INTERFACE_TYPE.PHYSICAL,805 INTERFACE_TYPE.PHYSICAL,
793806
=== modified file 'src/maasserver/tests/test_forms_interface.py'
--- src/maasserver/tests/test_forms_interface.py 2015-10-31 23:55:19 +0000
+++ src/maasserver/tests/test_forms_interface.py 2016-04-05 16:02:24 +0000
@@ -32,6 +32,7 @@
32)32)
33from maasserver.models.interface import build_vlan_interface_name33from maasserver.models.interface import build_vlan_interface_name
34from maasserver.testing.factory import factory34from maasserver.testing.factory import factory
35from maasserver.testing.orm import reload_object
35from maasserver.testing.testcase import MAASServerTestCase36from maasserver.testing.testcase import MAASServerTestCase
36from maasserver.utils.forms import compose_invalid_choice_text37from maasserver.utils.forms import compose_invalid_choice_text
37from testtools import ExpectedException38from testtools import ExpectedException
@@ -67,7 +68,8 @@
67 node = factory.make_Node()68 node = factory.make_Node()
68 mac_address = factory.make_mac_address()69 mac_address = factory.make_mac_address()
69 interface_name = 'eth0'70 interface_name = 'eth0'
70 vlan = factory.make_VLAN()71 fabric = factory.make_Fabric()
72 vlan = fabric.get_default_vlan()
71 tags = [73 tags = [
72 factory.make_name("tag")74 factory.make_name("tag")
73 for _ in range(3)75 for _ in range(3)
@@ -93,7 +95,8 @@
93 node = factory.make_Node()95 node = factory.make_Node()
94 mac_address = factory.make_mac_address()96 mac_address = factory.make_mac_address()
95 interface_name = 'eth0'97 interface_name = 'eth0'
96 vlan = factory.make_VLAN()98 fabric = factory.make_Fabric()
99 vlan = fabric.get_default_vlan()
97 tags = [100 tags = [
98 factory.make_name("tag")101 factory.make_name("tag")
99 for _ in range(3)102 for _ in range(3)
@@ -113,7 +116,8 @@
113116
114 def test__requires_mac_address(self):117 def test__requires_mac_address(self):
115 interface_name = 'eth0'118 interface_name = 'eth0'
116 vlan = factory.make_VLAN()119 fabric = factory.make_Fabric()
120 vlan = fabric.get_default_vlan()
117 form = PhysicalInterfaceForm(121 form = PhysicalInterfaceForm(
118 node=factory.make_Node(),122 node=factory.make_Node(),
119 data={123 data={
@@ -128,7 +132,9 @@
128 form.errors['mac_address'][0])132 form.errors['mac_address'][0])
129133
130 def test_rejects_interface_with_duplicate_name(self):134 def test_rejects_interface_with_duplicate_name(self):
131 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)135 fabric = factory.make_Fabric()
136 vlan = fabric.get_default_vlan()
137 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, vlan=vlan)
132 mac_address = factory.make_mac_address()138 mac_address = factory.make_mac_address()
133 form = PhysicalInterfaceForm(139 form = PhysicalInterfaceForm(
134 node=interface.node,140 node=interface.node,
@@ -144,9 +150,30 @@
144 "already has an interface named '%s'." % interface.name,150 "already has an interface named '%s'." % interface.name,
145 form.errors['name'][0])151 form.errors['name'][0])
146152
153 def test_rejects_interface_on_tagged_vlan(self):
154 fabric = factory.make_Fabric()
155 interface = factory.make_Interface(
156 INTERFACE_TYPE.PHYSICAL, vlan=fabric.get_default_vlan())
157 vlan = factory.make_VLAN(fabric=fabric)
158 mac_address = factory.make_mac_address()
159 form = PhysicalInterfaceForm(
160 node=interface.node,
161 data={
162 'name': factory.make_name("eth"),
163 'mac_address': mac_address,
164 'vlan': vlan.id,
165 })
166 self.assertFalse(form.is_valid(), form.errors)
167 self.assertItemsEqual(
168 ['vlan'], form.errors.keys(), form.errors)
169 self.assertIn(
170 "A physical interface can only belong to an untagged VLAN.",
171 form.errors['vlan'][0])
172
147 def test__rejects_parents(self):173 def test__rejects_parents(self):
148 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)174 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
149 vlan = factory.make_VLAN()175 fabric = factory.make_Fabric()
176 vlan = fabric.get_default_vlan()
150 form = PhysicalInterfaceForm(177 form = PhysicalInterfaceForm(
151 node=parent.node,178 node=parent.node,
152 data={179 data={
@@ -166,7 +193,8 @@
166 interface = factory.make_Interface(193 interface = factory.make_Interface(
167 INTERFACE_TYPE.PHYSICAL, name='eth0')194 INTERFACE_TYPE.PHYSICAL, name='eth0')
168 new_name = 'eth1'195 new_name = 'eth1'
169 new_vlan = factory.make_VLAN(vid=33)196 new_fabric = factory.make_Fabric()
197 new_vlan = new_fabric.get_default_vlan()
170 form = PhysicalInterfaceForm(198 form = PhysicalInterfaceForm(
171 instance=interface,199 instance=interface,
172 data={200 data={
@@ -187,7 +215,8 @@
187 node = factory.make_Node()215 node = factory.make_Node()
188 mac_address = factory.make_mac_address()216 mac_address = factory.make_mac_address()
189 interface_name = 'eth0'217 interface_name = 'eth0'
190 vlan = factory.make_VLAN()218 fabric = factory.make_Fabric()
219 vlan = fabric.get_default_vlan()
191 tags = [220 tags = [
192 factory.make_name("tag")221 factory.make_name("tag")
193 for _ in range(3)222 for _ in range(3)
@@ -226,7 +255,8 @@
226 "autoconf": autoconf,255 "autoconf": autoconf,
227 }256 }
228 new_name = 'eth1'257 new_name = 'eth1'
229 new_vlan = factory.make_VLAN(vid=33)258 new_fabric = factory.make_Fabric()
259 new_vlan = new_fabric.get_default_vlan()
230 form = PhysicalInterfaceForm(260 form = PhysicalInterfaceForm(
231 instance=interface,261 instance=interface,
232 data={262 data={
@@ -299,7 +329,7 @@
299329
300 def test__creates_vlan_interface(self):330 def test__creates_vlan_interface(self):
301 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)331 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
302 vlan = factory.make_VLAN(vid=10)332 vlan = factory.make_VLAN(fabric=parent.vlan.fabric, vid=10)
303 form = VLANInterfaceForm(333 form = VLANInterfaceForm(
304 node=parent.node,334 node=parent.node,
305 data={335 data={
@@ -317,7 +347,7 @@
317347
318 def test__create_ensures_link_up(self):348 def test__create_ensures_link_up(self):
319 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)349 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
320 vlan = factory.make_VLAN(vid=10)350 vlan = factory.make_VLAN(fabric=parent.vlan.fabric, vid=10)
321 form = VLANInterfaceForm(351 form = VLANInterfaceForm(
322 node=parent.node,352 node=parent.node,
323 data={353 data={
@@ -330,13 +360,10 @@
330 interface.ip_addresses.filter(alloc_type=IPADDRESS_TYPE.STICKY))360 interface.ip_addresses.filter(alloc_type=IPADDRESS_TYPE.STICKY))
331361
332 def test_rejects_interface_with_duplicate_name(self):362 def test_rejects_interface_with_duplicate_name(self):
333 vlan = factory.make_VLAN(vid=10)363 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
334 parent = factory.make_Interface(364 vlan = factory.make_VLAN(fabric=parent.vlan.fabric, vid=10)
335 INTERFACE_TYPE.PHYSICAL,
336 vlan=vlan)
337 interface = factory.make_Interface(365 interface = factory.make_Interface(
338 INTERFACE_TYPE.VLAN,366 INTERFACE_TYPE.VLAN, vlan=vlan, parents=[parent])
339 vlan=vlan, parents=[parent])
340 form = VLANInterfaceForm(367 form = VLANInterfaceForm(
341 node=parent.node,368 node=parent.node,
342 data={369 data={
@@ -350,6 +377,22 @@
350 "already has an interface named '%s'." % interface.name,377 "already has an interface named '%s'." % interface.name,
351 form.errors['name'][0])378 form.errors['name'][0])
352379
380 def test_rejects_interface_on_default_fabric(self):
381 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
382 vlan = parent.vlan.fabric.get_default_vlan()
383 form = VLANInterfaceForm(
384 node=parent.node,
385 data={
386 'vlan': vlan.id,
387 'parents': [parent.id],
388 })
389 self.assertFalse(form.is_valid(), form.errors)
390 self.assertItemsEqual(
391 ['vlan'], form.errors.keys(), form.errors)
392 self.assertIn(
393 "A VLAN interface can only belong to a tagged VLAN.",
394 form.errors['vlan'][0])
395
353 def test__rejects_no_parents(self):396 def test__rejects_no_parents(self):
354 vlan = factory.make_VLAN(vid=10)397 vlan = factory.make_VLAN(vid=10)
355 form = VLANInterfaceForm(398 form = VLANInterfaceForm(
@@ -365,13 +408,14 @@
365408
366 def test__rejects_vlan_parent(self):409 def test__rejects_vlan_parent(self):
367 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)410 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
411 vlan = factory.make_VLAN(fabric=parent.vlan.fabric, vid=10)
368 vlan_parent = factory.make_Interface(412 vlan_parent = factory.make_Interface(
369 INTERFACE_TYPE.VLAN, parents=[parent])413 INTERFACE_TYPE.VLAN, vlan=vlan, parents=[parent])
370 vlan = factory.make_VLAN(vid=10)414 other_vlan = factory.make_VLAN(fabric=parent.vlan.fabric, vid=11)
371 form = VLANInterfaceForm(415 form = VLANInterfaceForm(
372 node=parent.node,416 node=parent.node,
373 data={417 data={
374 'vlan': vlan.id,418 'vlan': other_vlan.id,
375 'parents': [vlan_parent.id],419 'parents': [vlan_parent.id],
376 })420 })
377 self.assertFalse(form.is_valid(), form.errors)421 self.assertFalse(form.is_valid(), form.errors)
@@ -380,10 +424,27 @@
380 "VLAN interface can't have another VLAN interface as parent.",424 "VLAN interface can't have another VLAN interface as parent.",
381 form.errors['parents'][0])425 form.errors['parents'][0])
382426
427 def test__rejects_vlan_not_on_same_fabric(self):
428 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
429 factory.make_VLAN(fabric=parent.vlan.fabric, vid=10)
430 other_vlan = factory.make_VLAN()
431 form = VLANInterfaceForm(
432 node=parent.node,
433 data={
434 'vlan': other_vlan.id,
435 'parents': [parent.id],
436 })
437 self.assertFalse(form.is_valid(), form.errors)
438 self.assertItemsEqual(['vlan'], form.errors.keys())
439 self.assertIn(
440 "A VLAN interface can only belong to a tagged VLAN on "
441 "the same fabric as its parent interface.",
442 form.errors['vlan'][0])
443
383 def test__rejects_parent_on_bond(self):444 def test__rejects_parent_on_bond(self):
384 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)445 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
385 factory.make_Interface(INTERFACE_TYPE.BOND, parents=[parent])446 bond = factory.make_Interface(INTERFACE_TYPE.BOND, parents=[parent])
386 vlan = factory.make_VLAN(vid=10)447 vlan = factory.make_VLAN(fabric=bond.vlan.fabric, vid=10)
387 form = VLANInterfaceForm(448 form = VLANInterfaceForm(
388 node=parent.node,449 node=parent.node,
389 data={450 data={
@@ -417,7 +478,7 @@
417 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)478 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
418 interface = factory.make_Interface(479 interface = factory.make_Interface(
419 INTERFACE_TYPE.VLAN, parents=[parent])480 INTERFACE_TYPE.VLAN, parents=[parent])
420 new_vlan = factory.make_VLAN(vid=33)481 new_vlan = factory.make_VLAN(fabric=interface.vlan.fabric, vid=33)
421 form = VLANInterfaceForm(482 form = VLANInterfaceForm(
422 instance=interface,483 instance=interface,
423 data={484 data={
@@ -438,15 +499,13 @@
438 def test__error_with_invalid_bond_mode(self):499 def test__error_with_invalid_bond_mode(self):
439 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)500 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
440 parent2 = factory.make_Interface(501 parent2 = factory.make_Interface(
441 INTERFACE_TYPE.PHYSICAL, node=parent1.node)502 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
442 interface_name = factory.make_name()503 interface_name = factory.make_name()
443 vlan = factory.make_VLAN(vid=10)
444 bond_mode = factory.make_name("bond_mode")504 bond_mode = factory.make_name("bond_mode")
445 form = BondInterfaceForm(505 form = BondInterfaceForm(
446 node=parent1.node,506 node=parent1.node,
447 data={507 data={
448 'name': interface_name,508 'name': interface_name,
449 'vlan': vlan.id,
450 'parents': [parent1.id, parent2.id],509 'parents': [parent1.id, parent2.id],
451 'bond_mode': bond_mode,510 'bond_mode': bond_mode,
452 })511 })
@@ -460,14 +519,12 @@
460 def test__creates_bond_interface(self):519 def test__creates_bond_interface(self):
461 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)520 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
462 parent2 = factory.make_Interface(521 parent2 = factory.make_Interface(
463 INTERFACE_TYPE.PHYSICAL, node=parent1.node)522 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
464 interface_name = factory.make_name()523 interface_name = factory.make_name()
465 vlan = factory.make_VLAN(vid=10)
466 form = BondInterfaceForm(524 form = BondInterfaceForm(
467 node=parent1.node,525 node=parent1.node,
468 data={526 data={
469 'name': interface_name,527 'name': interface_name,
470 'vlan': vlan.id,
471 'parents': [parent1.id, parent2.id],528 'parents': [parent1.id, parent2.id],
472 })529 })
473 self.assertTrue(form.is_valid(), form.errors)530 self.assertTrue(form.is_valid(), form.errors)
@@ -484,15 +541,13 @@
484 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)541 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
485 parent1.ensure_link_up()542 parent1.ensure_link_up()
486 parent2 = factory.make_Interface(543 parent2 = factory.make_Interface(
487 INTERFACE_TYPE.PHYSICAL, node=parent1.node)544 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
488 parent2.ensure_link_up()545 parent2.ensure_link_up()
489 interface_name = factory.make_name()546 interface_name = factory.make_name()
490 vlan = factory.make_VLAN(vid=10)
491 form = BondInterfaceForm(547 form = BondInterfaceForm(
492 node=parent1.node,548 node=parent1.node,
493 data={549 data={
494 'name': interface_name,550 'name': interface_name,
495 'vlan': vlan.id,
496 'parents': [parent1.id, parent2.id],551 'parents': [parent1.id, parent2.id],
497 })552 })
498 self.assertTrue(form.is_valid(), form.errors)553 self.assertTrue(form.is_valid(), form.errors)
@@ -511,14 +566,12 @@
511 def test__creates_bond_interface_with_parent_mac_address(self):566 def test__creates_bond_interface_with_parent_mac_address(self):
512 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)567 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
513 parent2 = factory.make_Interface(568 parent2 = factory.make_Interface(
514 INTERFACE_TYPE.PHYSICAL, node=parent1.node)569 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
515 interface_name = factory.make_name()570 interface_name = factory.make_name()
516 vlan = factory.make_VLAN(vid=10)
517 form = BondInterfaceForm(571 form = BondInterfaceForm(
518 node=parent1.node,572 node=parent1.node,
519 data={573 data={
520 'name': interface_name,574 'name': interface_name,
521 'vlan': vlan.id,
522 'parents': [parent1.id, parent2.id],575 'parents': [parent1.id, parent2.id],
523 'mac_address': parent1.mac_address,576 'mac_address': parent1.mac_address,
524 })577 })
@@ -534,14 +587,12 @@
534 def test__creates_bond_interface_with_default_bond_params(self):587 def test__creates_bond_interface_with_default_bond_params(self):
535 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)588 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
536 parent2 = factory.make_Interface(589 parent2 = factory.make_Interface(
537 INTERFACE_TYPE.PHYSICAL, node=parent1.node)590 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
538 interface_name = factory.make_name()591 interface_name = factory.make_name()
539 vlan = factory.make_VLAN(vid=10)
540 form = BondInterfaceForm(592 form = BondInterfaceForm(
541 node=parent1.node,593 node=parent1.node,
542 data={594 data={
543 'name': interface_name,595 'name': interface_name,
544 'vlan': vlan.id,
545 'parents': [parent1.id, parent2.id],596 'parents': [parent1.id, parent2.id],
546 })597 })
547 self.assertTrue(form.is_valid(), form.errors)598 self.assertTrue(form.is_valid(), form.errors)
@@ -558,9 +609,8 @@
558 def test__creates_bond_interface_with_bond_params(self):609 def test__creates_bond_interface_with_bond_params(self):
559 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)610 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
560 parent2 = factory.make_Interface(611 parent2 = factory.make_Interface(
561 INTERFACE_TYPE.PHYSICAL, node=parent1.node)612 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
562 interface_name = factory.make_name()613 interface_name = factory.make_name()
563 vlan = factory.make_VLAN(vid=10)
564 bond_mode = factory.pick_choice(BOND_MODE_CHOICES)614 bond_mode = factory.pick_choice(BOND_MODE_CHOICES)
565 bond_miimon = random.randint(0, 1000)615 bond_miimon = random.randint(0, 1000)
566 bond_downdelay = random.randint(0, 1000)616 bond_downdelay = random.randint(0, 1000)
@@ -572,7 +622,6 @@
572 node=parent1.node,622 node=parent1.node,
573 data={623 data={
574 'name': interface_name,624 'name': interface_name,
575 'vlan': vlan.id,
576 'parents': [parent1.id, parent2.id],625 'parents': [parent1.id, parent2.id],
577 'bond_mode': bond_mode,626 'bond_mode': bond_mode,
578 'bond_miimon': bond_miimon,627 'bond_miimon': bond_miimon,
@@ -593,13 +642,11 @@
593 }, interface.params)642 }, interface.params)
594643
595 def test__rejects_no_parents(self):644 def test__rejects_no_parents(self):
596 vlan = factory.make_VLAN(vid=10)
597 interface_name = factory.make_name()645 interface_name = factory.make_name()
598 form = BondInterfaceForm(646 form = BondInterfaceForm(
599 node=factory.make_Node(),647 node=factory.make_Node(),
600 data={648 data={
601 'name': interface_name,649 'name': interface_name,
602 'vlan': vlan.id,
603 })650 })
604 self.assertFalse(form.is_valid(), form.errors)651 self.assertFalse(form.is_valid(), form.errors)
605 self.assertItemsEqual(['parents', 'mac_address'], form.errors.keys())652 self.assertItemsEqual(['parents', 'mac_address'], form.errors.keys())
@@ -607,21 +654,37 @@
607 "A Bond interface must have one or more parents.",654 "A Bond interface must have one or more parents.",
608 form.errors['parents'][0])655 form.errors['parents'][0])
609656
657 def test__rejects_when_vlan_not_untagged(self):
658 interface_name = factory.make_name()
659 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
660 vlan = factory.make_VLAN(fabric=parent.vlan.fabric)
661 form = BondInterfaceForm(
662 node=parent.node,
663 data={
664 'name': interface_name,
665 'parents': [parent.id],
666 'mac_address': parent.mac_address,
667 'vlan': vlan.id,
668 })
669 self.assertFalse(form.is_valid(), form.errors)
670 self.assertItemsEqual(['vlan'], form.errors.keys())
671 self.assertIn(
672 "A bond interface can only belong to an untagged VLAN.",
673 form.errors['vlan'][0])
674
610 def test__rejects_when_parents_already_have_children(self):675 def test__rejects_when_parents_already_have_children(self):
611 node = factory.make_Node()676 node = factory.make_Node()
612 parent1 = factory.make_Interface(677 parent1 = factory.make_Interface(
613 INTERFACE_TYPE.PHYSICAL, node=node, name="eth0")678 INTERFACE_TYPE.PHYSICAL, node=node, name="eth0")
614 factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[parent1])679 factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[parent1])
615 parent2 = factory.make_Interface(680 parent2 = factory.make_Interface(
616 INTERFACE_TYPE.PHYSICAL, node=node, name="eth1")681 INTERFACE_TYPE.PHYSICAL, node=node, name="eth1", vlan=parent1.vlan)
617 factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[parent2])682 factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[parent2])
618 vlan = factory.make_VLAN(vid=10)
619 interface_name = factory.make_name()683 interface_name = factory.make_name()
620 form = BondInterfaceForm(684 form = BondInterfaceForm(
621 node=node,685 node=node,
622 data={686 data={
623 'name': interface_name,687 'name': interface_name,
624 'vlan': vlan.id,
625 'parents': [parent1.id, parent2.id]688 'parents': [parent1.id, parent2.id]
626 })689 })
627 self.assertFalse(form.is_valid(), form.errors)690 self.assertFalse(form.is_valid(), form.errors)
@@ -629,17 +692,36 @@
629 "eth0, eth1 is already in-use by another interface.",692 "eth0, eth1 is already in-use by another interface.",
630 form.errors['parents'][0])693 form.errors['parents'][0])
631694
695 def test__rejects_when_parents_not_in_same_vlan(self):
696 node = factory.make_Node()
697 parent1 = factory.make_Interface(
698 INTERFACE_TYPE.PHYSICAL, node=node, name="eth0")
699 parent2 = factory.make_Interface(
700 INTERFACE_TYPE.PHYSICAL, node=node, name="eth1")
701 interface_name = factory.make_name()
702 form = BondInterfaceForm(
703 node=node,
704 data={
705 'name': interface_name,
706 'parents': [parent1.id, parent2.id]
707 })
708 self.assertFalse(form.is_valid(), form.errors)
709 self.assertEquals(
710 "All parents must belong to the same VLAN.",
711 form.errors['parents'][0])
712
632 def test__edits_interface(self):713 def test__edits_interface(self):
633 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)714 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
634 parent2 = factory.make_Interface(715 parent2 = factory.make_Interface(
635 INTERFACE_TYPE.PHYSICAL, node=parent1.node)716 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
636 interface = factory.make_Interface(717 interface = factory.make_Interface(
637 INTERFACE_TYPE.BOND,718 INTERFACE_TYPE.BOND,
638 parents=[parent1, parent2])719 parents=[parent1, parent2])
639 new_vlan = factory.make_VLAN(vid=33)720 new_fabric = factory.make_Fabric()
721 new_vlan = new_fabric.get_default_vlan()
640 new_name = factory.make_name()722 new_name = factory.make_name()
641 new_parent = factory.make_Interface(723 new_parent = factory.make_Interface(
642 INTERFACE_TYPE.PHYSICAL, node=parent1.node)724 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
643 form = BondInterfaceForm(725 form = BondInterfaceForm(
644 instance=interface,726 instance=interface,
645 data={727 data={
@@ -656,6 +738,10 @@
656 vlan=new_vlan, type=INTERFACE_TYPE.BOND))738 vlan=new_vlan, type=INTERFACE_TYPE.BOND))
657 self.assertItemsEqual(739 self.assertItemsEqual(
658 [parent1, parent2, new_parent], interface.parents.all())740 [parent1, parent2, new_parent], interface.parents.all())
741 self.assertItemsEqual([new_vlan], set(
742 reload_object(parent).vlan
743 for parent in [parent1, parent2, new_parent]
744 ))
659745
660 def test__edits_interface_removes_parents(self):746 def test__edits_interface_removes_parents(self):
661 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)747 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
@@ -713,7 +799,7 @@
713 def test__edit_doesnt_overwrite_params(self):799 def test__edit_doesnt_overwrite_params(self):
714 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)800 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
715 parent2 = factory.make_Interface(801 parent2 = factory.make_Interface(
716 INTERFACE_TYPE.PHYSICAL, node=parent1.node)802 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
717 interface = factory.make_Interface(803 interface = factory.make_Interface(
718 INTERFACE_TYPE.BOND,804 INTERFACE_TYPE.BOND,
719 parents=[parent1, parent2])805 parents=[parent1, parent2])
@@ -733,12 +819,10 @@
733 "bond_xmit_hash_policy": bond_xmit_hash_policy,819 "bond_xmit_hash_policy": bond_xmit_hash_policy,
734 }820 }
735 interface.save()821 interface.save()
736 new_vlan = factory.make_VLAN(vid=33)
737 new_name = factory.make_name()822 new_name = factory.make_name()
738 form = BondInterfaceForm(823 form = BondInterfaceForm(
739 instance=interface,824 instance=interface,
740 data={825 data={
741 'vlan': new_vlan.id,
742 'name': new_name,826 'name': new_name,
743 })827 })
744 self.assertTrue(form.is_valid(), form.errors)828 self.assertTrue(form.is_valid(), form.errors)
@@ -755,7 +839,7 @@
755 def test__edit_does_overwrite_params(self):839 def test__edit_does_overwrite_params(self):
756 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)840 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
757 parent2 = factory.make_Interface(841 parent2 = factory.make_Interface(
758 INTERFACE_TYPE.PHYSICAL, node=parent1.node)842 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
759 interface = factory.make_Interface(843 interface = factory.make_Interface(
760 INTERFACE_TYPE.BOND,844 INTERFACE_TYPE.BOND,
761 parents=[parent1, parent2])845 parents=[parent1, parent2])
@@ -775,7 +859,6 @@
775 "bond_xmit_hash_policy": bond_xmit_hash_policy,859 "bond_xmit_hash_policy": bond_xmit_hash_policy,
776 }860 }
777 interface.save()861 interface.save()
778 new_vlan = factory.make_VLAN(vid=33)
779 new_name = factory.make_name()862 new_name = factory.make_name()
780 new_bond_mode = factory.pick_choice(BOND_MODE_CHOICES)863 new_bond_mode = factory.pick_choice(BOND_MODE_CHOICES)
781 new_bond_miimon = random.randint(0, 1000)864 new_bond_miimon = random.randint(0, 1000)
@@ -787,7 +870,6 @@
787 form = BondInterfaceForm(870 form = BondInterfaceForm(
788 instance=interface,871 instance=interface,
789 data={872 data={
790 'vlan': new_vlan.id,
791 'name': new_name,873 'name': new_name,
792 'bond_mode': new_bond_mode,874 'bond_mode': new_bond_mode,
793 'bond_miimon': new_bond_miimon,875 'bond_miimon': new_bond_miimon,
@@ -810,7 +892,7 @@
810 def test__edit_allows_zero_params(self):892 def test__edit_allows_zero_params(self):
811 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)893 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
812 parent2 = factory.make_Interface(894 parent2 = factory.make_Interface(
813 INTERFACE_TYPE.PHYSICAL, node=parent1.node)895 INTERFACE_TYPE.PHYSICAL, node=parent1.node, vlan=parent1.vlan)
814 interface = factory.make_Interface(896 interface = factory.make_Interface(
815 INTERFACE_TYPE.BOND,897 INTERFACE_TYPE.BOND,
816 parents=[parent1, parent2])898 parents=[parent1, parent2])
@@ -830,7 +912,6 @@
830 "bond_xmit_hash_policy": bond_xmit_hash_policy,912 "bond_xmit_hash_policy": bond_xmit_hash_policy,
831 }913 }
832 interface.save()914 interface.save()
833 new_vlan = factory.make_VLAN(vid=33)
834 new_name = factory.make_name()915 new_name = factory.make_name()
835 new_bond_mode = factory.pick_choice(BOND_MODE_CHOICES)916 new_bond_mode = factory.pick_choice(BOND_MODE_CHOICES)
836 new_bond_miimon = 0917 new_bond_miimon = 0
@@ -842,7 +923,6 @@
842 form = BondInterfaceForm(923 form = BondInterfaceForm(
843 instance=interface,924 instance=interface,
844 data={925 data={
845 'vlan': new_vlan.id,
846 'name': new_name,926 'name': new_name,
847 'bond_mode': new_bond_mode,927 'bond_mode': new_bond_mode,
848 'bond_miimon': new_bond_miimon,928 'bond_miimon': new_bond_miimon,
849929
=== modified file 'src/maasserver/websockets/handlers/tests/test_node.py'
--- src/maasserver/websockets/handlers/tests/test_node.py 2015-11-18 18:14:30 +0000
+++ src/maasserver/websockets/handlers/tests/test_node.py 2016-04-05 16:02:24 +0000
@@ -1912,7 +1912,8 @@
1912 handler = NodeHandler(user, {})1912 handler = NodeHandler(user, {})
1913 name = factory.make_name("eth")1913 name = factory.make_name("eth")
1914 mac_address = factory.make_mac_address()1914 mac_address = factory.make_mac_address()
1915 vlan = factory.make_VLAN()1915 fabric = factory.make_Fabric()
1916 vlan = fabric.get_default_vlan()
1916 handler.create_physical({1917 handler.create_physical({
1917 "system_id": node.system_id,1918 "system_id": node.system_id,
1918 "name": name,1919 "name": name,
@@ -1929,7 +1930,8 @@
1929 handler = NodeHandler(user, {})1930 handler = NodeHandler(user, {})
1930 name = factory.make_name("eth")1931 name = factory.make_name("eth")
1931 mac_address = factory.make_mac_address()1932 mac_address = factory.make_mac_address()
1932 vlan = factory.make_VLAN()1933 fabric = factory.make_Fabric()
1934 vlan = fabric.get_default_vlan()
1933 subnet = factory.make_Subnet(vlan=vlan)1935 subnet = factory.make_Subnet(vlan=vlan)
1934 handler.create_physical({1936 handler.create_physical({
1935 "system_id": node.system_id,1937 "system_id": node.system_id,
@@ -1951,7 +1953,8 @@
1951 handler = NodeHandler(user, {})1953 handler = NodeHandler(user, {})
1952 name = factory.make_name("eth")1954 name = factory.make_name("eth")
1953 mac_address = factory.make_mac_address()1955 mac_address = factory.make_mac_address()
1954 vlan = factory.make_VLAN()1956 fabric = factory.make_Fabric()
1957 vlan = fabric.get_default_vlan()
1955 handler.create_physical({1958 handler.create_physical({
1956 "system_id": node.system_id,1959 "system_id": node.system_id,
1957 "name": name,1960 "name": name,
@@ -1971,7 +1974,8 @@
1971 handler = NodeHandler(user, {})1974 handler = NodeHandler(user, {})
1972 name = factory.make_name("eth")1975 name = factory.make_name("eth")
1973 mac_address = factory.make_mac_address()1976 mac_address = factory.make_mac_address()
1974 vlan = factory.make_VLAN()1977 fabric = factory.make_Fabric()
1978 vlan = fabric.get_default_vlan()
1975 subnet = factory.make_Subnet(vlan=vlan)1979 subnet = factory.make_Subnet(vlan=vlan)
1976 handler.create_physical({1980 handler.create_physical({
1977 "system_id": node.system_id,1981 "system_id": node.system_id,
@@ -1992,7 +1996,7 @@
1992 node = factory.make_Node()1996 node = factory.make_Node()
1993 handler = NodeHandler(user, {})1997 handler = NodeHandler(user, {})
1994 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)1998 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
1995 new_vlan = factory.make_VLAN()1999 new_vlan = factory.make_VLAN(fabric=interface.vlan.fabric)
1996 handler.create_vlan({2000 handler.create_vlan({
1997 "system_id": node.system_id,2001 "system_id": node.system_id,
1998 "parent": interface.id,2002 "parent": interface.id,
@@ -2008,7 +2012,7 @@
2008 node = factory.make_Node()2012 node = factory.make_Node()
2009 handler = NodeHandler(user, {})2013 handler = NodeHandler(user, {})
2010 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)2014 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
2011 new_vlan = factory.make_VLAN()2015 new_vlan = factory.make_VLAN(fabric=interface.vlan.fabric)
2012 new_subnet = factory.make_Subnet(vlan=new_vlan)2016 new_subnet = factory.make_Subnet(vlan=new_vlan)
2013 handler.create_vlan({2017 handler.create_vlan({
2014 "system_id": node.system_id,2018 "system_id": node.system_id,
@@ -2030,7 +2034,7 @@
2030 node = factory.make_Node()2034 node = factory.make_Node()
2031 handler = NodeHandler(user, {})2035 handler = NodeHandler(user, {})
2032 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)2036 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
2033 new_vlan = factory.make_VLAN()2037 new_vlan = factory.make_VLAN(fabric=interface.vlan.fabric)
2034 handler.create_vlan({2038 handler.create_vlan({
2035 "system_id": node.system_id,2039 "system_id": node.system_id,
2036 "parent": interface.id,2040 "parent": interface.id,
@@ -2050,7 +2054,7 @@
2050 node = factory.make_Node()2054 node = factory.make_Node()
2051 handler = NodeHandler(user, {})2055 handler = NodeHandler(user, {})
2052 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)2056 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
2053 new_vlan = factory.make_VLAN()2057 new_vlan = factory.make_VLAN(fabric=interface.vlan.fabric)
2054 new_subnet = factory.make_Subnet(vlan=new_vlan)2058 new_subnet = factory.make_Subnet(vlan=new_vlan)
2055 handler.create_vlan({2059 handler.create_vlan({
2056 "system_id": node.system_id,2060 "system_id": node.system_id,
@@ -2110,7 +2114,8 @@
2110 handler = NodeHandler(user, {})2114 handler = NodeHandler(user, {})
2111 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)2115 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
2112 new_name = factory.make_name("name")2116 new_name = factory.make_name("name")
2113 new_vlan = factory.make_VLAN()2117 new_fabric = factory.make_Fabric()
2118 new_vlan = new_fabric.get_default_vlan()
2114 handler.update_interface({2119 handler.update_interface({
2115 "system_id": node.system_id,2120 "system_id": node.system_id,
2116 "interface_id": interface.id,2121 "interface_id": interface.id,

Subscribers

People subscribed via source and target branches