]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Enable multiple L3 GW services on NVP plugin
authorSalvatore Orlando <salv.orlando@gmail.com>
Mon, 25 Feb 2013 14:41:06 +0000 (15:41 +0100)
committerSalvatore Orlando <salv.orlando@gmail.com>
Wed, 27 Feb 2013 02:06:21 +0000 (03:06 +0100)
Bug 1130211

This patch allows for using multiple layer-3 gateways leveraging the
provider networks extension.

Change-Id: I293920c2565f4670e9be9b22dc1b60431b62f7e7

quantum/db/migration/alembic_migrations/versions/1341ed32cc1e_nvp_netbinding_update.py [new file with mode: 0644]
quantum/plugins/nicira/nicira_nvp_plugin/QuantumPlugin.py
quantum/plugins/nicira/nicira_nvp_plugin/nicira_db.py
quantum/plugins/nicira/nicira_nvp_plugin/nicira_models.py
quantum/plugins/nicira/nicira_nvp_plugin/nvplib.py
quantum/tests/unit/nicira/etc/fake_get_lrouter_lport_att.json
quantum/tests/unit/nicira/fake_nvpapiclient.py
quantum/tests/unit/nicira/test_nicira_plugin.py
quantum/tests/unit/test_l3_plugin.py

diff --git a/quantum/db/migration/alembic_migrations/versions/1341ed32cc1e_nvp_netbinding_update.py b/quantum/db/migration/alembic_migrations/versions/1341ed32cc1e_nvp_netbinding_update.py
new file mode 100644 (file)
index 0000000..c1f83a3
--- /dev/null
@@ -0,0 +1,66 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 OpenStack LLC
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+
+"""nvp_net_binding
+
+Revision ID: 1341ed32cc1e
+Revises: 3b54bf9e29f7
+Create Date: 2013-02-26 01:28:29.182195
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '1341ed32cc1e'
+down_revision = '3b54bf9e29f7'
+
+# Change to ['*'] if this migration applies to all plugins
+
+migration_for_plugins = [
+    'quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2'
+]
+
+from alembic import op
+import sqlalchemy as sa
+
+
+from quantum.db import migration
+
+
+def upgrade(active_plugin=None, options=None):
+    if not migration.should_run(active_plugin, migration_for_plugins):
+        return
+    op.alter_column('nvp_network_bindings', 'tz_uuid',
+                    name='phy_uuid',
+                    existing_type=sa.String(36),
+                    existing_nullable=True)
+    op.alter_column('nvp_network_bindings', 'binding_type',
+                    type_=sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
+                                  name='nvp_network_bindings_binding_type'),
+                    existing_nullable=True)
+
+
+def downgrade(active_plugin=None, options=None):
+    if not migration.should_run(active_plugin, migration_for_plugins):
+        return
+    op.alter_column('nvp_network_bindings', 'phy_uuid',
+                    name='tz_uuid',
+                    existing_type=sa.String(36),
+                    existing_nullable=True)
+    op.alter_column('nvp_network_bindings', 'binding_type',
+                    type_=sa.Enum('flat', 'vlan', 'stt', 'gre',
+                                  name='nvp_network_bindings_binding_type'),
+                    existing_nullable=True)
index 3ec2ddda8e6d3adf05ab69f84a4ed95a99dcab41..218f531e67af915c7a9fa9d48652ba90008a413b 100644 (file)
@@ -78,6 +78,7 @@ NVP_EXTGW_NAT_RULES_ORDER = 255
 # Provider network extension - allowed network types for the NVP Plugin
 class NetworkTypes:
     """ Allowed provider network types for the NVP Plugin """
+    L3_EXT = 'l3_ext'
     STT = 'stt'
     GRE = 'gre'
     FLAT = 'flat'
@@ -330,6 +331,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     def _create_and_attach_router_port(self, cluster, context,
                                        router_id, port_data,
                                        attachment_type, attachment,
+                                       attachment_vlan=None,
                                        subnet_ids=None):
         # Use a fake IP address if gateway port is not 'real'
         ip_addresses = (port_data.get('fake_ext_gw') and
@@ -352,33 +354,43 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                                                "%(router_id)s"),
                                              {'port_id': port_data.get('id'),
                                               'router_id': router_id})
+        self._update_router_port_attachment(cluster, context, router_id,
+                                            port_data, attachment_type,
+                                            attachment, attachment_vlan,
+                                            lrouter_port['uuid'])
+        return lrouter_port
+
+    def _update_router_port_attachment(self, cluster, context,
+                                       router_id, port_data,
+                                       attachment_type, attachment,
+                                       attachment_vlan=None,
+                                       nvp_router_port_id=None):
+        if not nvp_router_port_id:
+            nvp_router_port_id = self._find_router_gw_port(context, port_data)
         try:
-            # Add a L3 gateway attachment
-            # TODO(Salvatore-Orlando): Allow per router specification of
-            # l3 gw service uuid as well as per-tenant specification
             nvplib.plug_router_port_attachment(cluster, router_id,
-                                               lrouter_port['uuid'],
+                                               nvp_router_port_id,
                                                attachment,
-                                               attachment_type)
+                                               attachment_type,
+                                               attachment_vlan)
             LOG.debug(_("Attached %(att)s to NVP router port %(port)s"),
-                      {'att': attachment, 'port': lrouter_port['uuid']})
+                      {'att': attachment, 'port': nvp_router_port_id})
         except NvpApiClient.NvpApiException:
             # Must remove NVP logical port
             nvplib.delete_router_lport(cluster, router_id,
-                                       lrouter_port['uuid'])
+                                       nvp_router_port_id)
             LOG.exception(_("Unable to plug attachment in NVP logical "
                             "router port %(r_port_id)s, associated with "
                             "Quantum %(q_port_id)s"),
-                          {'r_port_id': lrouter_port['uuid'],
+                          {'r_port_id': nvp_router_port_id,
                            'q_port_id': port_data.get('id')})
             raise nvp_exc.NvpPluginException(
                 err_msg=(_("Unable to plug attachment in router port "
                            "%(r_port_id)s for quantum port id %(q_port_id)s "
                            "on router %(router_id)s") %
-                         {'r_port_id': lrouter_port['uuid'],
+                         {'r_port_id': nvp_router_port_id,
                           'q_port_id': port_data.get('id'),
                           'router_id': router_id}))
-        return lrouter_port
 
     def _get_port_by_device_id(self, context, device_id, device_owner):
         """ Retrieve ports associated with a specific device id.
