Deletes floating ip related connection states
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
This commit is contained in:
parent
9d5bd529cd
commit
9666455383
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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'])
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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'])
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user