From 4093727ae9c751cce8c39dd0eb022b2c9b90d624 Mon Sep 17 00:00:00 2001 From: LIU Yulong Date: Sat, 13 Jun 2020 23:14:47 +0800 Subject: [PATCH] [L3] Check agent gateway port robustly In patch [1] it introduced a binding of DB uniq constraint for L3 agent gateway. In some extreme case the DvrFipGatewayPortAgentBinding is in DB while the gateway port not. The current code path only checks the binding existence which will pass a "None" port to the following code path that results an AttributeError. This patch adds a simple check for that gateway port, if it is not created, new one. [1] https://review.opendev.org/#/c/702547/ Closes-Bug: #1883089 Change-Id: Ia90f2ee435b0a3476dbea028d3200cefe11e35e4 (cherry picked from commit 5fdfd4cbfc56425c56b4a5702d92a97da56ab6e8) --- neutron/db/l3_dvr_db.py | 3 ++- neutron/tests/unit/db/test_l3_dvr_db.py | 36 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/neutron/db/l3_dvr_db.py b/neutron/db/l3_dvr_db.py index 463174c9812..8ff05eb9d5d 100644 --- a/neutron/db/l3_dvr_db.py +++ b/neutron/db/l3_dvr_db.py @@ -1096,13 +1096,14 @@ class _DVRAgentInterfaceMixin(object): try: fip_agent_port_obj.create() except o_exc.NeutronDbObjectDuplicateEntry: - LOG.debug("Floating IP Agent Gateway port for network " + LOG.debug("Floating IP Gateway port agent binding for network " "%(network)s already exists on host %(host)s. " "Probably it was just created by other worker.", {'network': network_id, 'host': host}) agent_port = self._get_agent_gw_ports_exist_for_network( context, network_id, host, l3_agent_db['id']) + if agent_port: LOG.debug("Floating IP Agent Gateway port %(gw)s found " "for the destination host: %(dest_host)s", {'gw': agent_port, diff --git a/neutron/tests/unit/db/test_l3_dvr_db.py b/neutron/tests/unit/db/test_l3_dvr_db.py index e2eda73a2e7..c6d08afd792 100644 --- a/neutron/tests/unit/db/test_l3_dvr_db.py +++ b/neutron/tests/unit/db/test_l3_dvr_db.py @@ -762,6 +762,42 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): mock.call(self.ctx, network_id, 'host', fipagent['id'])]) self.assertIsNotNone(fport) + def test_create_fip_agent_gw_port_agent_binding_exists(self): + network_id = _uuid() + fport_db = {'id': _uuid()} + self.mixin._get_agent_gw_ports_exist_for_network = mock.Mock( + side_effect=[None, None]) + fipagent = agent_obj.Agent( + self.ctx, + id=_uuid(), + binary='foo-agent', + host='host', + agent_type='L3 agent', + topic='foo_topic', + configurations={"agent_mode": "dvr"}) + self.mixin._get_agent_by_type_and_host = mock.Mock( + return_value=fipagent) + self.mixin._populate_mtu_and_subnets_for_ports = mock.Mock() + + with mock.patch.object( + router_obj.DvrFipGatewayPortAgentBinding, 'create', + side_effect=o_exc.NeutronDbObjectDuplicateEntry( + mock.Mock(), mock.Mock()) + ) as dvr_fip_gateway_port_agent_binding_create,\ + mock.patch.object( + plugin_utils, "create_port", return_value=fport_db): + fport = self.mixin.create_fip_agent_gw_port_if_not_exists( + self.ctx, + network_id, + 'host') + dvr_fip_gateway_port_agent_binding_create.assert_called_once_with() + self.mixin._get_agent_gw_ports_exist_for_network.assert_has_calls([ + mock.call(self.ctx, network_id, 'host', fipagent['id']), + mock.call(self.ctx, network_id, 'host', fipagent['id'])]) + self.mixin._populate_mtu_and_subnets_for_ports.assert_has_calls([ + mock.call(self.ctx, [fport_db])]) + self.assertIsNotNone(fport) + def test_create_floatingip_agent_gw_port_with_non_dvr_router(self): floatingip = { 'id': _uuid(),