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:
parent
58cd00cd1f
commit
634fd1d23f
@ -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']
|
||||
|
@ -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,
|
||||
|
@ -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')
|
@ -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
|
||||
|
69
neutron/plugins/nicira/dbexts/distributedrouter.py
Normal file
69
neutron/plugins/nicira/dbexts/distributedrouter.py
Normal 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])
|
@ -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'))
|
||||
|
73
neutron/plugins/nicira/extensions/distributedrouter.py
Normal file
73
neutron/plugins/nicira/extensions/distributedrouter.py
Normal 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 {}
|
@ -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, },
|
||||
|
@ -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,
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"display_name": "%(display_name)s",
|
||||
%(distributed_json)s
|
||||
"uuid": "%(uuid)s",
|
||||
"tags": %(tags_json)s,
|
||||
"routing_config": {
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"display_name": "%(display_name)s",
|
||||
%(distributed_json)s
|
||||
"uuid": "%(uuid)s",
|
||||
"tags": [
|
||||
{
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user