@@ -596,6 +608,15 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                                    port_data['name'],
                                    True,
                                    ip_addresses)
+        ext_network = self.get_network(context, port_data['network_id'])
+        if ext_network.get(pnet.NETWORK_TYPE) == NetworkTypes.L3_EXT:
+            # Update attachment
+            self._update_router_port_attachment(
+                cluster, context, router_id, port_data,
+                "L3GatewayAttachment",
+                ext_network[pnet.PHYSICAL_NETWORK],
+                ext_network[pnet.SEGMENTATION_ID],
+                lr_port['uuid'])
         # Set the SNAT rule for each subnet (only first IP)
         for cidr in self._find_router_subnets_cidrs(context, router_id):
             nvplib.create_lrouter_snat_rule(
@@ -635,6 +656,12 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                     cluster, router_id, "SourceNatRule",
                     max_num_expected=1, min_num_expected=1,
                     source_ip_addresses=cidr)
+            # Reset attachment
+            self._update_router_port_attachment(
+                cluster, context, router_id, port_data,
+                "L3GatewayAttachment",
+                self.default_cluster.default_l3_gw_service_uuid,
+                nvp_router_port_id=lr_port['uuid'])
 
         except NvpApiClient.ResourceNotFound:
             raise nvp_exc.NvpPluginException(
@@ -780,6 +807,10 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 if binding:
                     raise q_exc.VlanIdInUse(vlan_id=segmentation_id,
                                             physical_network=physical_network)
+        elif network_type == NetworkTypes.L3_EXT:
+            if (segmentation_id_set and
+                (segmentation_id < 1 or segmentation_id > 4094)):
+                err_msg = _("%s out of range (1 to 4094)") % segmentation_id
         else:
             err_msg = _("%(net_type_param)s %(net_type_value)s not "
                         "supported") % {'net_type_param': pnet.NETWORK_TYPE,
@@ -796,10 +827,10 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                                                         network['id'])
             # With NVP plugin 'normal' overlay networks will have no binding
             # TODO(salvatore-orlando) make sure users can specify a distinct
-            # tz_uuid as 'provider network' for STT net type
+            # phy_uuid as 'provider network' for STT net type
             if binding:
                 network[pnet.NETWORK_TYPE] = binding.binding_type
-                network[pnet.PHYSICAL_NETWORK] = binding.tz_uuid
+                network[pnet.PHYSICAL_NETWORK] = binding.phy_uuid
                 network[pnet.SEGMENTATION_ID] = binding.vlan_id
 
     def _handle_lswitch_selection(self, cluster, network,
@@ -829,7 +860,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 cluster, network.tenant_id,
                 "%s-ext-%s" % (network.name, len(lswitches)),
                 network_binding.binding_type,
-                network_binding.tz_uuid,
+                network_binding.phy_uuid,
                 network_binding.vlan_id,
                 network.id)
             return selected_lswitch
@@ -907,7 +938,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                     context.session, new_net['id'],
                     net_data.get(pnet.NETWORK_TYPE),
                     net_data.get(pnet.PHYSICAL_NETWORK),
-                    net_data.get(pnet.SEGMENTATION_ID))
+                    net_data.get(pnet.SEGMENTATION_ID, 0))
                 self._extend_network_dict_provider(context, new_net,
                                                    net_binding)
             self._extend_network_port_security_dict(context, new_net)
