diff --git a/neutron/agent/common/ovs_lib.py b/neutron/agent/common/ovs_lib.py index 010644f01b4..9fe9df230c8 100644 --- a/neutron/agent/common/ovs_lib.py +++ b/neutron/agent/common/ovs_lib.py @@ -436,7 +436,8 @@ class OVSBridge(BaseOVS): def get_vifs_by_ids(self, port_ids): interface_info = self.get_ports_attributes( - "Interface", columns=["name", "external_ids", "ofport"]) + "Interface", columns=["name", "external_ids", "ofport"], + if_exists=True) by_id = {x['external_ids'].get('iface-id'): x for x in interface_info} result = {} for port_id in port_ids: diff --git a/neutron/tests/functional/agent/l2/base.py b/neutron/tests/functional/agent/l2/base.py index ef3779dd74b..9a29a72c3e3 100644 --- a/neutron/tests/functional/agent/l2/base.py +++ b/neutron/tests/functional/agent/l2/base.py @@ -120,8 +120,8 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase): agent.sg_agent = mock.Mock() return agent - def start_agent(self, agent): - self.setup_agent_rpc_mocks(agent) + def start_agent(self, agent, unplug_ports=[]): + self.setup_agent_rpc_mocks(agent, unplug_ports) polling_manager = polling.InterfacePollingMinimizer() self.addCleanup(polling_manager.stop) polling_manager.start() @@ -164,6 +164,11 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase): self.driver.init_l3(port.get('vif_name'), ip_cidrs, namespace=self.namespace) + def _unplug_ports(self, ports, agent): + for port in ports: + self.driver.unplug( + port.get('vif_name'), agent.int_br.br_name, self.namespace) + def _get_device_details(self, port, network): dev = {'device': port['id'], 'port_id': port['id'], @@ -236,15 +241,17 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase): 'devices_down': dev_down, 'failed_devices_down': []} - def setup_agent_rpc_mocks(self, agent): + def setup_agent_rpc_mocks(self, agent, unplug_ports): def mock_device_details(context, devices, agent_id, host=None): - details = [] for port in self.ports: if port['id'] in devices: dev = self._get_device_details( port, self.network) details.append(dev) + ports_to_unplug = [x for x in unplug_ports if x['id'] in devices] + if ports_to_unplug: + self._unplug_ports(ports_to_unplug, self.agent) return {'devices': details, 'failed_devices': []} (agent.plugin_rpc.get_devices_details_list_and_failed_devices. @@ -262,11 +269,12 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase): self.agent.plugin_rpc.update_device_list.side_effect = ( mock_device_raise_exception) - def wait_until_ports_state(self, ports, up): + def wait_until_ports_state(self, ports, up, timeout=60): port_ids = [p['id'] for p in ports] agent_utils.wait_until_true( lambda: self._expected_plugin_rpc_call( - self.agent.plugin_rpc.update_device_list, port_ids, up)) + self.agent.plugin_rpc.update_device_list, port_ids, up), + timeout=timeout) def setup_agent_and_ports(self, port_dicts, create_tunnels=True, trigger_resync=False): diff --git a/neutron/tests/functional/agent/test_l2_ovs_agent.py b/neutron/tests/functional/agent/test_l2_ovs_agent.py index c1baa8bafa2..b57a8f93158 100644 --- a/neutron/tests/functional/agent/test_l2_ovs_agent.py +++ b/neutron/tests/functional/agent/test_l2_ovs_agent.py @@ -16,6 +16,7 @@ import time +from eventlet.timeout import Timeout from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.tests.common import net_helpers from neutron.tests.functional.agent.l2 import base @@ -105,6 +106,20 @@ class TestOVSAgent(base.OVSAgentTestFramework): self.agent.setup_integration_br() time.sleep(0.25) + def test_noresync_after_port_gone(self): + '''This will test the scenario where a port is removed after listing + it but before getting vif info about it. + ''' + self.ports = self.create_test_ports(amount=2) + self.agent = self.create_agent(create_tunnels=False) + self.network = self._create_test_network_dict() + self._plug_ports(self.network, self.ports, self.agent) + self.start_agent(self.agent, unplug_ports=[self.ports[1]]) + self.wait_until_ports_state([self.ports[0]], up=True) + self.assertRaises( + Timeout, self.wait_until_ports_state, [self.ports[1]], up=True, + timeout=10) + class TestOVSAgentExtensionConfig(base.OVSAgentTestFramework): def setUp(self): diff --git a/neutron/tests/unit/agent/common/test_ovs_lib.py b/neutron/tests/unit/agent/common/test_ovs_lib.py index 8741dff79d2..468f953baa0 100644 --- a/neutron/tests/unit/agent/common/test_ovs_lib.py +++ b/neutron/tests/unit/agent/common/test_ovs_lib.py @@ -691,6 +691,9 @@ class OVS_Lib_Test(base.BaseTestCase): self.assertEqual('pid2', by_id['pid2'].vif_id) self.assertEqual('qvo2', by_id['pid2'].port_name) self.assertEqual(2, by_id['pid2'].ofport) + self.br.get_ports_attributes.assert_has_calls( + [mock.call('Interface', columns=['name', 'external_ids', 'ofport'], + if_exists=True)]) def _test_get_vif_port_by_id(self, iface_id, data, br_name=None, extra_calls_and_values=None):