Delete FIP namespace when last VM is deleted

On a compute node when the last VM with a floating IP association
is deleted, the L3 agent did not delete the fip namespace. However
the api server has already deleted the fip agent external gateway
port from the database.

This problem is happening on DVRs because the deletion of a VM port,
in addition to a floating IP disassociation, may also result in the
removal of the external gateway port binding AND the removal of the
fip agent external gateway port.

When the L3 agent is handling a routers_updated notification, it is
not processing floating ip address updates when the router has both
a floating ip disassociated and a external gateway port deleted.
This patch corrects this problem.

Closes-bug: #1377156
Change-Id: I86bdef7c9d988cb9d87c88adde55548d459f29a5
This commit is contained in:
Stephen Ma 2014-10-05 04:59:40 +00:00
parent 6e753dc436
commit e3b949c3bc
2 changed files with 69 additions and 18 deletions

View File

@ -1410,6 +1410,8 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
def external_gateway_removed(self, ri, ex_gw_port, interface_name):
if ri.router['distributed']:
self.process_router_floating_ip_nat_rules(ri)
self.process_router_floating_ip_addresses(ri, ex_gw_port)
for p in ri.internal_ports:
internal_interface = self.get_internal_device_name(p['id'])
self._snat_redirect_remove(ri, p, internal_interface)

View File

@ -44,6 +44,11 @@ FAKE_ID_2 = _uuid()
FIP_PRI = 32768
class FakeDev(object):
def __init__(self, name):
self.name = name
class TestExclusiveRouterProcessor(base.BaseTestCase):
def setUp(self):
super(TestExclusiveRouterProcessor, self).setUp()
@ -1034,8 +1039,11 @@ class TestBasicRouterOperations(base.BaseTestCase):
del router['gw_port']
agent.process_router(ri)
self.assertEqual(self.send_arp.call_count, 1)
self.assertFalse(agent.process_router_floating_ip_addresses.called)
self.assertFalse(agent.process_router_floating_ip_nat_rules.called)
distributed = ri.router.get('distributed', False)
self.assertEqual(agent.process_router_floating_ip_addresses.called,
distributed)
self.assertEqual(agent.process_router_floating_ip_nat_rules.called,
distributed)
def test_ha_router_keepalived_config(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
@ -1666,10 +1674,6 @@ vrrp_instance VR_1 {
matchers.LessThan(nat_rules.index(internal_net_rule)))
def test_process_router_delete_stale_internal_devices(self):
class FakeDev(object):
def __init__(self, name):
self.name = name
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
stale_devlist = [FakeDev('qr-a1b2c3d4-e5'),
FakeDev('qr-b2c3d4e5-f6')]
@ -1717,10 +1721,6 @@ vrrp_instance VR_1 {
self.mock_driver.unplug.assert_has_calls(calls, any_order=True)
def test_process_router_delete_stale_external_devices(self):
class FakeDev(object):
def __init__(self, name):
self.name = name
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
stale_devlist = [FakeDev('qg-a1b2c3d4-e5')]
stale_devnames = [dev.name for dev in stale_devlist]
@ -1766,10 +1766,6 @@ vrrp_instance VR_1 {
self.assertEqual(1, agent._queue.add.call_count)
def test_destroy_fip_namespace(self):
class FakeDev(object):
def __init__(self, name):
self.name = name
namespaces = ['qrouter-foo', 'qrouter-bar']
self.mock_ip.get_namespaces.return_value = namespaces
@ -1787,10 +1783,6 @@ vrrp_instance VR_1 {
self.mock_ip.del_veth.assert_called_once_with('fpr-aaaa')
def test_destroy_namespace(self):
class FakeDev(object):
def __init__(self, name):
self.name = name
namespace = 'qrouter-bar'
self.mock_ip.get_namespaces.return_value = [namespace]
@ -2281,6 +2273,63 @@ vrrp_instance VR_1 {
self.assertFalse(is_last)
self.assertEqual(len(agent.fip_ns_subscribers), 1)
def test_external_gateway_removed_ext_gw_port_and_fip(self):
self.conf.set_override('state_path', '/tmp')
self.conf.set_override('router_delete_namespaces', True)
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
agent.conf.agent_mode = 'dvr'
agent.agent_gateway_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
'subnet_id': _uuid()}],
'subnet': {'gateway_ip': '20.0.0.1'},
'id': _uuid(),
'network_id': _uuid(),
'mac_address': 'ca:fe:de:ad:be:ef',
'ip_cidr': '20.0.0.30/24'}
external_net_id = _uuid()
agent._fetch_external_net_id = mock.Mock(return_value=external_net_id)
router = prepare_router_data(num_internal_ports=2)
router['distributed'] = True
router['gw_port_host'] = HOSTNAME
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper, router)
vm_floating_ip = '19.4.4.2'
ri.floating_ips_dict[vm_floating_ip] = FIP_PRI
ri.dist_fip_count = 1
ri.ex_gw_port = ri.router['gw_port']
del ri.router['gw_port']
ri.rtr_fip_subnet = agent.local_subnets.allocate(ri.router_id)
_, fip_to_rtr = ri.rtr_fip_subnet.get_pair()
nat = ri.iptables_manager.ipv4['nat']
nat.clear_rules_by_tag = mock.Mock()
nat.add_rule = mock.Mock()
self.mock_ip.get_devices.return_value = [
FakeDev(agent.get_fip_ext_device_name(_uuid()))]
self.mock_ip_dev.addr.list.return_value = [
{'cidr': vm_floating_ip + '/32'},
{'cidr': '19.4.4.1/24'}]
self.device_exists.return_value = True
agent.external_gateway_removed(
ri, ri.ex_gw_port,
agent.get_external_device_name(ri.ex_gw_port['id']))
self.mock_ip.del_veth.assert_called_once_with(
agent.get_fip_int_device_name(ri.router['id']))
self.mock_ip_dev.route.delete_gateway.assert_called_once_with(
str(fip_to_rtr.ip), table=l3_agent.FIP_RT_TBL)
self.assertEqual(ri.dist_fip_count, 0)
self.assertEqual(len(agent.fip_ns_subscribers), 0)
self.assertEqual(self.mock_driver.unplug.call_count, 1)
self.assertIsNone(agent.agent_gateway_port)
self.mock_ip.netns.delete.assert_called_once_with(
agent.get_fip_ns_name(external_net_id))
self.assertFalse(nat.add_rule.called)
nat.clear_rules_by_tag.assert_called_once_with('floating_ip')
class TestL3AgentEventHandler(base.BaseTestCase):