diff --git a/neutron/agent/l3/dvr_router.py b/neutron/agent/l3/dvr_router.py index 4d2539ff44f..5eb1bf557c3 100644 --- a/neutron/agent/l3/dvr_router.py +++ b/neutron/agent/l3/dvr_router.py @@ -500,6 +500,17 @@ class DvrRouter(router.RouterInfo): "%s", fip_agent_port) if floating_ips: is_first = self.fip_ns.subscribe(self.router_id) + if is_first and not fip_agent_port: + LOG.debug("No FloatingIP agent gateway port possibly due to " + "late binding of the private port to the host, " + "requesting agent gateway port for 'network-id' :" + "%s", ex_gw_port['network_id']) + fip_agent_port = self.agent.plugin_rpc.get_agent_gateway_port( + self.agent.context, ex_gw_port['network_id']) + if not fip_agent_port: + LOG.error(_LE("No FloatingIP agent gateway port " + "returned from server for 'network-id': " + "%s"), ex_gw_port['network_id']) if is_first and fip_agent_port: if 'subnets' not in fip_agent_port: LOG.error(_LE('Missing subnet/agent_gateway_port')) diff --git a/neutron/tests/functional/agent/test_l3_agent.py b/neutron/tests/functional/agent/test_l3_agent.py index 5b503abc6fb..d8d1a03db7f 100755 --- a/neutron/tests/functional/agent/test_l3_agent.py +++ b/neutron/tests/functional/agent/test_l3_agent.py @@ -1246,3 +1246,48 @@ class TestDvrRouter(L3AgentTestFramework): router.router_id, router.fip_ns.get_int_device_name, router.fip_ns.get_name()) self.assertEqual(expected_mtu, dev_mtu) + + def test_dvr_router_fip_agent_mismatch(self): + """Test to validate the floatingip agent mismatch. + + This test validates the condition where floatingip agent + gateway port host mismatches with the agent and so the + binding will not be there. + + """ + self.agent.conf.agent_mode = 'dvr' + router_info = self.generate_dvr_router_info() + floating_ip = router_info['_floatingips'][0] + floating_ip['host'] = 'my_new_host' + # In this case the floatingip binding is different and so it + # should not create the floatingip namespace on the given agent. + # This is also like there is no current binding. + router1 = self.manage_router(self.agent, router_info) + fip_ns = router1.fip_ns.get_name() + self.assertTrue(self._namespace_exists(router1.ns_name)) + self.assertFalse(self._namespace_exists(fip_ns)) + self._assert_snat_namespace_does_not_exist(router1) + + def test_dvr_router_fip_late_binding(self): + """Test to validate the floatingip migration or latebinding. + + This test validates the condition where floatingip private + port changes while migration or when the private port host + binding is done later after floatingip association. + + """ + self.agent.conf.agent_mode = 'dvr' + router_info = self.generate_dvr_router_info() + fip_agent_gw_port = router_info[l3_constants.FLOATINGIP_AGENT_INTF_KEY] + # Now let us not pass the FLOATINGIP_AGENT_INTF_KEY, to emulate + # that the server did not create the port, since there was no valid + # host binding. + router_info[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = [] + mocked_gw_port = ( + neutron_l3_agent.L3PluginApi.return_value.get_agent_gateway_port) + mocked_gw_port.return_value = fip_agent_gw_port[0] + router1 = self.manage_router(self.agent, router_info) + fip_ns = router1.fip_ns.get_name() + self.assertTrue(self._namespace_exists(router1.ns_name)) + self.assertTrue(self._namespace_exists(fip_ns)) + self._assert_snat_namespace_does_not_exist(router1) diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index 727ac6f4358..651d66e22be 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -1183,6 +1183,50 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent_gateway_port[0], ri.get_floating_agent_gw_interface(fake_network_id)) + @mock.patch.object(lla.LinkLocalAllocator, '_write') + def test_create_dvr_fip_interfaces_for_late_binding(self, lla_write): + fake_network_id = _uuid() + fake_subnet_id = _uuid() + fake_floatingips = {'floatingips': [ + {'id': _uuid(), + 'floating_ip_address': '20.0.0.3', + 'fixed_ip_address': '192.168.0.1', + 'floating_network_id': _uuid(), + 'port_id': _uuid(), + 'host': HOSTNAME}]} + agent_gateway_port = ( + {'fixed_ips': [ + {'ip_address': '20.0.0.30', + 'prefixlen': 24, + 'subnet_id': fake_subnet_id}], + 'subnets': [ + {'id': fake_subnet_id, + 'gateway_ip': '20.0.0.1'}], + 'id': _uuid(), + 'network_id': fake_network_id, + 'mac_address': 'ca:fe:de:ad:be:ef'} + ) + + router = prepare_router_data(enable_snat=True) + router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] + router[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = [] + router['distributed'] = True + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + ri = dvr_router.DvrRouter( + agent, HOSTNAME, router['id'], router, **self.ri_kwargs) + + ext_gw_port = ri.router.get('gw_port') + ri.fip_ns = agent.get_fip_ns(ext_gw_port['network_id']) + ri.dist_fip_count = 0 + ri.fip_ns.subscribe = mock.Mock() + with mock.patch.object(agent.plugin_rpc, + 'get_agent_gateway_port') as fip_gw_port: + fip_gw_port.return_value = agent_gateway_port + ri.create_dvr_fip_interfaces(ext_gw_port) + self.assertTrue(fip_gw_port.called) + self.assertEqual(agent_gateway_port, + ri.fip_ns.agent_gateway_port) + @mock.patch.object(lla.LinkLocalAllocator, '_write') def test_create_dvr_fip_interfaces(self, lla_write): fake_network_id = _uuid()