From 5652e20b0bdc3a707d11d2b9ee521066b8f1150f Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Tue, 15 Oct 2013 22:39:47 +0000 Subject: [PATCH] Preserve floating ips when initializing l3 gateway interface Change-Id: I5a88225d291538cb9db0f8f4afa348192b8b984d Closes-Bug: #1233271 --- neutron/agent/l3_agent.py | 12 +++++++++++- neutron/agent/linux/interface.py | 7 +++++-- neutron/tests/unit/test_l3_agent.py | 8 ++++++++ neutron/tests/unit/test_linux_interface.py | 15 +++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/neutron/agent/l3_agent.py b/neutron/agent/l3_agent.py index 16c73fa81..6c9783fcf 100644 --- a/neutron/agent/l3_agent.py +++ b/neutron/agent/l3_agent.py @@ -50,6 +50,7 @@ NS_PREFIX = 'qrouter-' INTERNAL_DEV_PREFIX = 'qr-' EXTERNAL_DEV_PREFIX = 'qg-' RPC_LOOP_INTERVAL = 1 +FLOATING_IP_CIDR_SUFFIX = '/32' class L3PluginApi(proxy.RpcProxy): @@ -521,8 +522,17 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager): bridge=self.conf.external_network_bridge, namespace=ri.ns_name(), prefix=EXTERNAL_DEV_PREFIX) + + # Compute a list of addresses this router is supposed to have. + # This avoids unnecessarily removing those addresses and + # causing a momentarily network outage. + floating_ips = ri.router.get(l3_constants.FLOATINGIP_KEY, []) + preserve_ips = [ip['floating_ip_address'] + FLOATING_IP_CIDR_SUFFIX + for ip in floating_ips] + self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']], - namespace=ri.ns_name()) + namespace=ri.ns_name(), + preserve_ips=preserve_ips) ip_address = ex_gw_port['ip_cidr'].split('/')[0] self._send_gratuitous_arp_packet(ri, interface_name, ip_address) diff --git a/neutron/agent/linux/interface.py b/neutron/agent/linux/interface.py index 42a1f82d1..f0d738712 100644 --- a/neutron/agent/linux/interface.py +++ b/neutron/agent/linux/interface.py @@ -70,10 +70,12 @@ class LinuxInterfaceDriver(object): self.conf = conf self.root_helper = config.get_root_helper(conf) - def init_l3(self, device_name, ip_cidrs, namespace=None): + def init_l3(self, device_name, ip_cidrs, namespace=None, + preserve_ips=[]): """Set the L3 settings for the interface using data from the port. ip_cidrs: list of 'X.X.X.X/YY' strings + preserve_ips: list of ip cidrs that should not be removed from device """ device = ip_lib.IPDevice(device_name, self.root_helper, @@ -95,7 +97,8 @@ class LinuxInterfaceDriver(object): # clean up any old addresses for ip_cidr, ip_version in previous.items(): - device.addr.delete(ip_version, ip_cidr) + if ip_cidr not in preserve_ips: + device.addr.delete(ip_version, ip_cidr) def check_bridge_exists(self, bridge): if not ip_lib.device_exists(bridge): diff --git a/neutron/tests/unit/test_l3_agent.py b/neutron/tests/unit/test_l3_agent.py index f7cfdcff3..d4c55bd8d 100644 --- a/neutron/tests/unit/test_l3_agent.py +++ b/neutron/tests/unit/test_l3_agent.py @@ -166,12 +166,20 @@ class TestBasicRouterOperations(base.BaseTestCase): if action == 'add': self.device_exists.return_value = False + ri.router = mock.Mock() + ri.router.get.return_value = [{'floating_ip_address': + '192.168.1.34'}] agent.external_gateway_added(ri, ex_gw_port, interface_name, internal_cidrs) self.assertEqual(self.mock_driver.plug.call_count, 1) self.assertEqual(self.mock_driver.init_l3.call_count, 1) self.send_arp.assert_called_once_with(ri, interface_name, '20.0.0.30') + kwargs = {'preserve_ips': ['192.168.1.34/32'], + 'namespace': 'qrouter-' + router_id} + self.mock_driver.init_l3.assert_called_with(interface_name, + ['20.0.0.30/24'], + **kwargs) elif action == 'remove': self.device_exists.return_value = True diff --git a/neutron/tests/unit/test_linux_interface.py b/neutron/tests/unit/test_linux_interface.py index d5576439e..8b8894ac9 100644 --- a/neutron/tests/unit/test_linux_interface.py +++ b/neutron/tests/unit/test_linux_interface.py @@ -93,6 +93,21 @@ class TestABCDriver(TestBase): mock.call().addr.add(4, '192.168.1.2/24', '192.168.1.255'), mock.call().addr.delete(4, '172.16.77.240/24')]) + def test_l3_init_with_preserve(self): + addresses = [dict(ip_version=4, scope='global', + dynamic=False, cidr='192.168.1.3/32')] + self.ip_dev().addr.list = mock.Mock(return_value=addresses) + + bc = BaseChild(self.conf) + ns = '12345678-1234-5678-90ab-ba0987654321' + bc.init_l3('tap0', ['192.168.1.2/24'], namespace=ns, + preserve_ips=['192.168.1.3/32']) + self.ip_dev.assert_has_calls( + [mock.call('tap0', 'sudo', namespace=ns), + mock.call().addr.list(scope='global', filters=['permanent']), + mock.call().addr.add(4, '192.168.1.2/24', '192.168.1.255')]) + self.assertFalse(self.ip_dev().addr.delete.called) + class TestOVSInterfaceDriver(TestBase): -- 2.45.2