Merge lp:~mpontillo/maas/relax-bridge-restrictions--bug-1661203 into lp:~maas-committers/maas/trunk

Proposed by Mike Pontillo
Status: Merged
Approved by: Mike Pontillo
Approved revision: no longer in the source branch.
Merged at revision: 5937
Proposed branch: lp:~mpontillo/maas/relax-bridge-restrictions--bug-1661203
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 784 lines (+169/-92)
2 files modified
src/maasserver/forms/interface.py (+28/-11)
src/maasserver/forms/tests/test_interface.py (+141/-81)
To merge this branch: bzr merge lp:~mpontillo/maas/relax-bridge-restrictions--bug-1661203
Reviewer Review Type Date Requested Status
LaMont Jones (community) Approve
Review via email: mp+322066@code.launchpad.net

Commit message

Allow creating bridges whose parents already have VLANs.

Drive-by fix to use dict(form.errors) so that test failures produce nicer errors.

To post a comment you must log in.
Revision history for this message
LaMont Jones (lamont) wrote :

LGTM.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/forms/interface.py'
2--- src/maasserver/forms/interface.py 2017-02-28 11:37:12 +0000
3+++ src/maasserver/forms/interface.py 2017-04-05 23:39:39 +0000
4@@ -397,21 +397,25 @@
5 vlan = parents[0].vlan
6 self.cleaned_data['vlan'] = vlan
7
8- def _validate_parental_fidelity(self, parents):
9+ def get_delinquent_children(self, parents):
10+ """Returns either an empty set, or a set of children whose presence
11+ would deter the parent from adopting this new child."""
12+ return {
13+ parent.name for parent in parents
14+ for rel in parent.children_relationships.all()
15+ if rel.child.id != self.instance.id
16+ }
17+
18+ def validate_parental_fidelity(self, parents):
19 """Check that all of the parent interfaces are not already in a
20 relationship before committing them to this child.
21 """
22- parents_with_other_children = {
23- parent.name
24- for parent in parents
25- for rel in parent.children_relationships.all()
26- if rel.child.id != self.instance.id
27- }
28- if parents_with_other_children:
29+ dilinquents = self.get_delinquent_children(parents)
30+ if len(dilinquents) != 0:
31 set_form_error(
32 self, 'parents',
33 "Interfaces already in-use: %s." % (
34- ', '.join(sorted(parents_with_other_children))))
35+ ', '.join(sorted(dilinquents))))
36
37
38 class BondInterfaceForm(ChildInterfaceForm):
39@@ -466,7 +470,7 @@
40 # created.
41 if parents:
42 self._set_default_child_mac(parents)
43- self._validate_parental_fidelity(parents)
44+ self.validate_parental_fidelity(parents)
45 self._set_default_vlan(parents)
46 self._validate_parent_vlans_match(parents)
47 return cleaned_data
48@@ -544,6 +548,19 @@
49 "in a bond or a bridge.")
50 return parents
51
52+ def get_delinquent_children(self, parents):
53+ """Returns a set of children who would prevent the creation of this
54+ bridge interface. The only difference between this method and the
55+ method it overrides is that it allows VLAN interface children, whom
56+ bridges may get along with.
57+ """
58+ return {
59+ parent.name for parent in parents
60+ for rel in parent.children_relationships.all()
61+ if (rel.child.id != self.instance.id and
62+ rel.child.type != INTERFACE_TYPE.VLAN)
63+ }
64+
65 def clean(self):
66 cleaned_data = super().clean()
67 if self.fields_ok(['vlan', 'parents']):
68@@ -552,7 +569,7 @@
69 # created.
70 if parents:
71 self._set_default_child_mac(parents)
72- self._validate_parental_fidelity(parents)
73+ self.validate_parental_fidelity(parents)
74 self._set_default_vlan(parents)
75 return cleaned_data
76
77
78=== modified file 'src/maasserver/forms/tests/test_interface.py'
79--- src/maasserver/forms/tests/test_interface.py 2017-03-29 11:11:35 +0000
80+++ src/maasserver/forms/tests/test_interface.py 2017-04-05 23:39:39 +0000
81@@ -82,7 +82,7 @@
82 data={
83 'vlan': new_vlan.id,
84 })
85- self.assertTrue(form.is_valid(), form.errors)
86+ self.assertTrue(form.is_valid(), dict(form.errors))
87 interface = form.save()
88 self.assertThat(
89 interface,
90@@ -98,7 +98,7 @@
91 data={
92 'vlan': None,
93 })
94- self.assertTrue(form.is_valid(), form.errors)
95+ self.assertTrue(form.is_valid(), dict(form.errors))
96 interface = form.save()
97 self.assertThat(
98 interface,
99@@ -119,7 +119,7 @@
100 'name': new_name,
101 'mac_address': new_mac,
102 })
103- self.assertTrue(form.is_valid(), form.errors)
104+ self.assertTrue(form.is_valid(), dict(form.errors))
105 interface = form.save()
106 self.assertThat(
107 interface,
108@@ -147,7 +147,7 @@
109 'vlan': vlan.id,
110 'tags': ",".join(tags),
111 })
112- self.assertTrue(form.is_valid(), form.errors)
113+ self.assertTrue(form.is_valid(), dict(form.errors))
114 interface = form.save()
115 self.assertThat(
116 interface,
117@@ -174,7 +174,7 @@
118 'vlan': vlan.id,
119 'tags': ",".join(tags),
120 })
121- self.assertTrue(form.is_valid(), form.errors)
122+ self.assertTrue(form.is_valid(), dict(form.errors))
123 interface = form.save()
124 self.assertThat(
125 interface,
126@@ -198,7 +198,7 @@
127 'mac_address': mac_address,
128 'tags': ",".join(tags),
129 })
130- self.assertTrue(form.is_valid(), form.errors)
131+ self.assertTrue(form.is_valid(), dict(form.errors))
132 interface = form.save()
133 self.assertThat(
134 interface,
135@@ -225,7 +225,7 @@
136 'vlan': vlan.id,
137 'tags': ",".join(tags),
138 })
139- self.assertTrue(form.is_valid(), form.errors)
140+ self.assertTrue(form.is_valid(), dict(form.errors))
141 interface = form.save()
142 self.assertIsNotNone(
143 interface.ip_addresses.filter(alloc_type=IPADDRESS_TYPE.STICKY))
144@@ -240,7 +240,7 @@
145 'name': interface_name,
146 'vlan': vlan.id,
147 })
148- self.assertFalse(form.is_valid(), form.errors)
149+ self.assertFalse(form.is_valid(), dict(form.errors))
150 self.assertItemsEqual(
151 ['mac_address'], form.errors.keys(), form.errors)
152 self.assertIn(
153@@ -259,7 +259,7 @@
154 'mac_address': mac_address,
155 'vlan': interface.vlan.id,
156 })
157- self.assertFalse(form.is_valid(), form.errors)
158+ self.assertFalse(form.is_valid(), dict(form.errors))
159 self.assertItemsEqual(
160 ['name'], form.errors.keys(), form.errors)
161 self.assertIn(
162@@ -279,7 +279,7 @@
163 'mac_address': mac_address,
164 'vlan': vlan.id,
165 })
166- self.assertFalse(form.is_valid(), form.errors)
167+ self.assertFalse(form.is_valid(), dict(form.errors))
168 self.assertItemsEqual(
169 ['vlan'], form.errors.keys(), form.errors)
170 self.assertIn(
171@@ -301,7 +301,7 @@
172 'mac_address': mac_address,
173 'vlan': vlan.id,
174 })
175- self.assertTrue(form.is_valid(), form.errors)
176+ self.assertTrue(form.is_valid(), dict(form.errors))
177 interface = form.save()
178 self.assertEquals(vlan, interface.vlan)
179
180@@ -317,7 +317,7 @@
181 'vlan': vlan.id,
182 'parents': [parent.id],
183 })
184- self.assertFalse(form.is_valid(), form.errors)
185+ self.assertFalse(form.is_valid(), dict(form.errors))
186 self.assertItemsEqual(
187 ['parents'], form.errors.keys(), form.errors)
188 self.assertIn(
189@@ -338,7 +338,7 @@
190 'enabled': False,
191 'tags': "",
192 })
193- self.assertTrue(form.is_valid(), form.errors)
194+ self.assertTrue(form.is_valid(), dict(form.errors))
195 interface = form.save()
196 self.assertThat(
197 interface,
198@@ -358,7 +358,7 @@
199 'enabled': False,
200 'tags': "",
201 })
202- self.assertTrue(form.is_valid(), form.errors)
203+ self.assertTrue(form.is_valid(), dict(form.errors))
204 interface = form.save()
205 self.assertThat(
206 interface,
207@@ -378,7 +378,7 @@
208 'enabled': False,
209 'tags': "",
210 })
211- self.assertTrue(form.is_valid(), form.errors)
212+ self.assertTrue(form.is_valid(), dict(form.errors))
213 interface = form.save()
214 self.assertThat(
215 interface,
216@@ -410,7 +410,7 @@
217 'accept_ra': accept_ra,
218 'autoconf': autoconf,
219 })
220- self.assertTrue(form.is_valid(), form.errors)
221+ self.assertTrue(form.is_valid(), dict(form.errors))
222 interface = form.save()
223 self.assertEqual({
224 "mtu": mtu,
225@@ -440,7 +440,7 @@
226 'enabled': False,
227 'tags': "",
228 })
229- self.assertTrue(form.is_valid(), form.errors)
230+ self.assertTrue(form.is_valid(), dict(form.errors))
231 interface = form.save()
232 self.assertEqual({
233 "mtu": mtu,
234@@ -469,7 +469,7 @@
235 "accept_ra": new_accept_ra,
236 "autoconf": new_autoconf,
237 })
238- self.assertTrue(form.is_valid(), form.errors)
239+ self.assertTrue(form.is_valid(), dict(form.errors))
240 interface = form.save()
241 self.assertEqual({
242 "mtu": new_mtu,
243@@ -495,7 +495,7 @@
244 "accept_ra": "",
245 "autoconf": "",
246 })
247- self.assertTrue(form.is_valid(), form.errors)
248+ self.assertTrue(form.is_valid(), dict(form.errors))
249 interface = form.save()
250 self.assertEqual({}, interface.params)
251
252@@ -511,7 +511,7 @@
253 'vlan': vlan.id,
254 'parents': [parent.id],
255 })
256- self.assertTrue(form.is_valid(), form.errors)
257+ self.assertTrue(form.is_valid(), dict(form.errors))
258 interface = form.save()
259 interface_name = build_vlan_interface_name(parent, vlan)
260 self.assertThat(
261@@ -529,7 +529,7 @@
262 'vlan': vlan.id,
263 'parents': [parent.id],
264 })
265- self.assertTrue(form.is_valid(), form.errors)
266+ self.assertTrue(form.is_valid(), dict(form.errors))
267 interface = form.save()
268 self.assertIsNotNone(
269 interface.ip_addresses.filter(alloc_type=IPADDRESS_TYPE.STICKY))
270@@ -541,7 +541,7 @@
271 data={
272 'parents': [parent.id],
273 })
274- self.assertFalse(form.is_valid(), form.errors)
275+ self.assertFalse(form.is_valid(), dict(form.errors))
276 self.assertItemsEqual(
277 ['vlan'], form.errors.keys(), form.errors)
278 self.assertIn(
279@@ -559,7 +559,7 @@
280 'vlan': vlan.id,
281 'parents': [parent.id],
282 })
283- self.assertFalse(form.is_valid(), form.errors)
284+ self.assertFalse(form.is_valid(), dict(form.errors))
285 self.assertItemsEqual(
286 ['name'], form.errors.keys(), form.errors)
287 self.assertIn(
288@@ -575,7 +575,7 @@
289 'vlan': vlan.id,
290 'parents': [parent.id],
291 })
292- self.assertFalse(form.is_valid(), form.errors)
293+ self.assertFalse(form.is_valid(), dict(form.errors))
294 self.assertItemsEqual(
295 ['vlan'], form.errors.keys(), form.errors)
296 self.assertIn(
297@@ -589,7 +589,7 @@
298 data={
299 'vlan': vlan.id,
300 })
301- self.assertFalse(form.is_valid(), form.errors)
302+ self.assertFalse(form.is_valid(), dict(form.errors))
303 self.assertItemsEqual(['parents'], form.errors.keys())
304 self.assertIn(
305 "A VLAN interface must have exactly one parent.",
306@@ -607,7 +607,7 @@
307 'vlan': other_vlan.id,
308 'parents': [vlan_parent.id],
309 })
310- self.assertFalse(form.is_valid(), form.errors)
311+ self.assertFalse(form.is_valid(), dict(form.errors))
312 self.assertItemsEqual(['parents'], form.errors.keys())
313 self.assertIn(
314 "VLAN interface can't have another VLAN interface as parent.",
315@@ -621,7 +621,7 @@
316 'vlan': None,
317 'parents': [parent.id],
318 })
319- self.assertFalse(form.is_valid(), form.errors)
320+ self.assertFalse(form.is_valid(), dict(form.errors))
321 self.assertItemsEqual(['vlan'], form.errors.keys())
322 self.assertIn(
323 "A VLAN interface must be connected to a tagged VLAN.",
324@@ -637,7 +637,7 @@
325 'vlan': other_vlan.id,
326 'parents': [parent.id],
327 })
328- self.assertFalse(form.is_valid(), form.errors)
329+ self.assertFalse(form.is_valid(), dict(form.errors))
330 self.assertItemsEqual(['vlan'], form.errors.keys())
331 self.assertIn(
332 "A VLAN interface can only belong to a tagged VLAN on "
333@@ -654,7 +654,7 @@
334 'vlan': vlan.id,
335 'parents': [parent.id],
336 })
337- self.assertFalse(form.is_valid(), form.errors)
338+ self.assertFalse(form.is_valid(), dict(form.errors))
339 self.assertItemsEqual(['parents'], form.errors.keys())
340 self.assertIn(
341 "A VLAN interface can't have a parent that is already in a bond.",
342@@ -671,7 +671,7 @@
343 'vlan': vlan.id,
344 'parents': [parent1.id, parent2.id],
345 })
346- self.assertFalse(form.is_valid(), form.errors)
347+ self.assertFalse(form.is_valid(), dict(form.errors))
348 self.assertItemsEqual(['parents'], form.errors.keys())
349 self.assertIn(
350 "A VLAN interface must have exactly one parent.",
351@@ -688,7 +688,7 @@
352 data={
353 'vlan': new_vlan.id
354 })
355- self.assertTrue(form.is_valid(), form.errors)
356+ self.assertTrue(form.is_valid(), dict(form.errors))
357 interface = form.save()
358 self.assertThat(
359 interface,
360@@ -713,7 +713,7 @@
361 'parents': [parent1.id, parent2.id],
362 'bond_mode': bond_mode,
363 })
364- self.assertFalse(form.is_valid(), form.errors)
365+ self.assertFalse(form.is_valid(), dict(form.errors))
366 self.assertEqual({
367 "bond_mode": [
368 compose_invalid_choice_text(
369@@ -731,7 +731,7 @@
370 'name': interface_name,
371 'parents': [parent1.id, parent2.id],
372 })
373- self.assertTrue(form.is_valid(), form.errors)
374+ self.assertTrue(form.is_valid(), dict(form.errors))
375 interface = form.save()
376 self.assertThat(
377 interface,
378@@ -755,7 +755,7 @@
379 'name': interface_name,
380 'parents': [parent1.id, parent2.id],
381 })
382- self.assertTrue(form.is_valid(), form.errors)
383+ self.assertTrue(form.is_valid(), dict(form.errors))
384 interface = form.save()
385 self.assertEqual(
386 0,
387@@ -780,7 +780,7 @@
388 'parents': [parent1.id, parent2.id],
389 'mac_address': parent1.mac_address,
390 })
391- self.assertTrue(form.is_valid(), form.errors)
392+ self.assertTrue(form.is_valid(), dict(form.errors))
393 interface = form.save()
394 self.assertThat(
395 interface,
396@@ -800,7 +800,7 @@
397 'name': interface_name,
398 'parents': [parent1.id, parent2.id],
399 })
400- self.assertTrue(form.is_valid(), form.errors)
401+ self.assertTrue(form.is_valid(), dict(form.errors))
402 interface = form.save()
403 self.assertEqual({
404 "bond_mode": "balance-rr",
405@@ -835,7 +835,7 @@
406 'bond_lacp_rate': bond_lacp_rate,
407 'bond_xmit_hash_policy': bond_xmit_hash_policy,
408 })
409- self.assertTrue(form.is_valid(), form.errors)
410+ self.assertTrue(form.is_valid(), dict(form.errors))
411 interface = form.save()
412 self.assertEqual({
413 "bond_mode": bond_mode,
414@@ -853,7 +853,7 @@
415 data={
416 'name': interface_name,
417 })
418- self.assertFalse(form.is_valid(), form.errors)
419+ self.assertFalse(form.is_valid(), dict(form.errors))
420 self.assertItemsEqual(['parents', 'mac_address'], form.errors.keys())
421 self.assertIn(
422 "A bond interface must have one or more parents.",
423@@ -871,7 +871,7 @@
424 'mac_address': parent.mac_address,
425 'vlan': vlan.id,
426 })
427- self.assertFalse(form.is_valid(), form.errors)
428+ self.assertFalse(form.is_valid(), dict(form.errors))
429 self.assertItemsEqual(['vlan'], form.errors.keys())
430 self.assertIn(
431 "A bond interface can only belong to an untagged VLAN.",
432@@ -892,7 +892,7 @@
433 'name': interface_name,
434 'parents': [parent1.id, parent2.id]
435 })
436- self.assertFalse(form.is_valid(), form.errors)
437+ self.assertFalse(form.is_valid(), dict(form.errors))
438 self.assertIn(
439 "Interfaces already in-use: eth0, eth1.",
440 form.errors['parents'][0])
441@@ -910,7 +910,7 @@
442 'name': interface_name,
443 'parents': [parent1.id, parent2.id]
444 })
445- self.assertFalse(form.is_valid(), form.errors)
446+ self.assertFalse(form.is_valid(), dict(form.errors))
447 self.assertEquals(
448 "All parents must belong to the same VLAN.",
449 form.errors['parents'][0])
450@@ -934,7 +934,7 @@
451 'name': new_name,
452 'parents': [parent1.id, parent2.id, new_parent.id],
453 })
454- self.assertTrue(form.is_valid(), form.errors)
455+ self.assertTrue(form.is_valid(), dict(form.errors))
456 interface = form.save()
457 self.assertThat(
458 interface,
459@@ -959,7 +959,7 @@
460 data={
461 'vlan': None,
462 })
463- self.assertTrue(form.is_valid(), form.errors)
464+ self.assertTrue(form.is_valid(), dict(form.errors))
465 interface = form.save()
466 self.assertThat(
467 interface,
468@@ -983,7 +983,7 @@
469 'name': new_name,
470 'parents': [parent1.id, parent2.id],
471 })
472- self.assertTrue(form.is_valid(), form.errors)
473+ self.assertTrue(form.is_valid(), dict(form.errors))
474 interface = form.save()
475 self.assertThat(
476 interface,
477@@ -1009,7 +1009,7 @@
478 'name': new_name,
479 'parents': [parent1.id, parent2.id],
480 })
481- self.assertTrue(form.is_valid(), form.errors)
482+ self.assertTrue(form.is_valid(), dict(form.errors))
483 interface = form.save()
484 self.assertThat(
485 interface,
486@@ -1049,7 +1049,7 @@
487 data={
488 'name': new_name,
489 })
490- self.assertTrue(form.is_valid(), form.errors)
491+ self.assertTrue(form.is_valid(), dict(form.errors))
492 interface = form.save()
493 self.assertEqual({
494 "bond_mode": bond_mode,
495@@ -1102,7 +1102,7 @@
496 'bond_lacp_rate': new_bond_lacp_rate,
497 'bond_xmit_hash_policy': new_bond_xmit_hash_policy,
498 })
499- self.assertTrue(form.is_valid(), form.errors)
500+ self.assertTrue(form.is_valid(), dict(form.errors))
501 interface = form.save()
502 self.assertEqual({
503 "bond_mode": new_bond_mode,
504@@ -1155,7 +1155,7 @@
505 'bond_lacp_rate': new_bond_lacp_rate,
506 'bond_xmit_hash_policy': new_bond_xmit_hash_policy,
507 })
508- self.assertTrue(form.is_valid(), form.errors)
509+ self.assertTrue(form.is_valid(), dict(form.errors))
510 interface = form.save()
511 self.assertEqual({
512 "bond_mode": new_bond_mode,
513@@ -1178,14 +1178,62 @@
514 'name': interface_name,
515 'parents': [parent.id],
516 })
517- self.assertTrue(form.is_valid(), form.errors)
518- interface = form.save()
519- self.assertThat(
520- interface,
521- MatchesStructure.byEquality(
522- name=interface_name, type=INTERFACE_TYPE.BRIDGE))
523- self.assertEqual(interface.mac_address, parent.mac_address)
524- self.assertItemsEqual([parent], interface.parents.all())
525+ self.assertTrue(form.is_valid(), dict(form.errors))
526+ interface = form.save()
527+ self.assertThat(
528+ interface,
529+ MatchesStructure.byEquality(
530+ name=interface_name, type=INTERFACE_TYPE.BRIDGE))
531+ self.assertEqual(interface.mac_address, parent.mac_address)
532+ self.assertItemsEqual([parent], interface.parents.all())
533+
534+ def test__allows_bridge_on_parent_with_vlan_bridges(self):
535+ parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
536+ vlan1 = factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[parent])
537+ factory.make_Interface(INTERFACE_TYPE.BRIDGE, parents=[vlan1])
538+ vlan2 = factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[parent])
539+ factory.make_Interface(INTERFACE_TYPE.BRIDGE, parents=[vlan2])
540+ interface_name = factory.make_name()
541+ form = BridgeInterfaceForm(
542+ node=parent.node,
543+ data={
544+ 'name': interface_name,
545+ 'parents': [parent.id],
546+ })
547+ self.assertTrue(form.is_valid(), dict(form.errors))
548+ interface = form.save()
549+ self.assertThat(
550+ interface,
551+ MatchesStructure.byEquality(
552+ name=interface_name, type=INTERFACE_TYPE.BRIDGE))
553+ self.assertEqual(interface.mac_address, parent.mac_address)
554+ self.assertItemsEqual([parent], interface.parents.all())
555+
556+ def test__allows_bridge_on_bond_with_vlan_bridges(self):
557+ node = factory.make_Node()
558+ eth0 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
559+ eth1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
560+ bond0 = factory.make_Interface(
561+ INTERFACE_TYPE.BOND, parents=[eth0, eth1])
562+ vlan1 = factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[bond0])
563+ factory.make_Interface(INTERFACE_TYPE.BRIDGE, parents=[vlan1])
564+ vlan2 = factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[bond0])
565+ factory.make_Interface(INTERFACE_TYPE.BRIDGE, parents=[vlan2])
566+ interface_name = factory.make_name()
567+ form = BridgeInterfaceForm(
568+ node=node,
569+ data={
570+ 'name': interface_name,
571+ 'parents': [bond0.id],
572+ })
573+ self.assertTrue(form.is_valid(), dict(form.errors))
574+ interface = form.save()
575+ self.assertThat(
576+ interface,
577+ MatchesStructure.byEquality(
578+ name=interface_name, type=INTERFACE_TYPE.BRIDGE))
579+ self.assertEqual(interface.mac_address, bond0.mac_address)
580+ self.assertItemsEqual([bond0], interface.parents.all())
581
582 def test__create_removes_parent_links_and_sets_link_up_on_bridge(self):
583 parent = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
584@@ -1197,7 +1245,7 @@
585 'name': interface_name,
586 'parents': [parent.id],
587 })
588- self.assertTrue(form.is_valid(), form.errors)
589+ self.assertTrue(form.is_valid(), dict(form.errors))
590 interface = form.save()
591 self.assertEqual(
592 0,
593@@ -1216,7 +1264,7 @@
594 'parents': [parent.id],
595 'mac_address': parent.mac_address,
596 })
597- self.assertTrue(form.is_valid(), form.errors)
598+ self.assertTrue(form.is_valid(), dict(form.errors))
599 interface = form.save()
600 self.assertThat(
601 interface,
602@@ -1232,7 +1280,7 @@
603 data={
604 'name': interface_name,
605 })
606- self.assertFalse(form.is_valid(), form.errors)
607+ self.assertFalse(form.is_valid(), dict(form.errors))
608 self.assertItemsEqual(['parents', 'mac_address'], form.errors.keys())
609 self.assertIn(
610 "A bridge interface must have exactly one parent.",
611@@ -1240,17 +1288,24 @@
612
613 def test__rejects_when_parent_already_have_children(self):
614 node = factory.make_Node()
615- parent = factory.make_Interface(
616+ eth0 = factory.make_Interface(
617 INTERFACE_TYPE.PHYSICAL, node=node, name="eth0")
618- factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[parent])
619+ eth1 = factory.make_Interface(
620+ INTERFACE_TYPE.PHYSICAL, node=node, name="eth1")
621+ invalid0 = factory.make_Interface(
622+ INTERFACE_TYPE.BOND, parents=[eth0, eth1])
623+ # This should never happen, but in order to validate the case we're
624+ # trying to validate, we need a child that isn't a bond or bridge.
625+ invalid0.type = INTERFACE_TYPE.UNKNOWN
626+ invalid0.save()
627 interface_name = factory.make_name()
628 form = BridgeInterfaceForm(
629 node=node,
630 data={
631 'name': interface_name,
632- 'parents': [parent.id]
633+ 'parents': [eth0.id]
634 })
635- self.assertFalse(form.is_valid(), form.errors)
636+ self.assertFalse(form.is_valid(), dict(form.errors))
637 self.assertIn(
638 "Interfaces already in-use: eth0.",
639 form.errors['parents'][0])
640@@ -1265,7 +1320,7 @@
641 'name': interface_name,
642 'parents': [bridge.id]
643 })
644- self.assertFalse(form.is_valid(), form.errors)
645+ self.assertFalse(form.is_valid(), dict(form.errors))
646 self.assertIn(
647 "A bridge interface can't have another bridge interface as "
648 "parent.",
649@@ -1283,7 +1338,7 @@
650 'name': interface_name,
651 'parents': [parent.id]
652 })
653- self.assertFalse(form.is_valid(), form.errors)
654+ self.assertFalse(form.is_valid(), dict(form.errors))
655 self.assertIn(
656 "A bridge interface can't have a parent that is already "
657 "in a bond or a bridge.",
658@@ -1302,7 +1357,7 @@
659 'name': interface_name,
660 'parents': [parent1.id]
661 })
662- self.assertFalse(form.is_valid(), form.errors)
663+ self.assertFalse(form.is_valid(), dict(form.errors))
664 self.assertIn(
665 "A bridge interface can't have a parent that is already "
666 "in a bond or a bridge.",
667@@ -1325,7 +1380,7 @@
668 'name': new_name,
669 'parents': [new_parent.id],
670 })
671- self.assertTrue(form.is_valid(), form.errors)
672+ self.assertTrue(form.is_valid(), dict(form.errors))
673 interface = form.save()
674 self.assertThat(
675 interface,
676@@ -1345,7 +1400,7 @@
677 data={
678 'vlan': None,
679 })
680- self.assertTrue(form.is_valid(), form.errors)
681+ self.assertTrue(form.is_valid(), dict(form.errors))
682 interface = form.save()
683 self.assertThat(
684 interface,
685@@ -1372,7 +1427,7 @@
686 data={
687 'name': new_name,
688 })
689- self.assertTrue(form.is_valid(), form.errors)
690+ self.assertTrue(form.is_valid(), dict(form.errors))
691 interface = form.save()
692 self.assertEqual({
693 "bridge_stp": bridge_stp,
694@@ -1400,7 +1455,7 @@
695 'bridge_stp': new_bridge_stp,
696 'bridge_fd': new_bridge_fd,
697 })
698- self.assertTrue(form.is_valid(), form.errors)
699+ self.assertTrue(form.is_valid(), dict(form.errors))
700 interface = form.save()
701 self.assertEqual({
702 "bridge_stp": new_bridge_stp,
703@@ -1426,7 +1481,7 @@
704 'bridge_stp': new_bridge_stp,
705 'bridge_fd': new_bridge_fd,
706 })
707- self.assertTrue(form.is_valid(), form.errors)
708+ self.assertTrue(form.is_valid(), dict(form.errors))
709 interface = form.save()
710 self.assertEqual({
711 'bridge_stp': new_bridge_stp,
712@@ -1459,7 +1514,7 @@
713 'parents': [parent.id],
714 'tags': tags,
715 })
716- self.assertTrue(form.is_valid(), form.errors)
717+ self.assertTrue(form.is_valid(), dict(form.errors))
718 interface = form.save()
719 self.assertThat(
720 interface,
721@@ -1478,25 +1533,30 @@
722 data={
723 'name': interface_name,
724 })
725- self.assertFalse(form.is_valid(), form.errors)
726+ self.assertFalse(form.is_valid(), dict(form.errors))
727 self.assertItemsEqual(['parents', 'mac_address'], form.errors.keys())
728 self.assertIn(
729 "A bridge interface must have exactly one parent.",
730 form.errors['parents'][0])
731
732- def test__rejects_when_parent_already_have_children(self):
733+ def test__rejects_when_parent_already_have_non_vlan_children(self):
734 node = factory.make_Node()
735- parent = factory.make_Interface(
736+ eth0 = factory.make_Interface(
737 INTERFACE_TYPE.PHYSICAL, node=node, name="eth0")
738- factory.make_Interface(INTERFACE_TYPE.VLAN, parents=[parent])
739+ eth1 = factory.make_Interface(
740+ INTERFACE_TYPE.PHYSICAL, node=node, name="eth1")
741+ bond0 = factory.make_Interface(
742+ INTERFACE_TYPE.BOND, parents=[eth0, eth1])
743+ bond0.type = INTERFACE_TYPE.UNKNOWN
744+ bond0.save()
745 interface_name = factory.make_name()
746 form = AcquiredBridgeInterfaceForm(
747 node=node,
748 data={
749 'name': interface_name,
750- 'parents': [parent.id]
751+ 'parents': [eth0.id]
752 })
753- self.assertFalse(form.is_valid(), form.errors)
754+ self.assertFalse(form.is_valid(), dict(form.errors))
755 self.assertIn(
756 "Interfaces already in-use: eth0.",
757 form.errors['parents'][0])
758@@ -1511,7 +1571,7 @@
759 'name': interface_name,
760 'parents': [bridge.id]
761 })
762- self.assertFalse(form.is_valid(), form.errors)
763+ self.assertFalse(form.is_valid(), dict(form.errors))
764 self.assertIn(
765 "A bridge interface can't have another bridge interface as "
766 "parent.",
767@@ -1529,7 +1589,7 @@
768 'name': interface_name,
769 'parents': [parent.id]
770 })
771- self.assertFalse(form.is_valid(), form.errors)
772+ self.assertFalse(form.is_valid(), dict(form.errors))
773 self.assertIn(
774 "A bridge interface can't have a parent that is already "
775 "in a bond or a bridge.",
776@@ -1548,7 +1608,7 @@
777 'name': interface_name,
778 'parents': [parent1.id]
779 })
780- self.assertFalse(form.is_valid(), form.errors)
781+ self.assertFalse(form.is_valid(), dict(form.errors))
782 self.assertIn(
783 "A bridge interface can't have a parent that is already "
784 "in a bond or a bridge.",