Merge lp:~mpontillo/maas/relax-bridge-restrictions--bug-1661203 into lp:~maas-committers/maas/trunk
- relax-bridge-restrictions--bug-1661203
- Merge into 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 |
Related bugs: |
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.
Description of the change
To post a comment you must log in.
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.", |
LGTM.