From 86e307ea5c3bf9db817e706fe156f0d6c9c141f4 Mon Sep 17 00:00:00 2001 From: cheng Date: Sat, 30 Jul 2016 04:18:53 -0400 Subject: [PATCH] Fixes enable taas id reuse Change-Id: I93e64eae7794ee4a315ddda2ea85457e8e9597be closes-Bug: #1501953 --- .../alembic_migration/versions/CONTRACT_HEAD | 2 +- ...id_associations_to_support_tap_id_reuse.py | 50 +++++++++++++++++++ neutron_taas/db/taas_db.py | 50 ++++++++++++++++--- .../services/taas/service_drivers/taas_rpc.py | 14 ++---- .../unit/services/taas/test_taas_plugin.py | 28 +++++++++++ 5 files changed, 125 insertions(+), 19 deletions(-) create mode 100644 neutron_taas/db/migration/alembic_migration/versions/pike/contract/bac61f603e39_alter_tap_id_associations_to_support_tap_id_reuse.py diff --git a/neutron_taas/db/migration/alembic_migration/versions/CONTRACT_HEAD b/neutron_taas/db/migration/alembic_migration/versions/CONTRACT_HEAD index ac415528..b715d7a5 100644 --- a/neutron_taas/db/migration/alembic_migration/versions/CONTRACT_HEAD +++ b/neutron_taas/db/migration/alembic_migration/versions/CONTRACT_HEAD @@ -1 +1 @@ -4086b3cffc01 +bac61f603e39 diff --git a/neutron_taas/db/migration/alembic_migration/versions/pike/contract/bac61f603e39_alter_tap_id_associations_to_support_tap_id_reuse.py b/neutron_taas/db/migration/alembic_migration/versions/pike/contract/bac61f603e39_alter_tap_id_associations_to_support_tap_id_reuse.py new file mode 100644 index 00000000..bba51fcf --- /dev/null +++ b/neutron_taas/db/migration/alembic_migration/versions/pike/contract/bac61f603e39_alter_tap_id_associations_to_support_tap_id_reuse.py @@ -0,0 +1,50 @@ +# Copyright 2016-17 +# +# 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. + +"""Alter TapIdAssociations to support tap id reuse + +Revision ID: bac61f603e39 +Revises: 4086b3cffc01 +Create Date: 2016-07-27 09:31:54.200165 + +""" + +# revision identifiers, used by Alembic. +revision = 'bac61f603e39' +down_revision = '4086b3cffc01' + +from alembic import op +from sqlalchemy.engine import reflection + +import sqlalchemy as sa + +TABLE_NAME = 'tap_id_associations' + + +def upgrade(): + inspector = reflection.Inspector.from_engine(op.get_bind()) + fk_constraints = inspector.get_foreign_keys(TABLE_NAME) + for fk in fk_constraints: + op.drop_constraint(fk['name'], TABLE_NAME, type_='foreignkey') + + op.create_foreign_key('fk_tap_id_assoc_tap_service', TABLE_NAME, + 'tap_services', ['tap_service_id'], ['id'], + ondelete='SET NULL') + + op.alter_column(TABLE_NAME, 'taas_id', autoincrement=False, + existing_type=sa.INTEGER, nullable=False) + op.alter_column(TABLE_NAME, 'tap_service_id', + existing_type=sa.String(36), nullable=True) + op.create_unique_constraint('unique_taas_id', TABLE_NAME, + ['taas_id']) diff --git a/neutron_taas/db/taas_db.py b/neutron_taas/db/taas_db.py index 1275ebad..3309ed64 100644 --- a/neutron_taas/db/taas_db.py +++ b/neutron_taas/db/taas_db.py @@ -23,10 +23,10 @@ from neutron_lib import constants from neutron_lib.db import model_base from neutron_lib.plugins import directory from neutron_taas.extensions import taas +from oslo_config import cfg from oslo_log import log as logging from oslo_utils import uuidutils - LOG = logging.getLogger(__name__) @@ -68,12 +68,13 @@ class TapIdAssociation(model_base.BASEV2): __tablename__ = 'tap_id_associations' tap_service_id = sa.Column(sa.String(36), sa.ForeignKey("tap_services.id", - ondelete='CASCADE')) - taas_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) + ondelete='SET NULL'), + nullable=True) + taas_id = sa.Column(sa.Integer, primary_key=True, unique=True) tap_service = orm.relationship( TapService, backref=orm.backref("tap_service_id", - lazy="joined", cascade="delete"), + lazy="joined"), primaryjoin='TapService.id==TapIdAssociation.tap_service_id') @@ -147,14 +148,47 @@ class Taas_db_Mixin(taas.TaasPluginBase, base_db.CommonDbMixin): return self._make_tap_service_dict(tap_service_db) + def _rebuild_taas_id_allocation_range(self, context): + query = context.session.query( + TapIdAssociation).all() + + allocate_taas_id_list = [_q.taas_id for _q in query] + first_taas_id = cfg.CONF.taas.vlan_range_start + # Exclude range end + last_taas_id = cfg.CONF.taas.vlan_range_end + all_taas_id_set = set(range(first_taas_id, last_taas_id)) + vaild_taas_id_set = all_taas_id_set - set(allocate_taas_id_list) + + for _id in vaild_taas_id_set: + # new taas id + context.session.add(TapIdAssociation( + taas_id=_id)) + + def _allocate_taas_id_with_tap_service_id(self, context, tap_service_id): + query = context.session.query(TapIdAssociation).filter_by( + tap_service_id=None).first() + if not query: + self._rebuild_taas_id_allocation_range(context) + # try again + query = context.session.query(TapIdAssociation).filter_by( + tap_service_id=None).first() + + if query: + query.update({"tap_service_id": tap_service_id}) + return query + # not found + raise taas.TapServiceLimitReached() + def create_tap_id_association(self, context, tap_service_id): LOG.debug("create_tap_id_association() called") # create the TapIdAssociation object with context.session.begin(subtransactions=True): - tap_id_association_db = TapIdAssociation( - tap_service_id=tap_service_id - ) - context.session.add(tap_id_association_db) + # allocate Taas id. + # if conflict happened, it will raise db.DBDuplicateEntry. + # this will be retry request again in neutron controller framework. + # so we just make sure TapIdAssociation field taas_id is unique + tap_id_association_db = self._allocate_taas_id_with_tap_service_id( + context, tap_service_id) return self._make_tap_id_association_dict(tap_id_association_db) diff --git a/neutron_taas/services/taas/service_drivers/taas_rpc.py b/neutron_taas/services/taas/service_drivers/taas_rpc.py index 4a375bed..ea06561a 100644 --- a/neutron_taas/services/taas/service_drivers/taas_rpc.py +++ b/neutron_taas/services/taas/service_drivers/taas_rpc.py @@ -17,7 +17,6 @@ from neutron.common import rpc as n_rpc from neutron_lib import exceptions as n_exc from neutron_taas.common import topics -from neutron_taas.extensions import taas as taas_ex from neutron_taas.services.taas import service_drivers from neutron_taas.services.taas.service_drivers import taas_agent_api @@ -52,8 +51,7 @@ class TaasRpcDriver(service_drivers.TaasBaseDriver): tf['tap_service_id']) taas_id = (self.service_plugin.get_tap_id_association( context, - tap_service_id=ts['id'])['taas_id'] + - cfg.CONF.taas.vlan_range_start) + tap_service_id=ts['id']))['taas_id'] return taas_id def create_tap_service_precommit(self, context): @@ -71,15 +69,11 @@ class TaasRpcDriver(service_drivers.TaasBaseDriver): # Get taas id associated with the Tap Service ts = context.tap_service tap_id_association = context.tap_id_association - taas_vlan_id = (tap_id_association['taas_id'] + - cfg.CONF.taas.vlan_range_start) + taas_vlan_id = tap_id_association['taas_id'] port = self.service_plugin._get_port_details(context._plugin_context, ts['port_id']) host = port['binding:host_id'] - if taas_vlan_id > cfg.CONF.taas.vlan_range_end: - raise taas_ex.TapServiceLimitReached() - rpc_msg = {'tap_service': ts, 'taas_id': taas_vlan_id, 'port': port} @@ -99,8 +93,8 @@ class TaasRpcDriver(service_drivers.TaasBaseDriver): """ ts = context.tap_service tap_id_association = context.tap_id_association - taas_vlan_id = (tap_id_association['taas_id'] + - cfg.CONF.taas.vlan_range_start) + taas_vlan_id = tap_id_association['taas_id'] + try: port = self.service_plugin._get_port_details( context._plugin_context, diff --git a/neutron_taas/tests/unit/services/taas/test_taas_plugin.py b/neutron_taas/tests/unit/services/taas/test_taas_plugin.py index a00fdfdf..a01bcaa2 100644 --- a/neutron_taas/tests/unit/services/taas/test_taas_plugin.py +++ b/neutron_taas/tests/unit/services/taas/test_taas_plugin.py @@ -20,6 +20,7 @@ import testtools from neutron_lib import context from neutron_lib.utils import net as n_utils +from oslo_config import cfg from oslo_utils import uuidutils import neutron.common.rpc as n_rpc @@ -130,6 +131,33 @@ class TestTaasPlugin(testlib_api.SqlTestCase): with self.tap_service(): pass + def test_verify_taas_id_reused(self): + # make small range id + cfg.CONF.set_override("vlan_range_start", 1, group="taas") + cfg.CONF.set_override("vlan_range_end", 3, group="taas") + with self.tap_service() as ts_1, self.tap_service() as ts_2, \ + self.tap_service() as ts_3, self.tap_service() as ts_4: + ts_id_1 = ts_1['id'] + ts_id_2 = ts_2['id'] + ts_id_3 = ts_3['id'] + tap_id_assoc_1 = self._plugin.create_tap_id_association( + self._context, ts_id_1) + tap_id_assoc_2 = self._plugin.create_tap_id_association( + self._context, ts_id_2) + self.assertEqual(set([1, 2]), set([tap_id_assoc_1['taas_id'], + tap_id_assoc_2['taas_id']])) + with testtools.ExpectedException(taas_ext.TapServiceLimitReached): + self._plugin.create_tap_id_association( + self._context, + ts_4['id'] + ) + # free an tap_id and verify could reallocate same taas id + self._plugin.delete_tap_service(self._context, ts_id_1) + tap_id_assoc_3 = self._plugin.create_tap_id_association( + self._context, ts_id_3) + self.assertEqual(set([1, 2]), set([tap_id_assoc_3['taas_id'], + tap_id_assoc_2['taas_id']])) + def test_create_tap_service_wrong_tenant_id(self): self._port_details['tenant_id'] = 'other-tenant' with testtools.ExpectedException(taas_ext.PortDoesNotBelongToTenant), \