diff --git a/neutron/agent/l3/dvr_edge_router.py b/neutron/agent/l3/dvr_edge_router.py index 2fed106ec11..d015af3941b 100644 --- a/neutron/agent/l3/dvr_edge_router.py +++ b/neutron/agent/l3/dvr_edge_router.py @@ -21,6 +21,7 @@ from neutron.agent.l3 import router_info as router from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.common import constants as n_const +from neutron.common import utils as common_utils LOG = logging.getLogger(__name__) @@ -55,6 +56,15 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter): "current dvr_snat host.", self.snat_namespace.name) self.external_gateway_removed(ex_gw_port, interface_name) + def _list_centralized_floating_ip_cidrs(self): + # Compute a list of addresses this gw is supposed to have. + # This avoids unnecessarily removing those addresses and + # causing a momentarily network outage. + floating_ips = self.get_floating_ips() + return [common_utils.ip_to_cidr(ip['floating_ip_address']) + for ip in floating_ips + if ip.get(n_const.DVR_SNAT_BOUND)] + def external_gateway_updated(self, ex_gw_port, interface_name): if not self._is_this_snat_host(): # no centralized SNAT gateway for this node/agent @@ -71,10 +81,11 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter): # newly created gateway return self.external_gateway_added(ex_gw_port, interface_name) else: + preserve_ips = self._list_centralized_floating_ip_cidrs() self._external_gateway_added(ex_gw_port, interface_name, self.snat_namespace.name, - preserve_ips=[]) + preserve_ips) def _external_gateway_removed(self, ex_gw_port, interface_name): super(DvrEdgeRouter, self).external_gateway_removed(ex_gw_port, diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index 17200b0a4bd..6b9bdc24e24 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -750,6 +750,47 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_external_gateway_updated_dual_stack(self): self._test_external_gateway_updated(dual_stack=True) + def test_external_gateway_updated_dvr(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + agent.conf.agent_mode = 'dvr_snat' + agent.host = HOSTNAME + router = l3_test_common.prepare_router_data(num_internal_ports=2) + router['distributed'] = True + router['gw_port_host'] = HOSTNAME + self._set_ri_kwargs(agent, router['id'], router) + ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) + ri._create_dvr_gateway = mock.Mock() + ri.get_snat_interfaces = mock.Mock(return_value=self.snat_ports) + ri.snat_ports = self.snat_ports + ri._create_snat_namespace() + ex_net_id = _uuid() + ri.fip_ns = agent.get_fip_ns(ex_net_id) + ri.internal_ports = self.snat_ports + ri.use_ipv6 = False + interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test( + self, ri) + + fake_fip = {'floatingips': [{'id': _uuid(), + 'floating_ip_address': '192.168.1.34', + 'fixed_ip_address': '192.168.0.1', + 'port_id': _uuid(), + 'dvr_snat_bound': True}]} + router[lib_constants.FLOATINGIP_KEY] = fake_fip['floatingips'] + ri.external_gateway_updated(ex_gw_port, interface_name) + self.assertEqual(1, self.mock_driver.plug.call_count) + self.assertEqual(1, self.mock_driver.init_router_port.call_count) + exp_arp_calls = [mock.call(ri.snat_namespace.name, interface_name, + '20.0.0.30')] + self.send_adv_notif.assert_has_calls(exp_arp_calls) + ip_cidrs = ['20.0.0.30/24'] + kwargs = {'preserve_ips': ['192.168.1.34/32'], + 'namespace': ri.snat_namespace.name, + 'extra_subnets': [{'cidr': '172.16.0.0/24'}], + 'clean_connections': True} + self.mock_driver.init_router_port.assert_called_with(interface_name, + ip_cidrs, + **kwargs) + def test_dvr_edge_router_init_for_snat_namespace_object(self): router = {'id': _uuid()} self._set_ri_kwargs(mock.Mock(), router['id'], router)