Merge "NSX: unify the two distributed routing extensions"

This commit is contained in:
Jenkins 2014-08-13 12:40:54 +00:00 committed by Gerrit Code Review
commit 76dd028cdd
12 changed files with 118 additions and 166 deletions

View File

@ -36,6 +36,10 @@ class RouterExtraAttributes(model_base.BASEV2):
distributed = sa.Column(sa.Boolean, default=False,
server_default=sa.sql.false(),
nullable=False)
# Whether the router is to be considered a 'service' router
service_router = sa.Column(sa.Boolean, default=False,
server_default=sa.sql.false(),
nullable=False)
router = orm.relationship(
l3_db.Router,
backref=orm.backref("extra_attributes", lazy='joined',

View File

@ -0,0 +1,73 @@
# Copyright 2014 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.
#
"""Drop NSX table in favor of the extra_attributes one
Revision ID: 884573acbf1c
Revises: 5589aa32bf80
Create Date: 2013-01-07 13:47:29.093160
"""
revision = '884573acbf1c'
down_revision = '5589aa32bf80'
from alembic import op
import sqlalchemy as sa
def _migrate_data(old_table, new_table):
engine = op.get_bind().engine
if engine.name == 'postgresql':
op.execute(("UPDATE %(new_table)s new_t "
"SET distributed = old_t.distributed, "
"service_router = old_t.service_router "
"FROM %(old_table)s old_t "
"WHERE new_t.router_id = old_t.router_id") %
{'new_table': new_table, 'old_table': old_table})
else:
op.execute(("UPDATE %(new_table)s new_t "
"INNER JOIN %(old_table)s as old_t "
"ON new_t.router_id = old_t.router_id "
"SET new_t.distributed = old_t.distributed, "
"new_t.service_router = old_t.service_router") %
{'new_table': new_table, 'old_table': old_table})
def upgrade(active_plugins=None, options=None):
op.add_column('router_extra_attributes',
sa.Column('service_router', sa.Boolean(),
nullable=False,
server_default=sa.sql.false()))
_migrate_data('router_extra_attributes', 'nsxrouterextattributess')
op.drop_table('nsxrouterextattributess')
def downgrade(active_plugins=None, options=None):
op.create_table(
'nsxrouterextattributess',
sa.Column('router_id', sa.String(length=36), nullable=False),
sa.Column('distributed', sa.Boolean(), nullable=False,
server_default=sa.sql.false()),
sa.Column('service_router', sa.Boolean(), nullable=False,
server_default=sa.sql.false()),
sa.ForeignKeyConstraint(
['router_id'], ['routers.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('router_id')
)
op.execute(("INSERT INTO nsxrouterextattributess "
"SELECT * from router_extra_attributes"))
op.drop_column('router_extra_attributes', 'service_router')

View File

@ -1 +1 @@
5589aa32bf80
884573acbf1c

View File

@ -119,3 +119,7 @@ class LsnMigrationConflict(n_exc.Conflict):
class LsnConfigurationConflict(NsxPluginException):
message = _("Configuration conflict on Logical Service Node %(lsn_id)s")
class ReadOnlyAttribute(NsxPluginException):
message = _("Cannot update read-only attribute %(attribute)s")

View File

@ -1,28 +0,0 @@
# Copyright 2013 VMware, 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.plugins.vmware.dbexts import nsxrouter
from neutron.plugins.vmware.extensions import distributedrouter as dist_rtr
class DistributedRouter_mixin(nsxrouter.NsxRouterMixin):
"""Mixin class to enable distributed router support."""
nsx_attributes = (
nsxrouter.NsxRouterMixin.nsx_attributes + [{
'name': dist_rtr.DISTRIBUTED,
'default': False
}])

View File

@ -15,11 +15,8 @@
# under the License.
from sqlalchemy import Boolean, Column, Enum, ForeignKey, Integer, String
from sqlalchemy import orm
from sqlalchemy import sql
from sqlalchemy import Column, Enum, ForeignKey, Integer, String
from neutron.db import l3_db
from neutron.db import model_base
@ -118,21 +115,4 @@ class MultiProviderNetworks(model_base.BASEV2):
primary_key=True)
def __init__(self, network_id):
self.network_id = network_id
class NSXRouterExtAttributes(model_base.BASEV2):
"""Router attributes managed by NSX plugin extensions."""
router_id = Column(String(36),
ForeignKey('routers.id', ondelete="CASCADE"),
primary_key=True)
distributed = Column(Boolean, default=False, server_default=sql.false(),
nullable=False)
service_router = Column(Boolean, default=False, server_default=sql.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'))
self.network_id = network_id

View File

@ -1,66 +0,0 @@
# Copyright 2013 VMware, 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.db import db_base_plugin_v2
from neutron.extensions import l3
from neutron.openstack.common import log as logging
from neutron.plugins.vmware.dbexts import models
LOG = logging.getLogger(__name__)
class NsxRouterMixin(object):
"""Mixin class to enable nsx router support."""
nsx_attributes = []
def _extend_nsx_router_dict(self, router_res, router_db):
nsx_attrs = router_db['nsx_attributes']
# Return False if nsx attributes are not definied for this
# neutron router
for attr in self.nsx_attributes:
name = attr['name']
default = attr['default']
router_res[name] = (
nsx_attrs and nsx_attrs[name] or default)
def _process_nsx_router_create(
self, context, router_db, router_req):
if not router_db['nsx_attributes']:
kwargs = {}
for attr in self.nsx_attributes:
name = attr['name']
default = attr['default']
kwargs[name] = router_req.get(name, default)
nsx_attributes = models.NSXRouterExtAttributes(
router_id=router_db['id'], **kwargs)
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
for attr in self.nsx_attributes:
name = attr['name']
default = attr['default']
router_db['nsx_attributes'][name] = router_req.get(
name, default)
LOG.debug(_("Nsx 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_nsx_router_dict'])

View File

@ -13,15 +13,15 @@
# under the License.
#
from neutron.plugins.vmware.dbexts import distributedrouter as dist_rtr
from neutron.db import l3_dvr_db
from neutron.plugins.vmware.extensions import servicerouter
class ServiceRouter_mixin(dist_rtr.DistributedRouter_mixin):
class ServiceRouter_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin):
"""Mixin class to enable service router support."""
nsx_attributes = (
dist_rtr.DistributedRouter_mixin.nsx_attributes + [{
extra_attributes = (
l3_dvr_db.L3_NAT_with_dvr_db_mixin.extra_attributes + [{
'name': servicerouter.SERVICE_ROUTER,
'default': False
}])

View File

@ -12,28 +12,14 @@
# License for the specific language governing permissions and limitations
# under the License.
from neutron.api.v2 import attributes
# TODO(armando-migliaccio): This is deprecated in Juno, and
# to be removed in Kilo.
from neutron.extensions import dvr
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."""
class Distributedrouter(dvr.Dvr):
"""(Deprecated) Extension class supporting distributed router."""
@classmethod
def get_name(cls):
@ -45,26 +31,9 @@ class Distributedrouter(object):
@classmethod
def get_description(cls):
return "Enables configuration of NSX Distributed routers."
return ("Enables configuration of NSX "
"Distributed routers (Deprecated).")
@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

@ -34,6 +34,7 @@ from neutron.db import db_base_plugin_v2
from neutron.db import external_net_db
from neutron.db import extraroute_db
from neutron.db import l3_db
from neutron.db import l3_dvr_db
from neutron.db import l3_gwmode_db
from neutron.db import models_v2
from neutron.db import portbindings_db
@ -63,7 +64,6 @@ from neutron.plugins.vmware.common import securitygroups as sg_utils
from neutron.plugins.vmware.common import sync
from neutron.plugins.vmware.common import utils as c_utils
from neutron.plugins.vmware.dbexts import db as nsx_db
from neutron.plugins.vmware.dbexts import distributedrouter as dist_rtr
from neutron.plugins.vmware.dbexts import maclearning as mac_db
from neutron.plugins.vmware.dbexts import networkgw_db
from neutron.plugins.vmware.dbexts import qos_db
@ -89,7 +89,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
agentschedulers_db.DhcpAgentSchedulerDbMixin,
db_base_plugin_v2.NeutronDbPluginV2,
dhcpmeta_modes.DhcpMetadataAccess,
dist_rtr.DistributedRouter_mixin,
l3_dvr_db.L3_NAT_with_dvr_db_mixin,
external_net_db.External_net_db_mixin,
extraroute_db.ExtraRoute_db_mixin,
l3_gwmode_db.L3_NAT_db_mixin,
@ -102,6 +102,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
supported_extension_aliases = ["allowed-address-pairs",
"binding",
"dvr",
"dist-router",
"ext-gw-mode",
"extraroute",
@ -741,6 +742,8 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
webob.exc.HTTPBadRequest,
nsx_exc.NoMorePortsException:
webob.exc.HTTPBadRequest,
nsx_exc.ReadOnlyAttribute:
webob.exc.HTTPBadRequest,
nsx_exc.MaintenanceInProgress:
webob.exc.HTTPServiceUnavailable,
nsx_exc.InvalidSecurityCertificate:
@ -1439,7 +1442,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
admin_state_up=r['admin_state_up'],
status=lrouter['status'])
context.session.add(router_db)
self._process_nsx_router_create(context, router_db, r)
self._process_extra_attr_router_create(context, router_db, r)
# Ensure neutron router is moved into the transaction's buffer
context.session.flush()
# Add mapping between neutron and nsx identifiers
@ -1482,6 +1485,9 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
self.cluster, nsx_router_id, routes)
def update_router(self, context, router_id, router):
if isinstance(router['router'].get('distributed'), bool):
# Router conversion is not supported
raise nsx_exc.ReadOnlyAttribute(attribute='distributed')
# Either nexthop is updated or should be kept as it was before
r = router['router']
nexthop = None

View File

@ -142,7 +142,7 @@ class NsxAdvancedPlugin(sr_db.ServiceRouter_mixin,
router = self._get_router(context, router_id)
LOG.debug(_("EDGE: router = %s"), router)
if router['nsx_attributes']['service_router']:
if router['extra_attributes']['service_router']:
router_type = ROUTER_TYPE_ADVANCED
else:
router_type = ROUTER_TYPE_BASIC

View File

@ -28,6 +28,7 @@ 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 dvr
from neutron.extensions import external_net
from neutron.extensions import l3
from neutron.extensions import l3_ext_gw_mode
@ -43,7 +44,6 @@ from neutron.plugins.vmware.common import exceptions as nsx_exc
from neutron.plugins.vmware.common import sync
from neutron.plugins.vmware.common import utils
from neutron.plugins.vmware.dbexts import db as nsx_db
from neutron.plugins.vmware.extensions import distributedrouter as dist_router
from neutron.plugins.vmware import nsxlib
from neutron.tests.unit import _test_extension_portbindings as test_bindings
import neutron.tests.unit.test_db_plugin as test_plugin
@ -408,7 +408,7 @@ class TestL3ExtensionManager(object):
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, {}))
dvr.EXTENDED_ATTRIBUTES_2_0.get(key, {}))
# Finally add l3 resources to the global attribute map
attributes.RESOURCE_ATTRIBUTE_MAP.update(
l3.RESOURCE_ATTRIBUTE_MAP)
@ -1225,6 +1225,16 @@ class NeutronNsxOutOfSync(NsxPluginV2TestCase,
self._test_remove_router_interface_nsx_out_of_sync(unsync_action)
def test_update_router_distributed_bad_request(self):
res = self._create_router('json', 'tenant')
router = self.deserialize('json', res)
req = self.new_update_request(
'routers',
{'router': {'distributed': True}},
router['router']['id'])
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, 400)
def test_update_router_not_in_nsx(self):
res = self._create_router('json', 'tenant')
router = self.deserialize('json', res)