]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Nicira NVP plugin support for l3_ext_gw_mode extension
authorSalvatore <salv.orlando@gmail.com>
Thu, 4 Apr 2013 10:35:50 +0000 (12:35 +0200)
committerSalvatore Orlando <salv.orlando@gmail.com>
Mon, 15 Jul 2013 20:42:42 +0000 (22:42 +0200)
Bug 1121129

This patch adds support the 'configurable external gateway' extension
in the NVP plugin.

Change-Id: I531ebe0053b1b9e21d6f0685776acebe3173b170

neutron/db/l3_gwmode_db.py
neutron/db/migration/alembic_migrations/versions/128e042a2b68_ext_gw_mode.py
neutron/plugins/nicira/NeutronPlugin.py
neutron/tests/unit/nicira/test_nicira_plugin.py
neutron/tests/unit/test_extension_ext_gw_mode.py

index 74c0cf36ec41f749e38fc4ec90951ab59e40d2e9..c2b49b4c719494029ad6380da4f2a4371a595471 100644 (file)
@@ -43,8 +43,10 @@ class L3_NAT_db_mixin(l3_db.L3_NAT_db_mixin):
                                      'enable_snat': router.enable_snat}
         return self._fields(res, fields)
 
-    def _update_router_gw_info(self, context, router_id, info):
-        router = self._get_router(context, router_id)
+    def _update_router_gw_info(self, context, router_id, info, router=None):
+        # Load the router only if necessary
+        if not router:
+            router = self._get_router(context, router_id)
         # if enable_snat is not specified use the value
         # stored in the database (default:True)
         enable_snat = not info or info.get('enable_snat', router.enable_snat)
@@ -54,6 +56,9 @@ class L3_NAT_db_mixin(l3_db.L3_NAT_db_mixin):
         # Calls superclass, pass router db object for avoiding re-loading
         super(L3_NAT_db_mixin, self)._update_router_gw_info(
             context, router_id, info, router=router)
+        # Returning the router might come back useful if this
+        # method is overriden in child classes
+        return router
 
     def _build_routers_list(self, routers, gw_ports):
         gw_port_id_gw_port_dict = {}
index 20d08ae14af446fdb8a9b00ff56dceaad1cc95f5..8770b1e245e67d4609103e93d2deb5f763b278d5 100644 (file)
@@ -34,6 +34,7 @@ migration_for_plugins = [
     'neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2',
     'neutron.plugins.metaplugin.meta_neutron_plugin.MetaPluginV2',
     'neutron.plugins.nec.nec_plugin.NECPluginV2',
+    'neutron.plugins.nicira.NeutronPlugin.NvpPluginV2',
     'neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2',
     'neutron.plugins.ryu.ryu_neutron_plugin.RyuNeutronPluginV2'
 ]
index 557521e0257fe7df2a6e1c3c7a49d20e63033c3e..cf718fcac7d2fda778b85ce90644117c1a69aa51 100644 (file)
@@ -44,6 +44,7 @@ from neutron.db import db_base_plugin_v2
 from neutron.db import dhcp_rpc_base
 from neutron.db import extraroute_db
 from neutron.db import l3_db
+from neutron.db import l3_gwmode_db
 from neutron.db import models_v2
 from neutron.db import portsecurity_db
 from neutron.db import quota_db  # noqa
@@ -73,6 +74,7 @@ from neutron.plugins.nicira import nvplib
 
 
 LOG = logging.getLogger("NeutronPlugin")
+
 NVP_NOSNAT_RULES_ORDER = 10
 NVP_FLOATINGIP_NAT_RULES_ORDER = 224
 NVP_EXTGW_NAT_RULES_ORDER = 255
@@ -127,6 +129,7 @@ class NVPRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin):
 
 class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                   extraroute_db.ExtraRoute_db_mixin,
+                  l3_gwmode_db.L3_NAT_db_mixin,
                   portsecurity_db.PortSecurityDbMixin,
                   securitygroups_db.SecurityGroupDbMixin,
                   mac_db.MacLearningDbMixin,
