From: Swaminathan Vasudevan Date: Mon, 22 Jun 2015 23:50:43 +0000 (-0700) Subject: Add RPC command and delete if last FIP on Agent X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=639f1893dde0d393a97b29ca5309dba716831a7f;p=openstack-build%2Fneutron-build.git Add RPC command and delete if last FIP on Agent Today FloatingIP Agent gateway port is deleted and re-created for DVR based routers based on floatingip association and disassociation with VMs on compute nodes by the plugin. This introduces lot more strain on the plugin to create and delete these ports when VMs come up and get deleted that are associated with FloatingIps. This patch will introduce an RPC call for the agent to initiate a agent gateway port delete. Also the agent will look for the last floatingip that it manages, and if condition satisfies, the agent will request the server to remove the FloatingIP Agent Gateway port. Change-Id: I47694b2ee60c363e2fe59ad5f7d168252da08a45 Related-Bug: #1468007 Related-Bug: #1408855 Related-Bug: #1450982 --- diff --git a/neutron/agent/l3/agent.py b/neutron/agent/l3/agent.py index 99921846c..1a00ea870 100644 --- a/neutron/agent/l3/agent.py +++ b/neutron/agent/l3/agent.py @@ -80,7 +80,8 @@ class L3PluginApi(object): to update_ha_routers_states 1.5 - Added update_ha_routers_states 1.6 - Added process_prefix_update - + 1.7 - DVR support: new L3 plugin methods added. + - delete_agent_gateway_port """ def __init__(self, topic, host): @@ -139,6 +140,12 @@ class L3PluginApi(object): return cctxt.call(context, 'process_prefix_update', subnets=prefix_update) + def delete_agent_gateway_port(self, context, fip_net): + """Delete Floatingip_agent_gateway_port.""" + cctxt = self.client.prepare(version='1.7') + return cctxt.call(context, 'delete_agent_gateway_port', + host=self.host, network_id=fip_net) + class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, ha.AgentMixin, diff --git a/neutron/agent/l3/dvr_local_router.py b/neutron/agent/l3/dvr_local_router.py index 357e0f331..c6eb528d9 100644 --- a/neutron/agent/l3/dvr_local_router.py +++ b/neutron/agent/l3/dvr_local_router.py @@ -137,6 +137,17 @@ class DvrLocalRouter(dvr_router_base.DvrRouterBase): # destroying it. The two could end up conflicting on # creating/destroying interfaces and such. I think I'd like a # semaphore to sync creation/deletion of this namespace. + + # NOTE (Swami): Since we are deleting the namespace here we + # should be able to delete the floatingip agent gateway port + # for the provided external net since we don't need it anymore. + if self.fip_ns.agent_gateway_port: + LOG.debug('Removed last floatingip, so requesting the ' + 'server to delete Floatingip Agent Gateway port:' + '%s', self.fip_ns.agent_gateway_port) + self.agent.plugin_rpc.delete_agent_gateway_port( + self.agent.context, + self.fip_ns.agent_gateway_port['network_id']) self.fip_ns.delete() self.fip_ns = None diff --git a/neutron/api/rpc/handlers/l3_rpc.py b/neutron/api/rpc/handlers/l3_rpc.py index 6c2ca53f2..47157e5b9 100644 --- a/neutron/api/rpc/handlers/l3_rpc.py +++ b/neutron/api/rpc/handlers/l3_rpc.py @@ -45,7 +45,8 @@ class L3RpcCallback(object): # since it was unused. The RPC version was not changed # 1.5 Added update_ha_routers_states # 1.6 Added process_prefix_update to support IPv6 Prefix Delegation - target = oslo_messaging.Target(version='1.6') + # 1.7 Added method delete_agent_gateway_port for DVR Routers + target = oslo_messaging.Target(version='1.7') @property def plugin(self): @@ -281,3 +282,11 @@ class L3RpcCallback(object): subnet_id, {'subnet': {'cidr': prefix}})) return updated_subnets + + def delete_agent_gateway_port(self, context, **kwargs): + """Delete Floatingip agent gateway port.""" + network_id = kwargs.get('network_id') + host = kwargs.get('host') + admin_ctx = neutron_context.get_admin_context() + self.l3plugin.delete_floatingip_agent_gateway_port( + admin_ctx, network_id, host_id=host) diff --git a/neutron/db/l3_dvr_db.py b/neutron/db/l3_dvr_db.py index 123e9a256..e1619b245 100644 --- a/neutron/db/l3_dvr_db.py +++ b/neutron/db/l3_dvr_db.py @@ -170,7 +170,7 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin, ext_net_gw_ports = self._core_plugin.get_ports( context.elevated(), filters) if not ext_net_gw_ports: - self._delete_floatingip_agent_gateway_port( + self.delete_floatingip_agent_gateway_port( context.elevated(), None, gw_ext_net_id) def _create_gw_port(self, context, router_id, router, new_network, @@ -265,7 +265,7 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin, context, fip_hostid, floatingip_db['floating_network_id']): LOG.debug('Deleting the Agent GW Port for ext-net: ' '%s', floatingip_db['floating_network_id']) - self._delete_floatingip_agent_gateway_port( + self.delete_floatingip_agent_gateway_port( context, fip_hostid, floatingip_db['floating_network_id']) def delete_floatingip(self, context, id): @@ -553,7 +553,7 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin, return True return False - def _delete_floatingip_agent_gateway_port( + def delete_floatingip_agent_gateway_port( self, context, host_id, ext_net_id): """Function to delete FIP gateway port with given ext_net_id.""" # delete any fip agent gw port diff --git a/neutron/tests/functional/agent/test_l3_agent.py b/neutron/tests/functional/agent/test_l3_agent.py index d2ef8ae4c..ffa20e6dd 100644 --- a/neutron/tests/functional/agent/test_l3_agent.py +++ b/neutron/tests/functional/agent/test_l3_agent.py @@ -1460,3 +1460,27 @@ class TestDvrRouter(L3AgentTestFramework): router_updated, internal_dev_name=internal_device_name) self.assertFalse(sg_device) self.assertTrue(qg_device) + + def test_dvr_router_calls_delete_agent_gateway_if_last_fip(self): + """Test to validate delete fip if it is last fip managed by agent.""" + self.agent.conf.agent_mode = 'dvr_snat' + router_info = self.generate_dvr_router_info(enable_snat=True) + router1 = self.manage_router(self.agent, router_info) + floating_agent_gw_port = ( + router1.router[l3_constants.FLOATINGIP_AGENT_INTF_KEY]) + self.assertTrue(floating_agent_gw_port) + fip_ns = router1.fip_ns.get_name() + router1.fip_ns.agent_gw_port = floating_agent_gw_port + self.assertTrue(self._namespace_exists(router1.ns_name)) + self.assertTrue(self._namespace_exists(fip_ns)) + self._assert_dvr_floating_ips(router1) + self._assert_dvr_snat_gateway(router1) + router1.router[l3_constants.FLOATINGIP_KEY] = [] + rpc_mock = mock.patch.object( + self.agent.plugin_rpc, 'delete_agent_gateway_port').start() + self.agent._process_updated_router(router1.router) + self.assertTrue(rpc_mock.called) + rpc_mock.assert_called_once_with( + self.agent.context, + floating_agent_gw_port[0]['network_id']) + self.assertFalse(self._namespace_exists(fip_ns)) diff --git a/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py b/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py index 6d67c46ca..6dbcb59cc 100644 --- a/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py +++ b/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py @@ -142,7 +142,7 @@ class L3DvrTestCase(ml2_test_base.ML2TestFramework): network_id, port = ( self.setup_create_agent_gw_port_for_network()) - self.l3_plugin._delete_floatingip_agent_gateway_port( + self.l3_plugin.delete_floatingip_agent_gateway_port( self.context, "", network_id) self.assertIsNone( self.l3_plugin._get_agent_gw_ports_exist_for_network( diff --git a/neutron/tests/unit/agent/l3/test_dvr_local_router.py b/neutron/tests/unit/agent/l3/test_dvr_local_router.py index 052ac68bf..06175302b 100644 --- a/neutron/tests/unit/agent/l3/test_dvr_local_router.py +++ b/neutron/tests/unit/agent/l3/test_dvr_local_router.py @@ -145,27 +145,27 @@ class TestDvrRouterOperations(base.BaseTestCase): 'interface_driver': self.mock_driver} def _create_router(self, router=None, **kwargs): - agent_conf = mock.Mock() + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.router_id = _uuid() if not router: router = mock.MagicMock() - return dvr_router.DvrLocalRouter(mock.sentinel.agent, - mock.sentinel.myhost, + return dvr_router.DvrLocalRouter(agent, + HOSTNAME, self.router_id, router, - agent_conf, - mock.sentinel.interface_driver, + self.conf, + mock.Mock(), **kwargs) def test_get_floating_ips_dvr(self): router = mock.MagicMock() - router.get.return_value = [{'host': mock.sentinel.myhost}, + router.get.return_value = [{'host': HOSTNAME}, {'host': mock.sentinel.otherhost}] ri = self._create_router(router) fips = ri.get_floating_ips() - self.assertEqual([{'host': mock.sentinel.myhost}], fips) + self.assertEqual([{'host': HOSTNAME}], fips) @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') @mock.patch.object(ip_lib, 'IPDevice') @@ -242,15 +242,16 @@ class TestDvrRouterOperations(base.BaseTestCase): ri.rtr_fip_subnet = lla.LinkLocalAddressPair('15.1.2.3/32') _, fip_to_rtr = ri.rtr_fip_subnet.get_pair() fip_ns = ri.fip_ns - - ri.floating_ip_removed_dist(fip_cidr) - - self.assertTrue(fip_ns.destroyed) - mIPWrapper().del_veth.assert_called_once_with( - fip_ns.get_int_device_name(router['id'])) - mIPDevice().route.delete_gateway.assert_called_once_with( - str(fip_to_rtr.ip), table=16) - fip_ns.unsubscribe.assert_called_once_with(ri.router_id) + with mock.patch.object(self.plugin_api, + 'delete_agent_gateway_port') as del_fip_gw: + ri.floating_ip_removed_dist(fip_cidr) + self.assertTrue(del_fip_gw.called) + self.assertTrue(fip_ns.destroyed) + mIPWrapper().del_veth.assert_called_once_with( + fip_ns.get_int_device_name(router['id'])) + mIPDevice().route.delete_gateway.assert_called_once_with( + str(fip_to_rtr.ip), table=16) + fip_ns.unsubscribe.assert_called_once_with(ri.router_id) def _test_add_floating_ip(self, ri, fip, is_failure): ri._add_fip_addr_to_device = mock.Mock(return_value=is_failure) diff --git a/neutron/tests/unit/db/test_l3_dvr_db.py b/neutron/tests/unit/db/test_l3_dvr_db.py index 2a5e2a464..0dc5330a4 100644 --- a/neutron/tests/unit/db/test_l3_dvr_db.py +++ b/neutron/tests/unit/db/test_l3_dvr_db.py @@ -198,7 +198,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): self.ctx, port['id']) - def test_prevent__delete_floatingip_agent_gateway_port(self): + def test_prevent_delete_floatingip_agent_gateway_port(self): port = { 'id': 'my_port_id', 'fixed_ips': mock.ANY, @@ -253,7 +253,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): '_check_fips_availability_on_host_ext_net') as cfips,\ mock.patch.object( self.mixin, - '_delete_floatingip_agent_gateway_port') as dfips: + 'delete_floatingip_agent_gateway_port') as dfips: gfips.return_value = floatingip gvm.return_value = 'my-host' cfips.return_value = True @@ -305,7 +305,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): plugin = mock.Mock() gp.return_value = plugin plugin.get_ports.return_value = ports - self.mixin._delete_floatingip_agent_gateway_port( + self.mixin.delete_floatingip_agent_gateway_port( self.ctx, port_host, 'ext_network_id') plugin.get_ports.assert_called_with(self.ctx, filters={ 'network_id': ['ext_network_id'], @@ -317,10 +317,10 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): plugin.ipam.delete_port.assert_called_with( self.ctx, 'my_new_port_id') - def test__delete_floatingip_agent_gateway_port_without_host_id(self): + def test_delete_floatingip_agent_gateway_port_without_host_id(self): self._helper_delete_floatingip_agent_gateway_port(None) - def test__delete_floatingip_agent_gateway_port_with_host_id(self): + def test_delete_floatingip_agent_gateway_port_with_host_id(self): self._helper_delete_floatingip_agent_gateway_port( 'foo_host') @@ -346,7 +346,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): 'delete_csnat_router_interface_ports') as del_csnat_port,\ mock.patch.object( self.mixin, - '_delete_floatingip_agent_gateway_port') as del_agent_gw_port: + 'delete_floatingip_agent_gateway_port') as del_agent_gw_port: plugin = mock.Mock() gp.return_value = plugin plugin.get_ports.return_value = port