From 9e9a8a07c3cb1eed074399af36affe857011133e Mon Sep 17 00:00:00 2001 From: Swaminathan Vasudevan Date: Thu, 6 Jul 2017 14:11:50 -0700 Subject: [PATCH] DVR: Fix binding info for DVR port not found error Recently we have been seeing an error in neutron associated with DVR routers, that says 'Binding info for DVR port not found'. This error is thrown when the 'get_bound_port_context' is called when trying to notify_l2pop_port_wiring. notify_l2pop_port_wiring is intended for router 'HA' ports, so the get_bound_port_context should be called here only for 'HA' ports. This was introduced by a recent refactor in neutron Icd4cd4e3f735e88299e86468380c5f786e7628fe Change-Id: I1c636344068518aa26be6c96c598c61b7f0f3563 Closes-Bug: #1702769 --- neutron/plugins/ml2/rpc.py | 7 +++ .../ml2/drivers/l2pop/test_mech_driver.py | 55 +++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/neutron/plugins/ml2/rpc.py b/neutron/plugins/ml2/rpc.py index 22ea803e20e..cd27928a632 100644 --- a/neutron/plugins/ml2/rpc.py +++ b/neutron/plugins/ml2/rpc.py @@ -302,6 +302,13 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): 'l2population') if not l2pop_driver: return + port = ml2_db.get_port(rpc_context, port_id) + if not port: + return + # NOTE: DVR ports are already handled and updated through l2pop + # and so we don't need to update it again here + if port['device_owner'] == n_const.DEVICE_OWNER_DVR_INTERFACE: + return port_context = plugin.get_bound_port_context( rpc_context, port_id) if not port_context: diff --git a/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py b/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py index 25aff7cc146..11f0431b95b 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py @@ -239,12 +239,19 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase): plugin.update_port(self.adminContext, port['id'], {port_def.RESOURCE_NAME: port}) - def _get_first_interface(self, net_id, router_id): + def _get_first_interface(self, net_id, router): plugin = directory.get_plugin() - device_filter = {'device_id': [router_id], - 'device_owner': - [constants.DEVICE_OWNER_HA_REPLICATED_INT]} - return plugin.get_ports(self.adminContext, filters=device_filter)[0] + if router['distributed']: + device_filter = {'device_id': [router['id']], + 'device_owner': + [constants.DEVICE_OWNER_DVR_INTERFACE]} + else: + device_filter = {'device_id': [router['id']], + 'device_owner': + [constants.DEVICE_OWNER_HA_REPLICATED_INT]} + ports = plugin.get_ports(self.adminContext, filters=device_filter) + if ports: + return ports[0] def _add_router_interface(self, subnet, router, host): interface_info = {'subnet_id': subnet['id']} @@ -254,7 +261,7 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase): self.adminContext, {router['id']: n_const.HA_ROUTER_STATE_ACTIVE}, host) - port = self._get_first_interface(subnet['network_id'], router['id']) + port = self._get_first_interface(subnet['network_id'], router) self.mock_cast.reset_mock() self.mock_fanout.reset_mock() @@ -268,6 +275,12 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase): self._bind_router(router['id'], router['tenant_id']) return router + def _create_dvr_router(self): + self._setup_l3() + router = self._create_router(distributed=True) + self._bind_router(router['id'], router['tenant_id']) + return router + def _verify_remove_fdb(self, expected, agent_id, device, host=None): self.mock_fanout.reset_mock() self.callbacks.update_device_down(self.adminContext, agent_id=host, @@ -339,6 +352,36 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase): self.mock_fanout.assert_called_with( mock.ANY, 'remove_fdb_entries', expected) + def test_ha_agents_with_dvr_rtr_does_not_get_other_fdb(self): + router = self._create_dvr_router() + directory.add_plugin(plugin_constants.L3, self.plugin) + with self.subnet(network=self._network, enable_dhcp=False) as snet: + host_arg = {portbindings.HOST_ID: HOST_4, 'admin_state_up': True} + with self.port(subnet=snet, + device_owner=DEVICE_OWNER_COMPUTE, + arg_list=(portbindings.HOST_ID,), + **host_arg) as port1: + p1 = port1['port'] + device1 = 'tap' + p1['id'] + self.callbacks.update_device_up( + self.adminContext, agent_id=HOST_4, device=device1) + + subnet = snet['subnet'] + port = self._add_router_interface(subnet, router, HOST) + + self.mock_cast.assert_not_called() + self.mock_fanout.assert_not_called() + + self.mock_cast.reset_mock() + self.mock_fanout.reset_mock() + + self.callbacks.update_device_up( + self.adminContext, agent_id=HOST_2, + device=port['id'], host=HOST_2) + + self.mock_cast.assert_not_called() + self.mock_fanout.assert_not_called() + def test_ha_agents_get_other_fdb(self): # First network port is added on HOST4, then HA router port is # added on HOST and HOST2.