]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Deletes floating ip related connection states
authorYong Sheng Gong <gongysh@unitedstack.com>
Mon, 30 Jun 2014 07:01:17 +0000 (15:01 +0800)
committerCarl Baldwin <carl.baldwin@hp.com>
Tue, 23 Sep 2014 21:48:06 +0000 (21:48 +0000)
When a floating ip is dissociated with a port, the current
connection with the floating ip is still working. This patch
will clear the connection state and cut off the connection
immediately.

Since conntrack -D will return 1, which is not an error code,
so add extra_ok_codes argument to execute methods.

Change-Id: Ia9bd7ae243a0859dcb97e2fa939f7d16f9c2456c
Closes-Bug: 1334926

etc/neutron/rootwrap.d/l3.filters
neutron/agent/l3_agent.py
neutron/agent/linux/interface.py
neutron/agent/linux/ip_lib.py
neutron/agent/linux/utils.py
neutron/tests/unit/test_l3_agent.py
neutron/tests/unit/test_linux_dhcp.py
neutron/tests/unit/test_linux_external_process.py
neutron/tests/unit/test_linux_ip_lib.py

index 65631698277b48722c74e79af22296a6d32ca763..9a3031822a58227a7cc79835732f25714bf7e251 100644 (file)
@@ -46,3 +46,6 @@ ip6tables-restore: CommandFilter, ip6tables-restore, root
 # Keepalived
 keepalived: CommandFilter, keepalived, root
 kill_keepalived: KillFilter, root, /usr/sbin/keepalived, -HUP, -15, -9
+
+# l3 agent to delete floatingip's conntrack state
+conntrack: CommandFilter, conntrack, root
index 84953bc2e227bcdd956748aade494e24b17afbae..1dd673f3a3e1e4ca778b3da271a866957da5cc63 100644 (file)
@@ -1115,6 +1115,9 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
         else:
             net = netaddr.IPNetwork(ip_cidr)
             device.addr.delete(net.version, ip_cidr)
+            self.driver.delete_conntrack_state(root_helper=self.root_helper,
+                                               namespace=ri.ns_name,
+                                               ip=ip_cidr)
             if ri.router['distributed']:
                 self.floating_ip_removed_dist(ri, ip_cidr)
 
index 77b8365c4b38cf9ee42d08930efa0e6ebf921272..7613db41053e8ee41bf0b24b7cf3ca3b3e236345 100644 (file)
@@ -25,6 +25,7 @@ from neutron.agent.linux import ovs_lib
 from neutron.agent.linux import utils
 from neutron.common import exceptions
 from neutron.extensions import flavor
+from neutron.openstack.common.gettextutils import _LE
 from neutron.openstack.common import importutils
 from neutron.openstack.common import log as logging
 
@@ -110,6 +111,9 @@ class LinuxInterfaceDriver(object):
         for ip_cidr, ip_version in previous.items():
             if ip_cidr not in preserve_ips:
                 device.addr.delete(ip_version, ip_cidr)
+                self.delete_conntrack_state(root_helper=self.root_helper,
+                                            namespace=namespace,
+                                            ip=ip_cidr)
 
         if gateway:
             device.route.add_gateway(gateway)
@@ -121,6 +125,43 @@ class LinuxInterfaceDriver(object):
         for route in existing_onlink_routes - new_onlink_routes:
             device.route.delete_onlink_route(route)
 
+    def delete_conntrack_state(self, root_helper, namespace, ip):
+        """Delete conntrack state associated with an IP address.
+
+        This terminates any active connections through an IP.  Call this soon
+        after removing the IP address from an interface so that new connections
+        cannot be created before the IP address is gone.
+
+        root_helper: root_helper to gain root access to call conntrack
+        namespace: the name of the namespace where the IP has been configured
+        ip: the IP address for which state should be removed.  This can be
+            passed as a string with or without /NN.  A netaddr.IPAddress or
+            netaddr.Network representing the IP address can also be passed.
+        """
+        ip_str = str(netaddr.IPNetwork(ip).ip)
+        ip_wrapper = ip_lib.IPWrapper(root_helper, namespace=namespace)
+
+        # Delete conntrack state for ingress traffic
+        # If 0 flow entries have been deleted
+        # conntrack -D will return 1
+        try:
+            ip_wrapper.netns.execute(["conntrack", "-D", "-d", ip_str],
+                                     check_exit_code=True,
+                                     extra_ok_codes=[1])
+
+        except RuntimeError:
+            LOG.exception(_LE("Failed deleting ingress connection state of"
+                              " floatingip %s"), ip_str)
+
+        # Delete conntrack state for egress traffic
+        try:
+            ip_wrapper.netns.execute(["conntrack", "-D", "-q", ip_str],
+                                     check_exit_code=True,
+                                     extra_ok_codes=[1])
+        except RuntimeError:
+            LOG.exception(_LE("Failed deleting egress connection state of"
+                              " floatingip %s"), ip_str)
+
     def check_bridge_exists(self, bridge):
         if not ip_lib.device_exists(bridge):
             raise exceptions.BridgeDoesNotExist(bridge=bridge)
index f085b4ded87ee837af839ca469983099c238d3dd..1d32a5497f87e4eec05356eeb6b8cd0f9ada9e32 100644 (file)
@@ -532,7 +532,8 @@ class IpNetnsCommand(IpCommandBase):
     def delete(self, name):
         self._as_root('delete', name, use_root_namespace=True)
 
-    def execute(self, cmds, addl_env={}, check_exit_code=True):
+    def execute(self, cmds, addl_env={}, check_exit_code=True,
+                extra_ok_codes=None):
         ns_params = []
         if self._parent.namespace:
             if not self._parent.root_helper:
