diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py index e3244c36eca..5e410d2fb57 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py @@ -62,6 +62,10 @@ class PciOsWrapper(object): vf_list.append((pci_slot, vf_index)) return vf_list + @classmethod + def pf_device_exists(cls, dev_name): + return os.path.isdir(cls.DEVICE_PATH % dev_name) + @classmethod def is_assigned_vf(cls, dev_name, vf_index, ip_link_show_output): """Check if VF is assigned. @@ -75,12 +79,19 @@ class PciOsWrapper(object): @param vf_index: vf index @param ip_link_show_output: 'ip link show' output """ + + if not cls.pf_device_exists(dev_name): + # If the root PCI path does not exist, then the VF cannot + # actually have been allocated and there is no way we can + # manage it. + return False + path = cls.PCI_PATH % (dev_name, vf_index) try: ifname_list = os.listdir(path) except OSError: - # PCI_PATH does not exist means that the DIRECT VF assigend + # PCI_PATH does not exist means that the DIRECT VF assigned return True # Note(moshele) kernel < 3.13 doesn't create symbolic link @@ -351,6 +362,26 @@ class ESwitchManager(object): embedded_switch.set_device_spoofcheck(pci_slot, enabled) + def _process_emb_switch_map(self, phys_net, dev_name, exclude_devices): + """Process emb_switch_map + @param phys_net: physical network + @param dev_name: device name + @param exclude_devices: PCI devices to ignore. + """ + emb_switches = self.emb_switches_map.get(phys_net, []) + for switch in emb_switches: + if switch.dev_name == dev_name: + if not PciOsWrapper.pf_device_exists(dev_name): + # If the device is given to the VM as PCI-PT + # then delete the respective emb_switch from map + self.emb_switches_map.get(phys_net).remove(switch) + return + + # We don't know about this device at the moment, so add to the map. + if PciOsWrapper.pf_device_exists(dev_name): + self._create_emb_switch(phys_net, dev_name, + exclude_devices.get(dev_name, set())) + def discover_devices(self, device_mappings, exclude_devices): """Discover which Virtual functions to manage. @@ -362,8 +393,8 @@ class ESwitchManager(object): exclude_devices = {} for phys_net, dev_names in six.iteritems(device_mappings): for dev_name in dev_names: - self._create_emb_switch(phys_net, dev_name, - exclude_devices.get(dev_name, set())) + self._process_emb_switch_map(phys_net, dev_name, + exclude_devices) def _create_emb_switch(self, phys_net, dev_name, exclude_devices): embedded_switch = EmbSwitch(phys_net, dev_name, exclude_devices) diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py index c6c9861f3e0..0527bb8e95b 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py @@ -114,6 +114,8 @@ class SriovNicSwitchAgent(object): self.polling_interval = polling_interval self.network_ports = collections.defaultdict(list) self.conf = cfg.CONF + self.device_mappings = physical_devices_mappings + self.exclude_devices = exclude_devices self.setup_eswitch_mgr(physical_devices_mappings, exclude_devices) @@ -377,6 +379,8 @@ class SriovNicSwitchAgent(object): updated_devices_copy = self.updated_devices self.updated_devices = set() try: + self.eswitch_mgr.discover_devices(self.device_mappings, + self.exclude_devices) device_info = self.scan_devices(devices, updated_devices_copy) if self._device_info_has_changes(device_info): LOG.debug("Agent loop found changes! %s", device_info) diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py index ed56cb49f3b..1eecf1f421e 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py @@ -41,6 +41,9 @@ class TestCreateESwitchManager(base.BaseTestCase): "eswitch_manager.PciOsWrapper.scan_vf_devices", side_effect=exc.InvalidDeviceError( dev_name="p6p1", reason="device" " not found")),\ + mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.PciOsWrapper.pf_device_exists", + return_value=True), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True): @@ -55,6 +58,9 @@ class TestCreateESwitchManager(base.BaseTestCase): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", return_value=self.SCANNED_DEVICES),\ + mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.PciOsWrapper.pf_device_exists", + return_value=True), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True): @@ -91,11 +97,36 @@ class TestESwitchManagerApi(base.BaseTestCase): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", return_value=self.SCANNED_DEVICES), \ + mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.PciOsWrapper.pf_device_exists", + return_value=True), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True): eswitch_mgr.discover_devices(device_mappings, None) + def test_discover_devices_with_device(self): + device_mappings = {'physnet1': ['p6p1', 'p6p2']} + with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.PciOsWrapper.pf_device_exists", + return_value=True), \ + mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.ESwitchManager._create_emb_switch", + ) as emb_switch: + self.eswitch_mgr.discover_devices(device_mappings, None) + self.assertTrue(emb_switch.called) + + def test_discover_devices_without_device(self): + device_mappings = {'physnet1': ['p6p1', 'p6p2']} + with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.PciOsWrapper.pf_device_exists", + return_value=False), \ + mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.ESwitchManager._create_emb_switch", + ) as emb_switch: + self.eswitch_mgr.discover_devices(device_mappings, None) + self.assertFalse(emb_switch.called) + def test_get_assigned_devices_info(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_assigned_devices_info", @@ -264,6 +295,36 @@ class TestESwitchManagerApi(base.BaseTestCase): clear_rate_mock.assert_called_once_with(self.PCI_SLOT, self.MIN_RATE) + def test_process_emb_switch_without_device(self): + device_mappings = {'physnet1': ['p6p1', 'p6p2']} + phys_net = 'physnet1' + dev_name = 'p6p1' + self._set_eswitch_manager(self.eswitch_mgr, device_mappings) + with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.PciOsWrapper.pf_device_exists", + return_value=False), \ + mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.ESwitchManager._create_emb_switch", + ) as emb_switch: + self.eswitch_mgr._process_emb_switch_map(phys_net, + dev_name, {}) + self.assertFalse(emb_switch.called) + + def test_process_emb_switch_with_device(self): + device_mappings = {'physnet1': ['p6p1', 'p6p2']} + phys_net = 'physnet1' + dev_name = 'p6p3' + self._set_eswitch_manager(self.eswitch_mgr, device_mappings) + with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.PciOsWrapper.pf_device_exists", + return_value=True), \ + mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.ESwitchManager._create_emb_switch", + ) as emb_switch: + self.eswitch_mgr._process_emb_switch_map(phys_net, + dev_name, {}) + self.assertTrue(emb_switch.called) + def _test_clear_rate(self, rate_type, pci_slot, passed, mac_address): with mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.' 'eswitch_manager.EmbSwitch.set_device_rate') \ @@ -273,7 +334,10 @@ class TestESwitchManagerApi(base.BaseTestCase): return_value=mac_address), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "pci_lib.PciDeviceIPWrapper.link_show", - return_value=''): + return_value=''), \ + mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.PciOsWrapper.pf_device_exists", + return_value=True): self.eswitch_mgr._clear_rate(pci_slot, rate_type) if passed: set_rate_mock.assert_called_once_with(pci_slot, rate_type, 0) @@ -573,9 +637,13 @@ class TestPciOsWrapper(base.BaseTestCase): @mock.patch("os.listdir", side_effect=OSError()) def test_is_assigned_vf_true(self, *args): - self.assertTrue(esm.PciOsWrapper.is_assigned_vf( - self.DEV_NAME, self.VF_INDEX, '')) + with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." + "eswitch_manager.PciOsWrapper.pf_device_exists", + return_value=True): + self.assertTrue(esm.PciOsWrapper.is_assigned_vf( + self.DEV_NAME, self.VF_INDEX, '')) + @mock.patch("os.path.exists", return_value=True) @mock.patch("os.listdir", return_value=[DEV_NAME, "eth1"]) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.is_macvtap_assigned", return_value=False) @@ -583,6 +651,7 @@ class TestPciOsWrapper(base.BaseTestCase): self.assertFalse(esm.PciOsWrapper.is_assigned_vf( self.DEV_NAME, self.VF_INDEX, '')) + @mock.patch("os.path.exists", return_value=True) @mock.patch("os.listdir", return_value=["eth0", "eth1"]) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.is_macvtap_assigned", return_value=True) @@ -591,6 +660,7 @@ class TestPciOsWrapper(base.BaseTestCase): esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME, self.VF_INDEX, '') mock_is_macvtap_assigned.called_with(self.VF_INDEX, "eth0") + @mock.patch("os.path.exists", return_value=True) @mock.patch("os.listdir", side_effect=OSError()) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.is_macvtap_assigned") @@ -598,3 +668,18 @@ class TestPciOsWrapper(base.BaseTestCase): self, mock_is_macvtap_assigned, *args): esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME, self.VF_INDEX, '') self.assertFalse(mock_is_macvtap_assigned.called) + + @mock.patch("os.path.exists", return_value=False) + @mock.patch("os.listdir", return_value=["eth0", "eth1"]) + def test_is_assigned_vf_pf_disappeared(self, list_dir_mock, *args): + self.assertFalse(esm.PciOsWrapper.is_assigned_vf( + self.DEV_NAME, self.VF_INDEX, '')) + self.assertFalse(list_dir_mock.called) + + def test_pf_device_exists_with_no_dir(self): + with mock.patch("os.path.isdir", return_value=False): + self.assertFalse(esm.PciOsWrapper.pf_device_exists('p6p1')) + + def test_pf_device_exists_with_dir(self): + with mock.patch("os.path.isdir", return_value=True): + self.assertTrue(esm.PciOsWrapper.pf_device_exists('p6p1'))