From 035708c37d587e4c5ede7fe80270bdbff98016ac Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Wed, 3 Oct 2018 12:54:53 -0400 Subject: [PATCH] Handle IndexError in _populate_neutron_binding_profile This fixes the code that was blindly pop'ing an entry from an empty list of PCI devices claimed by the instance. It's not exactly clear how we can get into this situation, presumably there was a failure in the actual PCI device claim logic in the ResourceTracker - maybe related to the configured PCI passthrough whitelist. Regardless, we should handle the empty PCI device list in this method and raise an appropriate exception to fail the build on this host. Change-Id: I401bb74cf6e17c2b72cc62bf8ec03ec58238c44a Closes-Bug: #1795064 --- nova/network/neutronv2/api.py | 19 +++++++++++++++++-- nova/tests/unit/network/test_neutronv2.py | 13 +++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index be6aebc8f609..dad2666f646d 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -1398,10 +1398,25 @@ class API(base_api.NetworkAPI): """Populate neutron binding:profile. Populate it with SR-IOV related information + + :raises PciDeviceNotFound: If a claimed PCI device for the given + pci_request_id cannot be found on the instance. """ if pci_request_id: - pci_dev = pci_manager.get_instance_pci_devs( - instance, pci_request_id).pop() + pci_devices = pci_manager.get_instance_pci_devs( + instance, pci_request_id) + if not pci_devices: + # The pci_request_id likely won't mean much except for tracing + # through the logs since it is generated per request. + LOG.error('Unable to find PCI device using PCI request ID in ' + 'list of claimed instance PCI devices: %s. Is the ' + '[pci]/passthrough_whitelist configuration correct?', + # Convert to a primitive list to stringify it. + list(instance.pci_devices), instance=instance) + raise exception.PciDeviceNotFound( + _('PCI device not found for request ID %s.') % + pci_request_id) + pci_dev = pci_devices.pop() if port_req_body['port'].get(BINDING_PROFILE) is None: port_req_body['port'][BINDING_PROFILE] = {} profile = copy.deepcopy(port_req_body['port'][BINDING_PROFILE]) diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index 24d8138c95dc..e58a469cd76a 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -5480,6 +5480,19 @@ class TestNeutronv2Portbinding(TestNeutronv2Base): exception.PciDeviceNotFound, api._populate_neutron_binding_profile, instance, pci_req_id, port_req_body) + @mock.patch.object(pci_manager, 'get_instance_pci_devs', return_value=[]) + def test_populate_neutron_binding_profile_pci_dev_not_found( + self, mock_get_instance_pci_devs): + api = neutronapi.API() + instance = objects.Instance(pci_devices=objects.PciDeviceList()) + port_req_body = {'port': {}} + pci_req_id = 'my_req_id' + self.assertRaises(exception.PciDeviceNotFound, + api._populate_neutron_binding_profile, + instance, pci_req_id, port_req_body) + mock_get_instance_pci_devs.assert_called_once_with( + instance, pci_req_id) + @mock.patch.object(pci_manager, 'get_instance_pci_devs') def test_pci_parse_whitelist_called_once(self, mock_get_instance_pci_devs):