Fixes enable taas id reuse

Change-Id: I93e64eae7794ee4a315ddda2ea85457e8e9597be
closes-Bug: #1501953
This commit is contained in:
cheng 2016-07-30 04:18:53 -04:00
parent e15cbf3892
commit 86e307ea5c
5 changed files with 125 additions and 19 deletions

View File

@ -1 +1 @@
4086b3cffc01
bac61f603e39

View File

@ -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'])

View File

@ -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)

View File

@ -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,

View File

@ -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), \