index 821a24bee4252da664ccab16e4a53363dcae5131..a40e0d7c45ec943278c01011d95aa3066fc94656 100644 (file)
@@ -47,10 +47,22 @@ def get_network_binding_by_vlanid(session, vlan_id):
         return
 
 
-def add_network_binding(session, network_id, binding_type, tz_uuid, vlan_id):
+def get_network_binding_by_vlanid_and_phynet(session, vlan_id,
+                                             physical_network):
+    session = session or db.get_session()
+    try:
+        binding = (session.query(nicira_models.NvpNetworkBinding).
+                   filter_by(vlan_id=vlan_id, phy_uuid=physical_network).
+                   one())
+        return binding
+    except exc.NoResultFound:
+        return
+
+
+def add_network_binding(session, network_id, binding_type, phy_uuid, vlan_id):
     with session.begin(subtransactions=True):
         binding = nicira_models.NvpNetworkBinding(network_id, binding_type,
-                                                  tz_uuid, vlan_id)
+                                                  phy_uuid, vlan_id)
         session.add(binding)
     return binding
 
index 0e81eb556d8260b736bc45d5d6b06bb1f0961747..1c879c9f5e166214740e128585144a6644f3acf4 100644 (file)
@@ -33,22 +33,22 @@ class NvpNetworkBinding(model_base.BASEV2):
                         ForeignKey('networks.id', ondelete="CASCADE"),
                         primary_key=True)
     # 'flat', 'vlan', stt' or 'gre'