@@ -546,7 +547,7 @@ class IpNetnsCommand(IpCommandBase):
         return utils.execute(
             ns_params + env_params + list(cmds),
             root_helper=self._parent.root_helper,
-            check_exit_code=check_exit_code)
+            check_exit_code=check_exit_code, extra_ok_codes=extra_ok_codes)
 
     def exists(self, name):
         output = self._parent._execute('o', 'netns', ['list'])
index e82a0d1496c34f18ad6e1abf510b5735bafe1a13..e0a66f843ca1a2b2fe0bab19dbe79508cbac2789 100644 (file)
@@ -58,7 +58,8 @@ def create_process(cmd, root_helper=None, addl_env=None):
 
 
 def execute(cmd, root_helper=None, process_input=None, addl_env=None,
-            check_exit_code=True, return_stderr=False, log_fail_as_error=True):
+            check_exit_code=True, return_stderr=False, log_fail_as_error=True,
+            extra_ok_codes=None):
     try:
         obj, cmd = create_process(cmd, root_helper=root_helper,
                                   addl_env=addl_env)
@@ -70,6 +71,10 @@ def execute(cmd, root_helper=None, process_input=None, addl_env=None,
               "Stderr: %(stderr)r") % {'cmd': cmd, 'code': obj.returncode,
                                        'stdout': _stdout, 'stderr': _stderr}
 
+        extra_ok_codes = extra_ok_codes or []
+        if obj.returncode and obj.returncode in extra_ok_codes:
+            obj.returncode = None
+
         if obj.returncode and log_fail_as_error:
             LOG.error(m)
         else:
index 0be506e7ce66bf27475ef854995b858e817a709e..8c0d0256f9f1d73a5536776db57c18c5e0122415 100644 (file)
@@ -1153,6 +1153,10 @@ vrrp_instance VR_1 {
             ri, {'id': _uuid()})
         self.assertEqual({}, fip_statuses)
         device.addr.delete.assert_called_once_with(4, '15.1.2.3/32')
+        self.mock_driver.delete_conntrack_state.assert_called_once_with(
+            root_helper=self.conf.root_helper,
+            namespace=ri.ns_name,
+            ip='15.1.2.3/32')
 
     def test_process_router_floating_ip_nat_rules_remove(self):
         ri = mock.MagicMock()
index f6f5fc6a23a13da81487ccb034a26d351ef8b036..b3090e1b57535d4e9529bfa31696f02f7969d636 100644 (file)
@@ -798,7 +798,8 @@ class TestDnsmasq(TestBase):
                 self.assertTrue(mocks['_output_opts_file'].called)
                 self.execute.assert_called_once_with(expected,
                                                      root_helper='sudo',
-                                                     check_exit_code=True)
+                                                     check_exit_code=True,
+                                                     extra_ok_codes=None)
 
     def test_spawn(self):
         self._test_spawn(['--conf-file=', '--domain=openstacklocal'])
index 85c24544a24ca27d7c1c233b95da742c8e439b4a..88f4bc075bcd12ef6de34e1765dd5382ea39398b 100644 (file)
@@ -41,7 +41,8 @@ class TestProcessManager(base.BaseTestCase):
                 name.assert_called_once_with(ensure_pids_dir=True)
                 self.execute.assert_called_once_with(['the', 'cmd'],
                                                      root_helper='sudo',
-                                                     check_exit_code=True)
+                                                     check_exit_code=True,
+                                                     extra_ok_codes=None)
 
     def test_enable_with_namespace(self):
         callback = mock.Mock()
index 6c20aa6d743f083d09e70c362a1ad9a5a18dc5f3..2b9486d3e755587dd7ecf691f679825b744c9815 100644 (file)
@@ -792,7 +792,7 @@ class TestIpNetnsCommand(TestIPCmdBase):
             execute.assert_called_once_with(
                 ['ip', 'netns', 'exec', 'ns',
                  'sysctl', '-w', 'net.ipv4.conf.all.promote_secondaries=1'],
-                root_helper='sudo', check_exit_code=True)
+                root_helper='sudo', check_exit_code=True, extra_ok_codes=None)
 
     def test_delete_namespace(self):
         with mock.patch('neutron.agent.linux.utils.execute'):
@@ -830,7 +830,8 @@ class TestIpNetnsCommand(TestIPCmdBase):
             execute.assert_called_once_with(['ip', 'netns', 'exec', 'ns', 'ip',
                                              'link', 'list'],
                                             root_helper='sudo',
-                                            check_exit_code=True)
+                                            check_exit_code=True,
+                                            extra_ok_codes=None)
 
     def test_execute_env_var_prepend(self):
         self.parent.namespace = 'ns'
@@ -841,7 +842,7 @@ class TestIpNetnsCommand(TestIPCmdBase):
                 ['ip', 'netns', 'exec', 'ns', 'env'] +
                 ['%s=%s' % (k, v) for k, v in env.items()] +
                 ['ip', 'link', 'list'],
-                root_helper='sudo', check_exit_code=True)
+                root_helper='sudo', check_exit_code=True, extra_ok_codes=None)
 
     def test_execute_nosudo_with_no_namespace(self):
         with mock.patch('neutron.agent.linux.utils.execute') as execute:
@@ -850,7 +851,8 @@ class TestIpNetnsCommand(TestIPCmdBase):
             self.netns_cmd.execute(['test'])
             execute.assert_called_once_with(['test'],
                                             root_helper=None,
-                                            check_exit_code=True)
+                                            check_exit_code=True,
+                                            extra_ok_codes=None)
 
 
 class TestDeviceExists(base.BaseTestCase):