Merge ~newell-jensen/maas:lp1806969 into maas:master

Proposed by Newell Jensen
Status: Merged
Approved by: Newell Jensen
Approved revision: 16517d8dbfc64cc2546b1531ce63371d3547e087
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~newell-jensen/maas:lp1806969
Merge into: maas:master
Diff against target: 205 lines (+171/-4)
2 files modified
src/maasserver/forms/pods.py (+21/-4)
src/maasserver/forms/tests/test_pods.py (+150/-0)
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Björn Tillenius Needs Fixing
Review via email: mp+370083@code.launchpad.net

Commit message

LP: #1806969 -- Explicitly search for bridges, bonds, followed by the remaining interfaces when retrieving a requested machine via interface constraints.

Description of the change

The reason this bug was occurring is that the interface constraints specified were being mapped to a list of interface_ids and the max id of this list was being chosen but the the bridge interface was the one with a lower id. This branch changes how the interfaces are chosen if there is a list of interface_ids by searching for a bridge or bond and then taking the max id of this. If nothing is found or if there isn't a list of interface_ids to check, the max is taken as was done before.

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

Why combine bridges and bonds? Should the search order be bridges first, then bonds, then other interfaces.

review: Needs Information
Revision history for this message
Björn Tillenius (bjornt) wrote :

I agree with Blake, that you should order first by bridges, and then by bonds, and then other interfaces.

I also think you need some more tests. See inline comments.

review: Needs Fixing
~newell-jensen/maas:lp1806969 updated
beed205... by Newell Jensen

Update queries so that we first try to match on bridges, followed by bonds, and then finally interfaces. Add better unit test coverage.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Some optimizations need to be done, but the test coverage now looks better.

review: Needs Fixing
~newell-jensen/maas:lp1806969 updated
1c00626... by Newell Jensen

Re-write django queries to improve the performance.

Revision history for this message
Blake Rouse (blake-rouse) :
review: Needs Fixing
~newell-jensen/maas:lp1806969 updated
d547dd0... by Newell Jensen

Review fixes.

Revision history for this message
Blake Rouse (blake-rouse) :
review: Needs Fixing
~newell-jensen/maas:lp1806969 updated
16517d8... by Newell Jensen

