Support for NVP distributed router

Blueprint nvp-distributed-router

This patch adds support for NVP distributed logical routers
adding a simple attribute extension.
The default router type can be controlled used the default_router_type
option in the nvp section of neutron configuration.
In order to ensure backward compatibility, pre-existing routers
will be treated as centralized routers.

Change-Id: Iaab9ffb6071c93990be711ebb56c212230544a7a
This commit is contained in:
Salvatore Orlando 2013-07-03 22:07:09 +02:00
parent 58cd00cd1f
commit 634fd1d23f
16 changed files with 458 additions and 68 deletions

View File

@ -167,7 +167,7 @@ class L3_NAT_db_mixin(l3.RouterPluginBase):
context.session.add(router_db)
if has_gw_info:
self._update_router_gw_info(context, router_db['id'], gw_info)
return self._make_router_dict(router_db)
return self._make_router_dict(router_db, process_extensions=False)
def update_router(self, context, id, router):
r = router['router']

View File

@ -35,8 +35,9 @@ setattr(l3_db.Router, 'enable_snat',
class L3_NAT_db_mixin(l3_db.L3_NAT_db_mixin):
"""Mixin class to add configurable gateway modes."""
def _make_router_dict(self, router, fields=None):
res = super(L3_NAT_db_mixin, self)._make_router_dict(router)
def _make_router_dict(self, router, fields=None, process_extensions=True):
res = super(L3_NAT_db_mixin, self)._make_router_dict(
router, process_extensions=process_extensions)
if router['gw_port_id']:
nw_id = router.gw_port['network_id']
res[EXTERNAL_GW_INFO] = {'network_id': nw_id,

View File

@ -0,0 +1,60 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 OpenStack Foundation
#
# 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_dist_router
Revision ID: 40dffbf4b549
Revises: 63afba73813
Create Date: 2013-08-21 18:00:26.214923
"""
# revision identifiers, used by Alembic.
revision = '40dffbf4b549'
down_revision = '63afba73813'
# Change to ['*'] if this migration applies to all plugins
migration_for_plugins = [
'neutron.plugins.nicira.NeutronPlugin.NvpPluginV2'
]
from alembic import op
import sqlalchemy as sa
from neutron.db import migration
def upgrade(active_plugins=None, options=None):
if not migration.should_run(active_plugins, migration_for_plugins):
return
op.create_table(
'nsxrouterextattributess',
sa.Column('router_id', sa.String(length=36), nullable=False),
sa.Column('distributed', sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(
['router_id'], ['routers.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('router_id')
)
def downgrade(active_plugins=None, options=None):
if not migration.should_run(active_plugins, migration_for_plugins):
return
op.drop_table('nsxrouterextattributess')

View File

@ -64,6 +64,7 @@ from neutron.plugins.nicira.common import config # noqa
from neutron.plugins.nicira.common import exceptions as nvp_exc
from neutron.plugins.nicira.common import metadata_access as nvp_meta
from neutron.plugins.nicira.common import securitygroups as nvp_sec
from neutron.plugins.nicira.dbexts import distributedrouter as dist_rtr
from neutron.plugins.nicira.dbexts import maclearning as mac_db
from neutron.plugins.nicira.dbexts import nicira_db
from neutron.plugins.nicira.dbexts import nicira_networkgw_db as networkgw_db
@ -134,6 +135,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,
dist_rtr.DistributedRouter_mixin,
portbindings_db.PortBindingMixin,
portsecurity_db.PortSecurityDbMixin,
securitygroups_db.SecurityGroupDbMixin,
@ -152,6 +154,7 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
supported_extension_aliases = ["agent",
"binding",
"dhcp_agent_scheduler",
"dist-router",
"ext-gw-mode",
"extraroute",
"mac-learning",
@ -162,7 +165,7 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
"provider",
"quotas",
"router",
"security-group", ]
"security-group"]
__native_bulk_support = True
@ -171,6 +174,8 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
def __init__(self):
# TODO(salv-orlando): Replace These dicts with
# collections.defaultdict for better handling of default values
# Routines for managing logical ports in NVP
self._port_drivers = {
'create': {l3_db.DEVICE_OWNER_ROUTER_GW:
@ -1624,11 +1629,16 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
if ext_net.subnets:
ext_subnet = ext_net.subnets[0]
nexthop = ext_subnet.gateway_ip
lrouter = nvplib.create_lrouter(self.cluster, tenant_id,
router['router']['name'],
nexthop)
distributed = r.get('distributed')
lrouter = nvplib.create_lrouter(
self.cluster, tenant_id, router['router']['name'], nexthop,
distributed=attr.is_attr_set(distributed) and distributed)
# Use NVP identfier for Neutron resource
router['router']['id'] = lrouter['uuid']
r['id'] = lrouter['uuid']
# Update 'distributed' with value returned from NVP
# This will be useful for setting the value if the API request
# did not specify any value for the 'distributed' attribute
r['distributed'] = lrouter['distributed']
except NvpApiClient.NvpApiException:
raise nvp_exc.NvpPluginException(
err_msg=_("Unable to create logical router on NVP Platform"))
@ -1652,15 +1662,20 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
err_msg=_("Unable to create router %s") % r['name'])
with context.session.begin(subtransactions=True):
router_db = l3_db.Router(id=lrouter['uuid'],
tenant_id=tenant_id,
name=r['name'],
admin_state_up=r['admin_state_up'],
status="ACTIVE")
context.session.add(router_db)
# Transaction nesting is needed to avoid foreign key violations
# when processing the service provider binding
with context.session.begin(subtransactions=True):
router_db = l3_db.Router(id=lrouter['uuid'],
tenant_id=tenant_id,
name=r['name'],
admin_state_up=r['admin_state_up'],
status="ACTIVE")
self._process_distributed_router_create(context, router_db, r)
context.session.add(router_db)
if has_gw_info:
self._update_router_gw_info(context, router_db['id'], gw_info)
return self._make_router_dict(router_db)
router = self._make_router_dict(router_db)
return router
def update_router(self, context, router_id, router):
# Either nexthop is updated or should be kept as it was before
@ -1816,9 +1831,7 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
LOG.warning(_("Found %s logical routers not bound "
"to Neutron routers. Neutron and NVP are "
"potentially out of sync"), len(nvp_lrouters))
return [self._make_router_dict(router, fields)
for router in routers]
return [self._make_router_dict(router, fields) for router in routers]
def add_router_interface(self, context, router_id, interface_info):
# When adding interface by port_id we need to create the

View File

@ -0,0 +1,69 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 Nicira Networks, Inc. All rights reserved.
#
# 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.
#
# @author: Salvatore Orlando, Nicira, Inc
#
from neutron.db import db_base_plugin_v2
from neutron.extensions import l3
from neutron.openstack.common import log as logging
from neutron.plugins.nicira.dbexts import nicira_models
from neutron.plugins.nicira.extensions import distributedrouter as dist_rtr
LOG = logging.getLogger(__name__)
class DistributedRouter_mixin(object):
"""Mixin class to enable distributed router support."""
def _extend_router_dict_distributed(self, router_res, router_db):
# Avoid setting attribute to None for routers already existing before
# the data model was extended with the distributed attribute
nsx_attrs = router_db['nsx_attributes']
# Return False if nsx attributes are not definied for this
# neutron router
router_res[dist_rtr.DISTRIBUTED] = (
nsx_attrs and nsx_attrs['distributed'] or False)
def _process_distributed_router_create(
self, context, router_db, router_req):
"""Ensures persistency for the 'distributed' attribute.
Either creates or fetches the nicira extended attributes
record for this router and stores the 'distributed'
attribute value.
This method should be called from within a transaction, as
it does not start a new one.
"""
if not router_db['nsx_attributes']:
nsx_attributes = nicira_models.NSXRouterExtAttributes(
router_id=router_db['id'],
distributed=router_req['distributed'])
context.session.add(nsx_attributes)
router_db['nsx_attributes'] = nsx_attributes
else:
# The situation where the record already exists will
# be likely once the NSXRouterExtAttributes model
# will allow for defining several attributes pertaining
# to different extensions
router_db['nsx_attributes']['distributed'] = (
router_req['distributed'])
LOG.debug(_("Distributed router extension successfully processed "
"for router:%s"), router_db['id'])
# Register dict extend functions for ports
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
l3.ROUTERS, [_extend_router_dict_distributed])

View File

@ -16,8 +16,10 @@
# under the License.
from sqlalchemy import Column, Enum, ForeignKey, Integer, String
from sqlalchemy import Boolean, Column, Enum, ForeignKey, Integer, String
from sqlalchemy import orm
from neutron.db import l3_db
from neutron.db.models_v2 import model_base
@ -79,3 +81,17 @@ class MultiProviderNetworks(model_base.BASEV2):
def __init__(self, network_id):
self.network_id = network_id
class NSXRouterExtAttributes(model_base.BASEV2):
"""Router attributes managed by Nicira plugin extensions."""
router_id = Column(String(36),
ForeignKey('routers.id', ondelete="CASCADE"),
primary_key=True)
distributed = Column(Boolean, default=False, nullable=False)
# Add a relationship to the Router model in order to instruct
# SQLAlchemy to eagerly load this association
router = orm.relationship(
l3_db.Router,
backref=orm.backref("nsx_attributes", lazy='joined',
uselist=False, cascade='delete'))

View File

@ -0,0 +1,73 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 Nicira Networks, Inc. All rights reserved.
#
# 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.
#
from neutron.api.v2 import attributes
def convert_to_boolean_if_not_none(data):
if data is not None:
return attributes.convert_to_boolean(data)
return data
DISTRIBUTED = 'distributed'
EXTENDED_ATTRIBUTES_2_0 = {
'routers': {
DISTRIBUTED: {'allow_post': True, 'allow_put': False,
'convert_to': convert_to_boolean_if_not_none,
'default': attributes.ATTR_NOT_SPECIFIED,
'is_visible': True},
}
}
class Distributedrouter(object):
"""Extension class supporting distributed router."""
@classmethod
def get_name(cls):
return "Distributed Router"
@classmethod
def get_alias(cls):
return "dist-router"
@classmethod
def get_description(cls):
return "Enables configuration of NSX Distributed routers"
@classmethod
def get_namespace(cls):
return "http://docs.openstack.org/ext/dist-router/api/v1.0"
@classmethod
def get_updated(cls):
return "2013-08-1T10:00:00-00:00"
def get_required_extensions(self):
return ["router"]
@classmethod
def get_resources(cls):
"""Returns Ext Resources."""
return []
def get_extended_resources(self, version):
if version == "2.0":
return EXTENDED_ATTRIBUTES_2_0
else:
return {}

View File

@ -274,7 +274,8 @@ def create_l2_gw_service(cluster, tenant_id, display_name, devices):
json.dumps(gwservice_obj), cluster=cluster)
def _prepare_lrouter_body(name, tenant_id, router_type, **kwargs):
def _prepare_lrouter_body(name, tenant_id, router_type,
distributed=None, **kwargs):
body = {
"display_name": _check_and_truncate_name(name),
"tags": [{"tag": tenant_id, "scope": "os_tid"},
@ -284,12 +285,34 @@ def _prepare_lrouter_body(name, tenant_id, router_type, **kwargs):
},
"type": "LogicalRouterConfig"
}
# add the distributed key only if not None (ie: True or False)
if distributed is not None:
body['distributed'] = distributed
if kwargs:
body["routing_config"].update(kwargs)
return body
def create_implicit_routing_lrouter(cluster, tenant_id, display_name, nexthop):
def _create_implicit_routing_lrouter(cluster, tenant_id,
display_name, nexthop,
distributed=None):
implicit_routing_config = {
"default_route_next_hop": {
"gateway_ip_address": nexthop,
"type": "RouterNextHop"
},
}
lrouter_obj = _prepare_lrouter_body(
display_name, tenant_id,
"SingleDefaultRouteImplicitRoutingConfig",
distributed=distributed,
**implicit_routing_config)
return do_request(HTTP_POST, _build_uri_path(LROUTER_RESOURCE),
json.dumps(lrouter_obj), cluster=cluster)
def create_implicit_routing_lrouter(cluster, tenant_id,
display_name, nexthop):
"""Create a NVP logical router on the specified cluster.
:param cluster: The target NVP cluster
@ -300,24 +323,34 @@ def create_implicit_routing_lrouter(cluster, tenant_id, display_name, nexthop):
:raise NvpApiException: if there is a problem while communicating
with the NVP controller
"""
implicit_routing_config = {
"default_route_next_hop": {
"gateway_ip_address": nexthop,
"type": "RouterNextHop"
},
}
lrouter_obj = _prepare_lrouter_body(
display_name, tenant_id,
"SingleDefaultRouteImplicitRoutingConfig",
**implicit_routing_config)
return do_request(HTTP_POST, _build_uri_path(LROUTER_RESOURCE),
json.dumps(lrouter_obj), cluster=cluster)
return _create_implicit_routing_lrouter(
cluster, tenant_id, display_name, nexthop)
def create_implicit_routing_lrouter_with_distribution(
cluster, tenant_id, display_name, nexthop, distributed=None):
"""Create a NVP logical router on the specified cluster.
This function also allows for creating distributed lrouters
:param cluster: The target NVP cluster
:param tenant_id: Identifier of the Openstack tenant for which
the logical router is being created
:param display_name: Descriptive name of this logical router
:param nexthop: External gateway IP address for the logical router
:param distributed: True for distributed logical routers
:raise NvpApiException: if there is a problem while communicating
with the NVP controller
"""
return _create_implicit_routing_lrouter(
cluster, tenant_id, display_name, nexthop, distributed)
def create_explicit_routing_lrouter(cluster, tenant_id,
display_name, nexthop):
display_name, nexthop,
distributed=None):
lrouter_obj = _prepare_lrouter_body(
display_name, tenant_id, "RoutingTableRoutingConfig")
display_name, tenant_id, "RoutingTableRoutingConfig",
distributed=distributed)
router = do_request(HTTP_POST, _build_uri_path(LROUTER_RESOURCE),
json.dumps(lrouter_obj), cluster=cluster)
default_gw = {'prefix': '0.0.0.0/0', 'next_hop_ip': nexthop}
@ -1191,6 +1224,7 @@ NVPLIB_FUNC_DICT = {
'create_lrouter': {
2: {DEFAULT: create_implicit_routing_lrouter, },
3: {DEFAULT: create_implicit_routing_lrouter,
1: create_implicit_routing_lrouter_with_distribution,
2: create_explicit_routing_lrouter, }, },
'update_lrouter': {
2: {DEFAULT: update_implicit_routing_lrouter, },

View File

@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from neutron.common.test_lib import test_config
from neutron.tests.unit.metaplugin.test_metaplugin import setup_metaplugin_conf
from neutron.tests.unit import test_db_plugin as test_plugin
from neutron.tests.unit import test_l3_plugin
@ -24,11 +23,15 @@ class MetaPluginV2DBTestCase(test_plugin.NeutronDbPluginV2TestCase):
_plugin_name = ('neutron.plugins.metaplugin.'
'meta_neutron_plugin.MetaPluginV2')
def setUp(self):
def setUp(self, plugin=None, ext_mgr=None):
# NOTE(salv-orlando): The plugin keyword argument is ignored,
# as this class will always invoke super with self._plugin_name.
# These keyword parameters ensure setUp methods always have the
# same signature.
setup_metaplugin_conf()
ext_mgr = test_l3_plugin.L3TestExtensionManager()
test_config['extension_manager'] = ext_mgr
super(MetaPluginV2DBTestCase, self).setUp(self._plugin_name)
ext_mgr = ext_mgr or test_l3_plugin.L3TestExtensionManager()
super(MetaPluginV2DBTestCase, self).setUp(
plugin=self._plugin_name, ext_mgr=ext_mgr)
class TestMetaBasicGet(test_plugin.TestBasicGet,

View File

@ -1,5 +1,6 @@
{
"display_name": "%(display_name)s",
%(distributed_json)s
"uuid": "%(uuid)s",
"tags": %(tags_json)s,
"routing_config": {

View File

@ -1,5 +1,6 @@
{
"display_name": "%(display_name)s",
%(distributed_json)s
"uuid": "%(uuid)s",
"tags": [
{

View File

@ -168,6 +168,14 @@ class FakeClient:
'default_route_next_hop')
fake_lrouter['default_next_hop'] = default_nexthop.get(
'gateway_ip_address', '0.0.0.0')
# NOTE(salv-orlando): We won't make the Fake NVP API client
# aware of NVP version. The long term plan is to replace it
# with behavioral mocking of NVP API requests
if 'distributed' not in fake_lrouter:
fake_lrouter['distributed'] = False
distributed_json = ('"distributed": %s,' %
str(fake_lrouter['distributed']).lower())
fake_lrouter['distributed_json'] = distributed_json
return fake_lrouter
def _add_lrouter(self, body):

View File

@ -20,11 +20,13 @@ import netaddr
from oslo.config import cfg
import webob.exc
from neutron.api.v2 import attributes
from neutron.common import constants
from neutron.common import exceptions as ntn_exc
import neutron.common.test_lib as test_lib
from neutron import context
from neutron.extensions import l3
from neutron.extensions import l3_ext_gw_mode
from neutron.extensions import multiprovidernet as mpnet
from neutron.extensions import portbindings
from neutron.extensions import providernet as pnet
@ -34,6 +36,7 @@ from neutron.openstack.common import uuidutils
from neutron.plugins.nicira.common import exceptions as nvp_exc
from neutron.plugins.nicira.dbexts import nicira_db
from neutron.plugins.nicira.dbexts import nicira_qos_db as qos_db
from neutron.plugins.nicira.extensions import distributedrouter as dist_router
from neutron.plugins.nicira.extensions import nvp_networkgw
from neutron.plugins.nicira.extensions import nvp_qos as ext_qos
from neutron.plugins.nicira import NeutronPlugin
@ -57,6 +60,10 @@ import neutron.tests.unit.test_l3_plugin as test_l3_plugin
from neutron.tests.unit import testlib_api
from neutron.openstack.common import log
LOG = log.getLogger(__name__)
class NiciraPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
def _create_network(self, fmt, name, admin_state_up,
@ -64,9 +71,9 @@ class NiciraPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
data = {'network': {'name': name,
'admin_state_up': admin_state_up,
'tenant_id': self._tenant_id}}
attributes = kwargs
attrs = kwargs
if providernet_args:
attributes.update(providernet_args)
attrs.update(providernet_args)
for arg in (('admin_state_up', 'tenant_id', 'shared') +
(arg_list or ())):
# Arg must be present and not empty
@ -79,20 +86,23 @@ class NiciraPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
'', kwargs['tenant_id'])
return network_req.get_response(self.api)
def setUp(self):
def setUp(self, plugin=None, ext_mgr=None):
test_lib.test_config['config_files'] = [get_fake_conf('nvp.ini.test')]
# mock nvp api client
self.fc = fake_nvpapiclient.FakeClient(STUBS_PATH)
self.mock_nvpapi = mock.patch(NVPAPI_NAME, autospec=True)
instance = self.mock_nvpapi.start()
self.mock_instance = self.mock_nvpapi.start()
def _fake_request(*args, **kwargs):
return self.fc.fake_request(*args, **kwargs)
# Emulate tests against NVP 2.x
instance.return_value.get_nvp_version.return_value = NVPVersion("2.9")
instance.return_value.request.side_effect = _fake_request
super(NiciraPluginV2TestCase, self).setUp(plugin=PLUGIN_NAME)
self.mock_instance.return_value.get_nvp_version.return_value = (
NVPVersion("2.9"))
self.mock_instance.return_value.request.side_effect = _fake_request
plugin = plugin or PLUGIN_NAME
super(NiciraPluginV2TestCase, self).setUp(plugin=plugin,
ext_mgr=ext_mgr)
cfg.CONF.set_override('metadata_mode', None, 'NVP')
self.addCleanup(self.fc.reset_all)
self.addCleanup(self.mock_nvpapi.stop)
@ -361,9 +371,47 @@ class TestNiciraSecurityGroup(ext_sg.TestSecurityGroups,
self.assertEqual(sg['security_group']['name'], name)
class TestNiciraL3ExtensionManager(object):
def get_resources(self):
# Simulate extension of L3 attribute map
# First apply attribute extensions
for key in l3.RESOURCE_ATTRIBUTE_MAP.keys():
l3.RESOURCE_ATTRIBUTE_MAP[key].update(
l3_ext_gw_mode.EXTENDED_ATTRIBUTES_2_0.get(key, {}))
l3.RESOURCE_ATTRIBUTE_MAP[key].update(
dist_router.EXTENDED_ATTRIBUTES_2_0.get(key, {}))
# Finally add l3 resources to the global attribute map
attributes.RESOURCE_ATTRIBUTE_MAP.update(
l3.RESOURCE_ATTRIBUTE_MAP)
return l3.L3.get_resources()
def get_actions(self):
return []
def get_request_extensions(self):
return []
class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
NiciraPluginV2TestCase):
def _restore_l3_attribute_map(self):
l3.RESOURCE_ATTRIBUTE_MAP = self._l3_attribute_map_bk
def setUp(self):
self._l3_attribute_map_bk = {}
for item in l3.RESOURCE_ATTRIBUTE_MAP:
self._l3_attribute_map_bk[item] = (
l3.RESOURCE_ATTRIBUTE_MAP[item].copy())
cfg.CONF.set_override('api_extensions_path', NVPEXT_PATH)
self.addCleanup(self._restore_l3_attribute_map)
super(TestNiciraL3NatTestCase, self).setUp(
ext_mgr=TestNiciraL3ExtensionManager())
def tearDown(self):
super(TestNiciraL3NatTestCase, self).tearDown()
def _create_l3_ext_network(self, vlan_id=None):
name = 'l3_ext_net'
net_type = NeutronPlugin.NetworkTypes.L3_EXT
@ -432,6 +480,33 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
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_create_with_distributed(self, dist_input, dist_expected):
self.mock_instance.return_value.get_nvp_version.return_value = (
NvpApiClient.NVPVersion('3.1'))
data = {'tenant_id': 'whatever'}
data['name'] = 'router1'
data['distributed'] = dist_input
router_req = self.new_create_request(
'routers', {'router': data}, self.fmt)
try:
res = router_req.get_response(self.ext_api)
router = self.deserialize(self.fmt, res)
self.assertIn('distributed', router['router'])
self.assertEqual(dist_expected,
router['router']['distributed'])
finally:
self._delete('routers', router['router']['id'])
def test_router_create_distributed(self):
self._test_router_create_with_distributed(True, True)
def test_router_create_not_distributed(self):
self._test_router_create_with_distributed(False, False)
def test_router_create_distributed_unspecified(self):
self._test_router_create_with_distributed(None, False)
def test_router_create_nvp_error_returns_500(self, vlan_id=None):
with mock.patch.object(nvplib,
'create_router_lport',
@ -447,6 +522,16 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
res = router_req.get_response(self.ext_api)
self.assertEqual(500, res.status_int)
def test_router_add_gateway_invalid_network_returns_404(self):
# NOTE(salv-orlando): This unit test has been overriden
# as the nicira plugin support the ext_gw_mode extension
# which mandates a uuid for the external network identifier
with self.router() as r:
self._add_external_gateway_to_router(
r['router']['id'],
uuidutils.generate_uuid(),
expected_code=webob.exc.HTTPNotFound.code)
def _test_router_update_gateway_on_l3_ext_net(self, vlan_id=None):
with self.router() as r:
with self.subnet() as s1:
@ -1004,8 +1089,11 @@ class NiciraExtGwModeTestCase(NiciraPluginV2TestCase,
pass
class NiciraNeutronNVPOutOfSync(test_l3_plugin.L3NatTestCaseBase,
NiciraPluginV2TestCase):
class NiciraNeutronNVPOutOfSync(NiciraPluginV2TestCase,
test_l3_plugin.L3NatTestCaseBase):
def setUp(self):
super(NiciraNeutronNVPOutOfSync, self).setUp()
def test_delete_network_not_in_nvp(self):
res = self._create_network('json', 'net1', True)

View File

@ -42,7 +42,7 @@ class NvplibTestCase(base.BaseTestCase):
self.mock_nvpapi = mock.patch(NVPAPI_NAME, autospec=True)
instance = self.mock_nvpapi.start()
instance.return_value.login.return_value = "the_cookie"
fake_version = getattr(self, 'fake_version', "2.9")
fake_version = getattr(self, 'fake_version', "3.0")
instance.return_value.get_nvp_version.return_value = (
NvpApiClient.NVPVersion(fake_version))
@ -106,10 +106,11 @@ class NvplibNegativeTests(base.BaseTestCase):
self.mock_nvpapi = mock.patch(NVPAPI_NAME, autospec=True)
instance = self.mock_nvpapi.start()
instance.return_value.login.return_value = "the_cookie"
# Choose 2.9, but the version is irrelevant for the aim of
# Choose 3.0, but the version is irrelevant for the aim of
# these tests as calls are throwing up errors anyway
self.fake_version = NvpApiClient.NVPVersion('2.9')
instance.return_value.get_nvp_version.return_value = self.fake_version
fake_version = getattr(self, 'fake_version', "3.0")
instance.return_value.get_nvp_version.return_value = (
NvpApiClient.NVPVersion(fake_version))
def _faulty_request(*args, **kwargs):
raise nvplib.NvpApiClient.NvpApiException
@ -572,7 +573,8 @@ class TestNvplibLogicalRouters(NvplibTestCase):
expected_uuid,
expected_display_name,
expected_nexthop,
expected_tenant_id):
expected_tenant_id,
expected_distributed=None):
self.assertEqual(res_lrouter['uuid'], expected_uuid)
nexthop = (res_lrouter['routing_config']
['default_route_next_hop']['gateway_ip_address'])
@ -581,6 +583,9 @@ class TestNvplibLogicalRouters(NvplibTestCase):
self.assertIn('os_tid', router_tags)
self.assertEqual(res_lrouter['display_name'], expected_display_name)
self.assertEqual(expected_tenant_id, router_tags['os_tid'])
if expected_distributed is not None:
self.assertEqual(expected_distributed,
res_lrouter['distributed'])
def test_get_lrouters(self):
lrouter_uuids = [nvplib.create_lrouter(
@ -590,16 +595,33 @@ class TestNvplibLogicalRouters(NvplibTestCase):
for router in routers:
self.assertIn(router['uuid'], lrouter_uuids)
def test_create_and_get_lrouter(self):
lrouter = nvplib.create_lrouter(self.fake_cluster,
'pippo',
'fake-lrouter',
'10.0.0.1')
res_lrouter = nvplib.get_lrouter(self.fake_cluster,
lrouter['uuid'])
self._verify_lrouter(res_lrouter, lrouter['uuid'],
def _create_lrouter(self, version, distributed=None):
with mock.patch.object(
self.fake_cluster.api_client, 'get_nvp_version',
return_value=NvpApiClient.NVPVersion(version)):
lrouter = nvplib.create_lrouter(
self.fake_cluster, 'pippo', 'fake-lrouter',
'10.0.0.1', distributed=distributed)
return nvplib.get_lrouter(self.fake_cluster,
lrouter['uuid'])
def test_create_and_get_lrouter_v30(self):
res_lrouter = self._create_lrouter('3.0')
self._verify_lrouter(res_lrouter, res_lrouter['uuid'],
'fake-lrouter', '10.0.0.1', 'pippo')
def test_create_and_get_lrouter_v31_centralized(self):
res_lrouter = self._create_lrouter('3.1', distributed=False)
self._verify_lrouter(res_lrouter, res_lrouter['uuid'],
'fake-lrouter', '10.0.0.1', 'pippo',
expected_distributed=False)
def test_create_and_get_lrouter_v31_distributed(self):
res_lrouter = self._create_lrouter('3.1', distributed=True)
self._verify_lrouter(res_lrouter, res_lrouter['uuid'],
'fake-lrouter', '10.0.0.1', 'pippo',
expected_distributed=True)
def test_create_and_get_lrouter_name_exceeds_40chars(self):
display_name = '*' * 50
lrouter = nvplib.create_lrouter(self.fake_cluster,

View File

@ -300,7 +300,7 @@ class TestL3GwModeMixin(base.BaseTestCase):
class ExtGwModeTestCase(test_db_plugin.NeutronDbPluginV2TestCase,
test_l3_plugin.L3NatTestCaseMixin):
def setUp(self, plugin=None):
def setUp(self, plugin=None, ext_mgr=None):
# Store l3 resource attribute map as it will be updated
self._l3_attribute_map_bk = {}
for item in l3.RESOURCE_ATTRIBUTE_MAP:
@ -310,8 +310,9 @@ class ExtGwModeTestCase(test_db_plugin.NeutronDbPluginV2TestCase,
'neutron.tests.unit.test_extension_ext_gw_mode.TestDbPlugin')
# for these tests we need to enable overlapping ips
cfg.CONF.set_default('allow_overlapping_ips', True)
ext_mgr = ext_mgr or TestExtensionManager()
super(ExtGwModeTestCase, self).setUp(plugin=plugin,
ext_mgr=TestExtensionManager())
ext_mgr=ext_mgr)
self.addCleanup(self.restore_l3_attribute_map)
def restore_l3_attribute_map(self):

View File

@ -481,14 +481,14 @@ class L3NatTestCaseMixin(object):
class L3NatTestCaseBase(L3NatTestCaseMixin,
test_db_plugin.NeutronDbPluginV2TestCase):
def setUp(self):
def setUp(self, plugin=None, ext_mgr=None):
test_config['plugin_name_v2'] = (
'neutron.tests.unit.test_l3_plugin.TestL3NatPlugin')
# for these tests we need to enable overlapping ips
cfg.CONF.set_default('allow_overlapping_ips', True)
ext_mgr = L3TestExtensionManager()
test_config['extension_manager'] = ext_mgr
super(L3NatTestCaseBase, self).setUp()
ext_mgr = ext_mgr or L3TestExtensionManager()
super(L3NatTestCaseBase, self).setUp(
plugin=plugin, ext_mgr=ext_mgr)
# Set to None to reload the drivers
notifier_api._drivers = None