From e3b949c3bc08808e3df15215bc30d6610f3a4bd3 Mon Sep 17 00:00:00 2001 From: Stephen Ma Date: Sun, 5 Oct 2014 04:59:40 +0000 Subject: [PATCH] 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 --- neutron/agent/l3_agent.py | 2 + neutron/tests/unit/test_l3_agent.py | 85 +++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/neutron/agent/l3_agent.py b/neutron/agent/l3_agent.py index 3120f3c3c..6a032021b 100644 --- a/neutron/agent/l3_agent.py +++ b/neutron/agent/l3_agent.py @@ -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) diff --git a/neutron/tests/unit/test_l3_agent.py b/neutron/tests/unit/test_l3_agent.py index b4e63d604..732352a35 100644 --- a/neutron/tests/unit/test_l3_agent.py +++ b/neutron/tests/unit/test_l3_agent.py @@ -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): -- 2.45.2