]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add RPC command and delete if last FIP on Agent
authorSwaminathan Vasudevan <swaminathan.vasudevan@hp.com>
Mon, 22 Jun 2015 23:50:43 +0000 (16:50 -0700)
committerSwaminathan Vasudevan <swaminathan.vasudevan@hp.com>
Tue, 8 Sep 2015 18:52:11 +0000 (11:52 -0700)
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

neutron/agent/l3/agent.py
neutron/agent/l3/dvr_local_router.py
neutron/api/rpc/handlers/l3_rpc.py
neutron/db/l3_dvr_db.py
neutron/tests/functional/agent/test_l3_agent.py
neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py
neutron/tests/unit/agent/l3/test_dvr_local_router.py
neutron/tests/unit/db/test_l3_dvr_db.py

index 99921846c3e06680d7787f73302f878e3346cae6..1a00ea8701be95da2442747d5653a490ab4805f1 100644 (file)
@@ -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,
index 357e0f331d832618bbdf70a946c5b01b815a1207..c6eb528d9f12444e56fe8f25e31d3927a4a9b270 100644 (file)
@@ -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
 
index 6c2ca53f2ab6a713c5e1134184faba81a749fdd7..47157e5b961c916acc5c97ea5bcd75e7dcd10bce 100644 (file)
@@ -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)
index 123e9a256364a5d69b6f36e51ddc54b0cf81a8ae..e1619b2456621df7e15e7ccc1a9f4acc6cf7a314 100644 (file)
@@ -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
index d2ef8ae4cada5b90dd217c7aa437ba473972bdcd..ffa20e6dda76f29653f3ae3ca2d3dacd1feb72ad 100644 (file)
@@ -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))
index 6d67c46ca9da4e4704da90f33f9fe2e224f8ccb0..6dbcb59ccef8a26254ec85a9fd932425831d6026 100644 (file)
@@ -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(
index 052ac68bf2eafd296263e480853dc6fe04890934..06175302b591a378451f1b187cb3b355ef153630 100644 (file)
@@ -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)
index 2a5e2a464f9de60f68748a324efb76596747b809..0dc5330a45d5d5d521a150cd72a8b88299e870cd 100644 (file)
@@ -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