@@ -141,7 +144,8 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
     functionality using NVP.
     """
 
-    supported_extension_aliases = ["extraroute",
+    supported_extension_aliases = ["ext_gw_mode",
+                                   "extraroute",
                                    "mac-learning",
                                    "network-gateway",
                                    "nvp-qos",
@@ -278,6 +282,58 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                                             attachment_vlan)
         return lrouter_port
 
+    def _update_router_gw_info(self, context, router_id, info):
+        # NOTE(salvatore-orlando): We need to worry about rollback of NVP
+        # configuration in case of failures in the process
+        # Ref. LP bug 1102301
+        router = self._get_router(context, router_id)
+        # Check whether SNAT rule update should be triggered
+        # NVP also supports multiple external networks so there is also
+        # the possibility that NAT rules should be replaced
+        current_ext_net_id = router.gw_port_id and router.gw_port.network_id
+        new_ext_net_id = info and info.get('network_id')
+        # SNAT should be enabled unless info['enable_snat'] is
+        # explicitly set to false
+        enable_snat = new_ext_net_id and info.get('enable_snat', True)
+        # Remove if ext net removed, changed, or if snat disabled
+        remove_snat_rules = (current_ext_net_id and
+                             new_ext_net_id != current_ext_net_id or
+                             router.enable_snat and not enable_snat)
+        # Add rules if snat is enabled, and if either the external network
+        # changed or snat was previously disabled
+        # NOTE: enable_snat == True implies new_ext_net_id != None
+        add_snat_rules = (enable_snat and
+                          (new_ext_net_id != current_ext_net_id or
+                           not router.enable_snat))
+        router = super(NvpPluginV2, self)._update_router_gw_info(
+            context, router_id, info, router=router)
+        # Add/Remove SNAT rules as needed
+        # Create an elevated context for dealing with metadata access
+        # cidrs which are created within admin context
+        ctx_elevated = context.elevated()
+        if remove_snat_rules or add_snat_rules:
+            cidrs = self._find_router_subnets_cidrs(ctx_elevated, router_id)
+        if remove_snat_rules:
+            # Be safe and concede NAT rules might not exist.
+            # Therefore use min_num_expected=0
+            for cidr in cidrs:
+                nvplib.delete_nat_rules_by_match(
+                    self.cluster, router_id, "SourceNatRule",
+                    max_num_expected=1, min_num_expected=0,
+                    source_ip_addresses=cidr)
+        if add_snat_rules:
+            ip_addresses = self._build_ip_address_list(
+                ctx_elevated, router.gw_port['fixed_ips'])
+            # Set the SNAT rule for each subnet (only first IP)
+            for cidr in cidrs:
+                cidr_prefix = int(cidr.split('/')[1])
+                nvplib.create_lrouter_snat_rule(
+                    self.cluster, router_id,
+                    ip_addresses[0].split('/')[0],
+                    ip_addresses[0].split('/')[0],
+                    order=NVP_EXTGW_NAT_RULES_ORDER - cidr_prefix,
+                    match_criteria={'source_ip_addresses': cidr})
+
     def _update_router_port_attachment(self, cluster, context,
                                        router_id, port_data,
                                        nvp_router_port_id,
@@ -526,15 +582,6 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                 "L3GatewayAttachment",
                 ext_network[pnet.PHYSICAL_NETWORK],
                 ext_network[pnet.SEGMENTATION_ID])
-        # Set the SNAT rule for each subnet (only first IP)
-        for cidr in self._find_router_subnets_cidrs(context, router_id):
-            cidr_prefix = int(cidr.split('/')[1])
-            nvplib.create_lrouter_snat_rule(
-                self.cluster, router_id,
-                ip_addresses[0].split('/')[0],
-                ip_addresses[0].split('/')[0],
-                order=NVP_EXTGW_NAT_RULES_ORDER - cidr_prefix,
-                match_criteria={'source_ip_addresses': cidr})
 
         LOG.debug(_("_nvp_create_ext_gw_port completed on external network "
                     "%(ext_net_id)s, attached to router:%(router_id)s. "
@@ -559,13 +606,6 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                                        port_data['name'],
                                        True,
                                        ['0.0.0.0/31'])
-            # Delete the SNAT rule for each subnet, keep in mind
-            # that the rule might have already been removed from NVP
-            for cidr in self._find_router_subnets_cidrs(context, router_id):
-                nvplib.delete_nat_rules_by_match(
-                    self.cluster, router_id, "SourceNatRule",
-                    max_num_expected=1, min_num_expected=0,
-                    source_ip_addresses=cidr)
             # Reset attachment
             self._update_router_port_attachment(
                 self.cluster, context, router_id, port_data,
@@ -1654,7 +1694,7 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         # Fetch router from DB
         router = self._get_router(context, router_id)
         gw_port = router.gw_port
-        if gw_port:
+        if gw_port and router.enable_snat:
             # There is a change gw_port might have multiple IPs
             # In that case we will consider only the first one
             if gw_port.get('fixed_ips'):
@@ -1869,7 +1909,7 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                         match_criteria={'destination_ip_addresses':
                                         floating_ip})
                     # setup snat rule such that src ip of a IP packet when
-                    #  using floating is the floating ip itself.
+                    # using floating is the floating ip itself.
                     nvplib.create_lrouter_snat_rule(
                         self.cluster, router_id, floating_ip, floating_ip,
                         order=NVP_FLOATINGIP_NAT_RULES_ORDER,
index f95da9e153ac8239716586aa1d8be7bd958daa1b..bf7c6cc9254e286babd390434d91a0e56093d89a 100644 (file)
@@ -39,6 +39,7 @@ from neutron.plugins.nicira import nvplib
 from neutron.tests.unit.nicira import fake_nvpapiclient
 import neutron.tests.unit.nicira.test_networkgw as test_l2_gw
 import neutron.tests.unit.test_db_plugin as test_plugin
+import neutron.tests.unit.test_extension_ext_gw_mode as test_ext_gw_mode
 import neutron.tests.unit.test_extension_portsecurity as psec
 import neutron.tests.unit.test_extension_security_group as ext_sg
 from neutron.tests.unit import test_extensions
@@ -830,6 +831,11 @@ class TestNiciraQoSQueue(NiciraPluginV2TestCase):
             self.assertEqual(queue['qos_queue']['max'], 20)
 
 
+class NiciraExtGwModeTestCase(test_ext_gw_mode.ExtGwModeTestCase,
+                              NiciraPluginV2TestCase):
+    pass
+
+
 class NiciraNeutronNVPOutOfSync(test_l3_plugin.L3NatTestCaseBase,
                                 NiciraPluginV2TestCase):
 
index ac8ac0ccb7ec95d3e6c94d41581c2d585ab9f91d..5da43518f97a6b8e19d18a7a65aa885d0b12d23e 100644 (file)
@@ -302,7 +302,7 @@ class ExtGwModeTestCase(test_db_plugin.NeutronDbPluginV2TestCase,
                         test_l3_plugin.L3NatTestCaseMixin):
 
     def setUp(self):
-        # Store l3 resource attribute map as it's will be updated
+        # Store l3 resource attribute map as it will be updated
         self._l3_attribute_map_bk = {}
         for item in l3.RESOURCE_ATTRIBUTE_MAP:
             self._l3_attribute_map_bk[item] = (