-    binding_type = Column(Enum('flat', 'vlan', 'stt', 'gre',
+    binding_type = Column(Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
                                name='nvp_network_bindings_binding_type'),
                           nullable=False)
-    tz_uuid = Column(String(36))
+    phy_uuid = Column(String(36))
     vlan_id = Column(Integer)
 
-    def __init__(self, network_id, binding_type, tz_uuid, vlan_id):
+    def __init__(self, network_id, binding_type, phy_uuid, vlan_id):
         self.network_id = network_id
         self.binding_type = binding_type
-        self.tz_uuid = tz_uuid
+        self.phy_uuid = phy_uuid
         self.vlan_id = vlan_id
 
     def __repr__(self):
         return "<NetworkBinding(%s,%s,%s,%s)>" % (self.network_id,
                                                   self.binding_type,
-                                                  self.tz_uuid,
+                                                  self.phy_uuid,
                                                   self.vlan_id)
 
 
index 0bbabd5984a3e6a5361a288f6354be7e1b2ac895..3af0c34552a64325203ba4b417af87708c0346dd 100644 (file)
@@ -869,18 +869,22 @@ def find_router_gw_port(context, cluster, router_id):
     # TODO(salvatore-orlando): Consider storing it in Quantum DB
     results = query_lrouter_lports(
         cluster, router_id,
-        filters={'attachment_gwsvc_uuid': cluster.default_l3_gw_service_uuid})
-    if len(results):
-        # Return logical router port
-        return results[0]
+        relations="LogicalPortAttachment")
+    for lport in results:
+        if '_relations' in lport:
+            attachment = lport['_relations'].get('LogicalPortAttachment')
+            if attachment and attachment.get('type') == 'L3GatewayAttachment':
+                return lport
 
 
 def plug_router_port_attachment(cluster, router_id, port_id,
-                                attachment_uuid, nvp_attachment_type):
+                                attachment_uuid, nvp_attachment_type,
+                                attachment_vlan=None):
     """Attach a router port to the given attachment.
        Current attachment types:
        - PatchAttachment [-> logical switch port uuid]
        - L3GatewayAttachment [-> L3GatewayService uuid]
+       For the latter attachment type a VLAN ID can be specified as well
     """
     uri = _build_uri_path(LROUTERPORT_RESOURCE, port_id, router_id,
                           is_attachment=True)