Review fixes +2.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good! Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/maasserver/forms/pods.py b/src/maasserver/forms/pods.py
index f54d875..4072a84 100644
--- a/src/maasserver/forms/pods.py
+++ b/src/maasserver/forms/pods.py
@@ -495,6 +495,19 @@ class ComposeMachineForm(forms.Form):
495 known_host_interfaces=known_host_interfaces,495 known_host_interfaces=known_host_interfaces,
496 )496 )
497497
498 def _pick_interface(self, interfaces):
499 bridge_interfaces = interfaces.filter(
500 type=INTERFACE_TYPE.BRIDGE).order_by('-id')
501 bond_interfaces = interfaces.filter(
502 type=INTERFACE_TYPE.BOND).order_by('-id')
503 bridge_interface = list(bridge_interfaces[:1])
504 if bridge_interface:
505 return bridge_interface[0]
506 bond_interface = list(bond_interfaces[:1])
507 if bond_interface:
508 return bond_interface[0]
509 return interfaces[0]
510
498 def _get_requested_machine_interfaces_via_constraints(511 def _get_requested_machine_interfaces_via_constraints(
499 self, interfaces_label_map):512 self, interfaces_label_map):
500 requested_machine_interfaces = []513 requested_machine_interfaces = []
@@ -512,11 +525,15 @@ class ComposeMachineForm(forms.Form):
512 for label in result.label_map.keys():525 for label in result.label_map.keys():
513 # XXX: We might want to use the "deepest" interface in the526 # XXX: We might want to use the "deepest" interface in the
514 # heirarchy, to ensure we get a bridge or bond (if configured)527 # heirarchy, to ensure we get a bridge or bond (if configured)
515 # rather than its parent. max() is a good approximation, since528 # rather than its parent. Since child interfaces will be created
516 # child interfaces will be created after their parents.529 # after their parents, this is a good approximation.
530 # Search for bridges, followed by bonds, and finally with the
531 # remaining interfaces.
517 interface_ids = result.label_map[label][pod_node_id]532 interface_ids = result.label_map[label][pod_node_id]
518 interface_id = max(interface_ids)533 interfaces = Interface.objects.filter(
519 interface = Interface.objects.get(id=interface_id)534 id__in=interface_ids).order_by('-id')
535 interface = self._pick_interface(interfaces)
536
520 # Check to see if we have a bootable VLAN,537 # Check to see if we have a bootable VLAN,
521 # we need at least one.538 # we need at least one.
522 if interface.has_bootable_vlan():539 if interface.has_bootable_vlan():
diff --git a/src/maasserver/forms/tests/test_pods.py b/src/maasserver/forms/tests/test_pods.py
index 03d332a..a218d84 100644
--- a/src/maasserver/forms/tests/test_pods.py
+++ b/src/maasserver/forms/tests/test_pods.py
@@ -1241,6 +1241,156 @@ class TestComposeMachineForm(MAASTransactionServerTestCase):
1241 attach_options=Equals(MACVLAN_MODE.BRIDGE))),1241 attach_options=Equals(MACVLAN_MODE.BRIDGE))),
1242 ]))))1242 ]))))
12431243
1244 def test__get_machine_with_interfaces_by_subnets_bridge(self):
1245 request = MagicMock()
1246 cidr1 = "10.0.0.0/24"
1247 cidr2 = "192.168.100.0/24"
1248 vlan = factory.make_VLAN(dhcp_on=True)
1249 subnet = factory.make_Subnet(cidr=cidr2, vlan=vlan)
1250 pod_host = factory.make_Machine_with_Interface_on_Subnet(
1251 cidr=cidr1)
1252 space = factory.make_Space('dmz')
1253 pod_host.boot_interface.vlan.space = space
1254 pod_host.boot_interface.vlan.save()
1255
1256 # Create a bridge and non-bridge on the pod_host
1257 bridge = factory.make_Interface(
1258 iftype=INTERFACE_TYPE.BRIDGE, node=pod_host,
1259 subnet=pod_host.boot_interface.vlan.subnet_set.first())
1260 non_bridge = factory.make_Interface(node=pod_host, subnet=subnet)
1261
1262 pod = make_pod_with_hints()
1263 pod.ip_address = pod_host.boot_interface.ip_addresses.first()
1264 pod.save()
1265 interfaces = "eth0:subnet=%s;eth1:subnet=%s" % (cidr1, cidr2)
1266 form = ComposeMachineForm(data={
1267 'interfaces': interfaces,
1268 }, request=request, pod=pod)
1269 self.assertTrue(form.is_valid(), form.errors)
1270 request_machine = form.get_requested_machine(
1271 make_known_host_interfaces(pod))
1272 self.assertThat(request_machine, MatchesAll(
1273 IsInstance(RequestedMachine),
1274 MatchesStructure(
1275 interfaces=MatchesListwise([
1276 MatchesAll(
1277 IsInstance(RequestedMachineInterface),
1278 MatchesStructure(
1279 attach_name=Equals(bridge.name),
1280 attach_type=Equals(InterfaceAttachType.BRIDGE),
1281 attach_options=Equals(None))),
1282 MatchesAll(
1283 IsInstance(RequestedMachineInterface),
1284 MatchesStructure(
1285 attach_name=Equals(non_bridge.name),
1286 attach_type=Equals(InterfaceAttachType.MACVLAN),
1287 attach_options=Equals(MACVLAN_MODE.BRIDGE))),
1288 ]))))
1289
1290 def test__get_machine_with_interfaces_by_subnets_bond(self):
1291 request = MagicMock()
1292 cidr1 = "10.0.0.0/24"
1293 cidr2 = "192.168.100.0/24"
1294 vlan = factory.make_VLAN(dhcp_on=True)
1295 subnet = factory.make_Subnet(cidr=cidr2, vlan=vlan)
1296 pod_host = factory.make_Machine_with_Interface_on_Subnet(
1297 cidr=cidr1)
1298 space = factory.make_Space('dmz')
1299 pod_host.boot_interface.vlan.space = space
1300 pod_host.boot_interface.vlan.save()
1301
1302 # Create a bond and non-bond on the pod_host
1303 bond_if = factory.make_Interface(
1304 node=pod_host,
1305 subnet=pod_host.boot_interface.vlan.subnet_set.first())
1306 bond = factory.make_Interface(
1307 iftype=INTERFACE_TYPE.BOND, node=pod_host, parents=[
1308 pod_host.boot_interface, bond_if],
1309 subnet=pod_host.boot_interface.vlan.subnet_set.first())
1310 non_bond = factory.make_Interface(node=pod_host, subnet=subnet)
1311
1312 pod = make_pod_with_hints()
1313 pod.ip_address = pod_host.boot_interface.ip_addresses.first()
1314 pod.save()
1315 interfaces = "eth0:subnet=%s;eth1:subnet=%s" % (cidr1, cidr2)
1316 form = ComposeMachineForm(data={
1317 'interfaces': interfaces,
1318 }, request=request, pod=pod)
1319 self.assertTrue(form.is_valid(), form.errors)
1320 request_machine = form.get_requested_machine(
1321 make_known_host_interfaces(pod))
1322 self.assertThat(request_machine, MatchesAll(
1323 IsInstance(RequestedMachine),
1324 MatchesStructure(
1325 interfaces=MatchesListwise([
1326 MatchesAll(
1327 IsInstance(RequestedMachineInterface),
1328 MatchesStructure(
1329 attach_name=Equals(bond.name),
1330 attach_type=Equals(InterfaceAttachType.MACVLAN),
1331 attach_options=Equals(MACVLAN_MODE.BRIDGE))),
1332 MatchesAll(
1333 IsInstance(RequestedMachineInterface),
1334 MatchesStructure(
1335 attach_name=Equals(non_bond.name),
1336 attach_type=Equals(InterfaceAttachType.MACVLAN),
1337 attach_options=Equals(MACVLAN_MODE.BRIDGE))),
1338 ]))))
1339
1340 def test__get_machine_with_interfaces_by_subnets_bond_inside_bridge(self):
1341 request = MagicMock()
1342 cidr1 = "10.0.0.0/24"
1343 cidr2 = "192.168.100.0/24"
1344 vlan = factory.make_VLAN(dhcp_on=True)
1345 subnet = factory.make_Subnet(cidr=cidr2, vlan=vlan)
1346 pod_host = factory.make_Machine_with_Interface_on_Subnet(
1347 cidr=cidr1)
1348 space = factory.make_Space('dmz')
1349 pod_host.boot_interface.vlan.space = space
1350 pod_host.boot_interface.vlan.save()
1351
1352 # Create a bond and non-bond on the pod_host
1353 bond_if = factory.make_Interface(
1354 node=pod_host,
1355 subnet=pod_host.boot_interface.vlan.subnet_set.first())
1356 bond = factory.make_Interface(
1357 iftype=INTERFACE_TYPE.BOND, node=pod_host, parents=[
1358 pod_host.boot_interface, bond_if],
1359 subnet=pod_host.boot_interface.vlan.subnet_set.first())
1360 non_bond = factory.make_Interface(node=pod_host, subnet=subnet)
1361 # Create bridge using the bond
1362 bridge = factory.make_Interface(
1363 iftype=INTERFACE_TYPE.BRIDGE, node=pod_host, parents=[bond],
1364 subnet=pod_host.boot_interface.vlan.subnet_set.first())
1365
1366 pod = make_pod_with_hints()
1367 pod.ip_address = pod_host.boot_interface.ip_addresses.first()
1368 pod.save()
1369 interfaces = "eth0:subnet=%s;eth1:subnet=%s" % (cidr1, cidr2)
1370 form = ComposeMachineForm(data={
1371 'interfaces': interfaces,
1372 }, request=request, pod=pod)
1373 self.assertTrue(form.is_valid(), form.errors)
1374 request_machine = form.get_requested_machine(
1375 make_known_host_interfaces(pod))
1376 self.assertThat(request_machine, MatchesAll(
1377 IsInstance(RequestedMachine),
1378 MatchesStructure(
1379 interfaces=MatchesListwise([
1380 MatchesAll(
1381 IsInstance(RequestedMachineInterface),
1382 MatchesStructure(
1383 attach_name=Equals(bridge.name),
1384 attach_type=Equals(InterfaceAttachType.BRIDGE),
1385 attach_options=Equals(None))),
1386 MatchesAll(
1387 IsInstance(RequestedMachineInterface),
1388 MatchesStructure(
1389 attach_name=Equals(non_bond.name),
1390 attach_type=Equals(InterfaceAttachType.MACVLAN),
1391 attach_options=Equals(MACVLAN_MODE.BRIDGE))),
1392 ]))))
1393
1244 def test__get_machine_with_interfaces_by_space_as_bridge(self):1394 def test__get_machine_with_interfaces_by_space_as_bridge(self):
1245 request = MagicMock()1395 request = MagicMock()
1246 pod_host = factory.make_Machine_with_Interface_on_Subnet(1396 pod_host = factory.make_Machine_with_Interface_on_Subnet(

Subscribers

People subscribed via source and target branches