From 53ab6068a318079757fa76db37fc9838db2b4f24 Mon Sep 17 00:00:00 2001 From: Ludovic Beliveau Date: Mon, 4 Jul 2016 09:45:07 -0400 Subject: [PATCH] Don't parse PCI whitelist every time neutron ports are created The neutronv2 API is calling the method get_pci_device_devspec() everytime a neutron port is created in order to get a PciDeviceSpec for a given PCI device. This method creates a new Whitelist (based on the config CONF.pci_passthrough_whitelist) and parses it every time it is called. This is not a huge overhead but this is obvioulsy not needed and a waste of cycles. Since only neutronv2 API uses get_pci_device_devspec(), this commit removes the method in favor of using the Whitelist object directly (like it is done in the PciDevTracker). Change-Id: Idee4e9edecff0672680f323a916201aee8eeeabd Closes-Bug: #1598843 --- nova/network/neutronv2/api.py | 33 ++++++++++---------- nova/pci/whitelist.py | 5 --- nova/tests/unit/network/test_neutronv2.py | 37 ++++++++++++++++++++--- 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index 7192031c4f..415d9847b4 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -224,18 +224,6 @@ def _filter_hypervisor_macs(instance, ports, hypervisor_macs): return available_macs -def get_pci_device_profile(pci_dev): - dev_spec = pci_whitelist.get_pci_device_devspec(pci_dev) - if dev_spec: - return {'pci_vendor_info': "%s:%s" % - (pci_dev.vendor_id, pci_dev.product_id), - 'pci_slot': pci_dev.address, - 'physical_network': - dev_spec.get_tags().get('physical_network')} - raise exception.PciDeviceNotFound(node_id=pci_dev.compute_node_id, - address=pci_dev.address) - - class API(base_api.NetworkAPI): """API for interacting with the neutron 2.x API.""" @@ -243,6 +231,8 @@ class API(base_api.NetworkAPI): super(API, self).__init__() self.last_neutron_extension_sync = None self.extensions = {} + self.pci_whitelist = pci_whitelist.Whitelist( + CONF.pci.passthrough_whitelist) def _update_port_with_migration_profile( self, instance, port_id, port_profile, admin_client): @@ -1025,8 +1015,18 @@ class API(base_api.NetworkAPI): self._refresh_neutron_extensions_cache(context, neutron=neutron) return constants.AUTO_ALLOCATE_TOPO_EXT in self.extensions - @staticmethod - def _populate_neutron_binding_profile(instance, pci_request_id, + def _get_pci_device_profile(self, pci_dev): + dev_spec = self.pci_whitelist.get_devspec(pci_dev) + if dev_spec: + return {'pci_vendor_info': "%s:%s" % + (pci_dev.vendor_id, pci_dev.product_id), + 'pci_slot': pci_dev.address, + 'physical_network': + dev_spec.get_tags().get('physical_network')} + raise exception.PciDeviceNotFound(node_id=pci_dev.compute_node_id, + address=pci_dev.address) + + def _populate_neutron_binding_profile(self, instance, pci_request_id, port_req_body): """Populate neutron binding:profile. @@ -1035,7 +1035,7 @@ class API(base_api.NetworkAPI): if pci_request_id: pci_dev = pci_manager.get_instance_pci_devs( instance, pci_request_id).pop() - profile = get_pci_device_profile(pci_dev) + profile = self._get_pci_device_profile(pci_dev) port_req_body['port']['binding:profile'] = profile @staticmethod @@ -2398,7 +2398,8 @@ class API(base_api.NetworkAPI): pci_slot = binding_profile.get('pci_slot') new_dev = pci_mapping.get(pci_slot) if new_dev: - binding_profile.update(get_pci_device_profile(new_dev)) + binding_profile.update( + self._get_pci_device_profile(new_dev)) updates[BINDING_PROFILE] = binding_profile else: raise exception.PortUpdateFailed(port_id=p['id'], diff --git a/nova/pci/whitelist.py b/nova/pci/whitelist.py index 5c3549064a..ef7793f247 100644 --- a/nova/pci/whitelist.py +++ b/nova/pci/whitelist.py @@ -93,8 +93,3 @@ class Whitelist(object): for spec in self.specs: if spec.match_pci_obj(pci_dev): return spec - - -def get_pci_device_devspec(pci_dev): - dev_filter = Whitelist(CONF.pci.passthrough_whitelist) - return dev_filter.get_devspec(pci_dev) diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index 59f67c8b8d..8d17f1cf41 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -3700,7 +3700,7 @@ class TestNeutronv2WithMock(test.TestCase): update_port_mock.assert_called_once_with( 'fake-port-2', {'port': {'binding:host_id': instance.host}}) - @mock.patch.object(pci_whitelist, 'get_pci_device_devspec') + @mock.patch.object(pci_whitelist.Whitelist, 'get_devspec') @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock()) def test_update_port_bindings_for_instance_with_pci(self, get_client_mock, @@ -3756,7 +3756,7 @@ class TestNeutronv2WithMock(test.TestCase): 'physical_network': 'physnet1', 'pci_vendor_info': '1377:0047'}}}) - @mock.patch.object(pci_whitelist, 'get_pci_device_devspec') + @mock.patch.object(pci_whitelist.Whitelist, 'get_devspec') @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock()) def test_update_port_bindings_for_instance_with_pci_fail(self, get_client_mock, @@ -4573,7 +4573,7 @@ class TestNeutronv2Portbinding(TestNeutronv2Base): self.assertEqual(host_id, port_req_body['port']['binding:host_id']) self.assertFalse(port_req_body['port'].get('binding:profile')) - @mock.patch.object(pci_whitelist, 'get_pci_device_devspec') + @mock.patch.object(pci_whitelist.Whitelist, 'get_devspec') @mock.patch.object(pci_manager, 'get_instance_pci_devs') def test_populate_neutron_extension_values_binding_sriov(self, mock_get_instance_pci_devs, @@ -4604,7 +4604,7 @@ class TestNeutronv2Portbinding(TestNeutronv2Base): self.assertEqual(profile, port_req_body['port']['binding:profile']) - @mock.patch.object(pci_whitelist, 'get_pci_device_devspec') + @mock.patch.object(pci_whitelist.Whitelist, 'get_devspec') @mock.patch.object(pci_manager, 'get_instance_pci_devs') def test_populate_neutron_extension_values_binding_sriov_fail( self, mock_get_instance_pci_devs, mock_get_pci_device_devspec): @@ -4626,6 +4626,35 @@ 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') + def test_pci_parse_whitelist_called_once(self, + mock_get_instance_pci_devs): + white_list = [ + '{"address":"0000:0a:00.1","physical_network":"default"}'] + cfg.CONF.set_override('passthrough_whitelist', white_list, 'pci') + + api = neutronapi.API() + host_id = 'my_host_id' + instance = {'host': host_id} + pci_req_id = 'my_req_id' + port_req_body = {'port': {}} + pci_dev = {'vendor_id': '1377', + 'product_id': '0047', + 'address': '0000:0a:00.1', + } + + whitelist = pci_whitelist.Whitelist(CONF.pci.passthrough_whitelist) + with mock.patch.object(pci_whitelist.Whitelist, + '_parse_white_list_from_config', + wraps=whitelist._parse_white_list_from_config + ) as mock_parse_whitelist: + for i in range(2): + mydev = objects.PciDevice.create(None, pci_dev) + mock_get_instance_pci_devs.return_value = [mydev] + api._populate_neutron_binding_profile(instance, + pci_req_id, port_req_body) + self.assertEqual(0, mock_parse_whitelist.call_count) + def _populate_pci_mac_address_fakes(self): instance = fake_instance.fake_instance_obj(self.context) pci_dev = {'vendor_id': '1377', -- 2.39.5