@@ -890,6 +894,8 @@ def plug_router_port_attachment(cluster, router_id, port_id,
         attach_obj["peer_port_uuid"] = attachment_uuid
     elif nvp_attachment_type == "L3GatewayAttachment":
         attach_obj["l3_gateway_service_uuid"] = attachment_uuid
+        if attachment_vlan:
+            attach_obj['vlan_id'] = attachment_vlan
     else:
         raise Exception(_("Invalid NVP attachment type '%s'"),
                         nvp_attachment_type)
index 9ba94d3e42fc9ea0a36584145e5f3b26ce7e569c..bc5723d118ac8ad2e387ebd9fbfbcb82f4293fe5 100644 (file)
@@ -3,6 +3,8 @@
     {
       %(peer_port_href_field)s
       %(peer_port_uuid_field)s
+      %(l3_gateway_service_uuid_field)s
+      %(vlan_id)s
       "type": "%(type)s",
       "schema": "/ws.v1/schema/%(type)s"
     }
index 039e22930cf6113b56b099094a999eb2f9649c39..772343c5664500663d35600c0956859fcdbbbea3 100644 (file)
@@ -245,10 +245,9 @@ class FakeClient:
         if not '_relations' in src or not src['_relations'].get(relation):
             return  # Item does not have relation
         relation_data = src['_relations'].get(relation)
-        dst_relations = dst.get('_relations')
-        if not dst_relations:
-            dst_relations = {}
+        dst_relations = dst.get('_relations', {})
         dst_relations[relation] = relation_data
+        dst['_relations'] = dst_relations
 
     def _fill_attachment(self, att_data, ls_uuid=None,
                          lr_uuid=None, lp_uuid=None):
@@ -266,7 +265,8 @@ class FakeClient:
             else:
                 new_data['%s_field' % field_name] = ""
 
-        for field in ['vif_uuid', 'peer_port_href', 'peer_port_uuid']:
+        for field in ['vif_uuid', 'peer_port_href', 'vlan_id',
+                      'peer_port_uuid', 'l3_gateway_service_uuid']:
             populate_field(field)
         return new_data
 
@@ -460,13 +460,11 @@ class FakeClient:
             if not is_attachment:
                 resource.update(json.loads(body))
             else:
-                relations = resource.get("_relations")
-                if not relations:
-                    relations = {}
-                relations['LogicalPortAttachment'] = json.loads(body)
-                resource['_relations'] = relations
+                relations = resource.get("_relations", {})
                 body_2 = json.loads(body)
                 resource['att_type'] = body_2['type']
+                relations['LogicalPortAttachment'] = body_2
+                resource['_relations'] = relations
                 if body_2['type'] == "PatchAttachment":
                     # We need to do a trick here
                     if self.LROUTER_RESOURCE in res_type:
@@ -486,6 +484,7 @@ class FakeClient:
                 elif body_2['type'] == "L3GatewayAttachment":
                     resource['attachment_gwsvc_uuid'] = (
                         body_2['l3_gateway_service_uuid'])
+                    resource['vlan_id'] = body_2.get('vlan_id')
                 elif body_2['type'] == "L2GatewayAttachment":
                     resource['attachment_gwsvc_uuid'] = (
                         body_2['l2_gateway_service_uuid'])
index f4c8d97860ca18e6d59f727b064998dbe9ef51f7..34d5749f864bc86706509221ee3f55a396fc6f17 100644 (file)
@@ -14,7 +14,6 @@
 # limitations under the License.
 
 import contextlib
-import logging
 import os
 
 import mock
@@ -26,6 +25,7 @@ import webob.exc
 from quantum.common import constants
 import quantum.common.test_lib as test_lib
 from quantum import context
+from quantum.extensions import l3
 from quantum.extensions import providernet as pnet
 from quantum.extensions import securitygroup as secgrp
 from quantum import manager
@@ -34,6 +34,7 @@ from quantum.plugins.nicira.nicira_nvp_plugin.extensions import nvp_networkgw
 from quantum.plugins.nicira.nicira_nvp_plugin.extensions import (nvp_qos
                                                                  as ext_qos)
 from quantum.plugins.nicira.nicira_nvp_plugin import nvplib
+from quantum.plugins.nicira.nicira_nvp_plugin import QuantumPlugin
 from quantum.tests.unit.nicira import fake_nvpapiclient
 import quantum.tests.unit.nicira.test_networkgw as test_l2_gw
 import quantum.tests.unit.test_db_plugin as test_plugin
@@ -42,7 +43,6 @@ import quantum.tests.unit.test_extension_security_group as ext_sg
 from quantum.tests.unit import test_extensions
 import quantum.tests.unit.test_l3_plugin as test_l3_plugin
 
-LOG = logging.getLogger(__name__)
 NICIRA_PKG_PATH = nvp_plugin.__name__
 NICIRA_EXT_PATH = "../../plugins/nicira/nicira_nvp_plugin/extensions"
 
@@ -261,6 +261,115 @@ class TestNiciraSecurityGroup(ext_sg.TestSecurityGroups,
 class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
                               NiciraPluginV2TestCase):
 
+    def _create_l3_ext_network(self, vlan_id=None):
+        name = 'l3_ext_net'
+        net_type = QuantumPlugin.NetworkTypes.L3_EXT
+        providernet_args = {pnet.NETWORK_TYPE: net_type,
+                            pnet.PHYSICAL_NETWORK: 'l3_gw_uuid'}
+        if vlan_id:
+            providernet_args[pnet.SEGMENTATION_ID] = vlan_id
+        return self.network(name=name,
+                            router__external=True,
+                            providernet_args=providernet_args,
+                            arg_list=(pnet.NETWORK_TYPE,
+                                      pnet.PHYSICAL_NETWORK,
+                                      pnet.SEGMENTATION_ID))
+
+    def _test_create_l3_ext_network(self, vlan_id=None):
+        name = 'l3_ext_net'
+        net_type = QuantumPlugin.NetworkTypes.L3_EXT
+        expected = [('subnets', []), ('name', name), ('admin_state_up', True),
+                    ('status', 'ACTIVE'), ('shared', False),
+                    (l3.EXTERNAL, True),
+                    (pnet.NETWORK_TYPE, net_type),
+                    (pnet.PHYSICAL_NETWORK, 'l3_gw_uuid'),
+                    (pnet.SEGMENTATION_ID, vlan_id)]
+        with self._create_l3_ext_network(vlan_id) as net:
+            for k, v in expected:
+                self.assertEqual(net['network'][k], v)
+
+    def _nvp_validate_ext_gw(self, router_id, l3_gw_uuid, vlan_id):
+        """ Verify data on fake NVP API client in order to validate
+        plugin did set them properly"""
+        ports = [port for port in self.fc._fake_lrouter_lport_dict.values()
+                 if (port['lr_uuid'] == router_id and
+                     port['att_type'] == "L3GatewayAttachment")]
+        self.assertEqual(len(ports), 1)
+        self.assertEqual(ports[0]['attachment_gwsvc_uuid'], l3_gw_uuid)
+        self.assertEqual(ports[0].get('vlan_id'), vlan_id)
+
+    def test_create_l3_ext_network_without_vlan(self):
+        self._test_create_l3_ext_network()
+
+    def _test_router_create_with_gwinfo_and_l3_ext_net(self, vlan_id=None):
+        with self._create_l3_ext_network(vlan_id) as net:
+            with self.subnet(network=net) as s:
+                data = {'router': {'tenant_id': 'whatever'}}
+                data['router']['name'] = 'router1'
+                data['router']['external_gateway_info'] = {
+                    'network_id': s['subnet']['network_id']}
+                router_req = self.new_create_request('routers', data,
+                                                     self.fmt)
+                try:
+                    res = router_req.get_response(self.ext_api)
+                    router = self.deserialize(self.fmt, res)
+                    self.assertEqual(
+                        s['subnet']['network_id'],
+                        (router['router']['external_gateway_info']
+                         ['network_id']))
+                    self._nvp_validate_ext_gw(router['router']['id'],
+                                              'l3_gw_uuid', vlan_id)
+                finally:
+                    self._delete('routers', router['router']['id'])
+
+    def test_router_create_with_gwinfo_and_l3_ext_net(self):
+        self._test_router_create_with_gwinfo_and_l3_ext_net()
+
+    def test_router_create_with_gwinfo_and_l3_ext_net_with_vlan(self):
+        self._test_router_create_with_gwinfo_and_l3_ext_net(444)
+
+    def _test_router_update_gateway_on_l3_ext_net(self, vlan_id=None):
+        with self.router() as r:
+            with self.subnet() as s1:
+                with self._create_l3_ext_network(vlan_id) as net:
+                    with self.subnet(network=net) as s2:
+                        self._set_net_external(s1['subnet']['network_id'])
+                        try:
+                            self._add_external_gateway_to_router(
+                                r['router']['id'],
+                                s1['subnet']['network_id'])
+                            body = self._show('routers', r['router']['id'])
+                            net_id = (body['router']
+                                      ['external_gateway_info']['network_id'])
+                            self.assertEqual(net_id,
+                                             s1['subnet']['network_id'])
+                            # Plug network with external mapping
+                            self._set_net_external(s2['subnet']['network_id'])
+                            self._add_external_gateway_to_router(
+                                r['router']['id'],
+                                s2['subnet']['network_id'])
+                            body = self._show('routers', r['router']['id'])
+                            net_id = (body['router']
+                                      ['external_gateway_info']['network_id'])
+                            self.assertEqual(net_id,
+                                             s2['subnet']['network_id'])
+                            self._nvp_validate_ext_gw(body['router']['id'],
+                                                      'l3_gw_uuid', vlan_id)
+                        finally:
+                            # Cleanup
+                            self._remove_external_gateway_from_router(
+                                r['router']['id'],
+                                s2['subnet']['network_id'])
+
+    def test_router_update_gateway_on_l3_ext_net(self):
+        self._test_router_update_gateway_on_l3_ext_net()
+
+    def test_router_update_gateway_on_l3_ext_net_with_vlan(self):
+        self._test_router_update_gateway_on_l3_ext_net(444)
+
+    def test_create_l3_ext_network_with_vlan(self):
+        self._test_create_l3_ext_network(666)
+
     def test_floatingip_with_assoc_fails(self):
         self._test_floatingip_with_assoc_fails(
             'quantum.plugins.nicira.nicira_nvp_plugin.'
index 557353e04d9a6d37346a3a62f98ab7f8d05fcefa..3e0e7b0d5091e2897cbd4bbbb66870668ea4a904 100644 (file)
@@ -333,7 +333,7 @@ class L3NatTestCaseBase(test_db_plugin.QuantumDbPluginV2TestCase):
         new_args = dict(itertools.izip(map(lambda x: x.replace('__', ':'),
                                            kwargs),
                                        kwargs.values()))
-        arg_list = (l3.EXTERNAL,)
+        arg_list = new_args.pop('arg_list', ()) + (l3.EXTERNAL,)
         return super(L3NatTestCaseBase, self)._create_network(
             fmt, name, admin_state_up, arg_list=arg_list, **new_args)