[apic_aim] ML2Plus SubnetPool and AddressScope support
Add mechanism driver and extension driver support the the SubnetPool and AddressScope resources, including ensure_tenant. Change-Id: Icb00eb223d1e503f946ffba54de51e89ba260a1d
This commit is contained in:
parent
2498dc10eb
commit
a6b918934a
@ -19,6 +19,70 @@ import six
|
||||
from neutron.plugins.ml2 import driver_api
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class SubnetPoolContext(object):
|
||||
"""Context passed to MechanismDrivers for changes to subnet pool
|
||||
resources.
|
||||
|
||||
A SubnetPoolContext instance wraps a subnet pool resource. It
|
||||
provides helper methods for accessing other relevant
|
||||
information. Results from expensive operations are cached so that
|
||||
other MechanismDrivers can freely access the same information.
|
||||
"""
|
||||
|
||||
@abc.abstractproperty
|
||||
def current(self):
|
||||
"""Return the subnet pool in its current configuration.
|
||||
|
||||
Return the subnet pool with all its properties 'current' at
|
||||
the time the context was established.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractproperty
|
||||
def original(self):
|
||||
"""Return the subnet pool in its original configuration.
|
||||
|
||||
Return the subnet pool, with all its properties set to their
|
||||
original values prior to a call to update_address_scope. Method is
|
||||
only valid within calls to update_address_scope_precommit and
|
||||
update_address_scope_postcommit.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class AddressScopeContext(object):
|
||||
"""Context passed to MechanismDrivers for changes to address scope
|
||||
resources.
|
||||
|
||||
An AddressScopeContext instance wraps an address scope
|
||||
resource. It provides helper methods for accessing other relevant
|
||||
information. Results from expensive operations are cached so that
|
||||
other MechanismDrivers can freely access the same information.
|
||||
"""
|
||||
|
||||
@abc.abstractproperty
|
||||
def current(self):
|
||||
"""Return the address scope in its current configuration.
|
||||
|
||||
Return the address scope with all its properties 'current' at
|
||||
the time the context was established.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractproperty
|
||||
def original(self):
|
||||
"""Return the address scope in its original configuration.
|
||||
|
||||
Return the address scope, with all its properties set to their
|
||||
original values prior to a call to update_address_scope. Method is
|
||||
only valid within calls to update_address_scope_precommit and
|
||||
update_address_scope_postcommit.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class MechanismDriver(driver_api.MechanismDriver):
|
||||
|
||||
@ -39,9 +103,280 @@ class MechanismDriver(driver_api.MechanismDriver):
|
||||
"""
|
||||
pass
|
||||
|
||||
# TODO(rkukura): Add precommit/postcommit calls for address_scope,
|
||||
# subnet_pool, and other resources.
|
||||
def create_subnetpool_precommit(self, context):
|
||||
"""Allocate resources for a new subnet pool.
|
||||
|
||||
:param context: SubnetPoolContext instance describing the new
|
||||
subnet pool.
|
||||
|
||||
Create a new subnet pool, allocating resources as necessary in
|
||||
the database. Called inside transaction context on
|
||||
session. Call cannot block. Raising an exception will result
|
||||
in a rollback of the current transaction.
|
||||
"""
|
||||
pass
|
||||
|
||||
def create_subnetpool_postcommit(self, context):
|
||||
"""Create a subnet pool.
|
||||
|
||||
:param context: SubnetPoolContext instance describing the new
|
||||
subnet pool.
|
||||
|
||||
Called after the transaction commits. Call can block, though
|
||||
will block the entire process so care should be taken to not
|
||||
drastically affect performance. Raising an exception will
|
||||
cause the deletion of the resource.
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_subnetpool_precommit(self, context):
|
||||
"""Update resources of a subnet pool.
|
||||
|
||||
:param context: SubnetPoolContext instance describing the new
|
||||
state of the subnet pool, as well as the original state prior
|
||||
to the update_subnetpool call.
|
||||
|
||||
Update values of a subnet pool, updating the associated
|
||||
resources in the database. Called inside transaction context
|
||||
on session. Raising an exception will result in rollback of
|
||||
the transaction.
|
||||
|
||||
update_subnetpool_precommit is called for all changes to the
|
||||
subnet pool state. It is up to the mechanism driver to ignore
|
||||
state or state changes that it does not know or care about.
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_subnetpool_postcommit(self, context):
|
||||
"""Update a subnet pool.
|
||||
|
||||
:param context: SubnetPoolContext instance describing the new
|
||||
state of the subnet pool, as well as the original state prior
|
||||
to the update_subnetpool call.
|
||||
|
||||
Called after the transaction commits. Call can block, though
|
||||
will block the entire process so care should be taken to not
|
||||
drastically affect performance. Raising an exception will
|
||||
cause the deletion of the resource.
|
||||
|
||||
update_subnetpool_postcommit is called for all changes to the
|
||||
subnet pool state. It is up to the mechanism driver to ignore
|
||||
state or state changes that it does not know or care about.
|
||||
"""
|
||||
pass
|
||||
|
||||
def delete_subnetpool_precommit(self, context):
|
||||
"""Delete resources for a subnet pool.
|
||||
|
||||
:param context: SubnetPoolContext instance describing the
|
||||
current state of the subnet pool, prior to the call to delete
|
||||
it.
|
||||
|
||||
Delete subnet pool resources previously allocated by this
|
||||
mechanism driver for a subnet pool. Called inside transaction
|
||||
context on session. Runtime errors are not expected, but
|
||||
raising an exception will result in rollback of the
|
||||
transaction.
|
||||
"""
|
||||
pass
|
||||
|
||||
def delete_subnetpool_postcommit(self, context):
|
||||
"""Delete a subnet pool.
|
||||
|
||||
:param context: SubnetPoolContext instance describing the
|
||||
current state of the subnet pool, prior to the call to delete
|
||||
it.
|
||||
|
||||
Called after the transaction commits. Call can block, though
|
||||
will block the entire process so care should be taken to not
|
||||
drastically affect performance. Runtime errors are not
|
||||
expected, and will not prevent the resource from being
|
||||
deleted.
|
||||
"""
|
||||
pass
|
||||
|
||||
def create_address_scope_precommit(self, context):
|
||||
"""Allocate resources for a new address scope.
|
||||
|
||||
:param context: AddressScopeContext instance describing the
|
||||
new address scope.
|
||||
|
||||
Create a new address scope, allocating resources as necessary
|
||||
in the database. Called inside transaction context on
|
||||
session. Call cannot block. Raising an exception will result
|
||||
in a rollback of the current transaction.
|
||||
"""
|
||||
pass
|
||||
|
||||
def create_address_scope_postcommit(self, context):
|
||||
"""Create an address scope.
|
||||
|
||||
:param context: AddressScopeContext instance describing the
|
||||
new address scope.
|
||||
|
||||
Called after the transaction commits. Call can block, though
|
||||
will block the entire process so care should be taken to not
|
||||
drastically affect performance. Raising an exception will
|
||||
cause the deletion of the resource.
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_address_scope_precommit(self, context):
|
||||
"""Update resources of an address scope.
|
||||
|
||||
:param context: AddressScopeContext instance describing the
|
||||
new state of the address scope, as well as the original state
|
||||
prior to the update_address_scope call.
|
||||
|
||||
Update values of an address scope, updating the associated
|
||||
resources in the database. Called inside transaction context
|
||||
on session. Raising an exception will result in rollback of
|
||||
the transaction.
|
||||
|
||||
update_address_scope_precommit is called for all changes to
|
||||
the address scope state. It is up to the mechanism driver to
|
||||
ignore state or state changes that it does not know or care
|
||||
about.
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_address_scope_postcommit(self, context):
|
||||
"""Update an address scope.
|
||||
|
||||
:param context: AddressScopeContext instance describing the
|
||||
new state of the address scope, as well as the original state
|
||||
prior to the update_address_scope call.
|
||||
|
||||
Called after the transaction commits. Call can block, though
|
||||
will block the entire process so care should be taken to not
|
||||
drastically affect performance. Raising an exception will
|
||||
cause the deletion of the resource.
|
||||
|
||||
update_address_scope_postcommit is called for all changes to
|
||||
the address scope state. It is up to the mechanism driver to
|
||||
ignore state or state changes that it does not know or care
|
||||
about.
|
||||
"""
|
||||
pass
|
||||
|
||||
def delete_address_scope_precommit(self, context):
|
||||
"""Delete resources for an address scope.
|
||||
|
||||
:param context: AddressScopeContext instance describing the
|
||||
current state of the address scope, prior to the call to
|
||||
delete it.
|
||||
|
||||
Delete address scope resources previously allocated by this
|
||||
mechanism driver for an address scope. Called inside
|
||||
transaction context on session. Runtime errors are not
|
||||
expected, but raising an exception will result in rollback of
|
||||
the transaction.
|
||||
"""
|
||||
pass
|
||||
|
||||
def delete_address_scope_postcommit(self, context):
|
||||
"""Delete an address scope.
|
||||
|
||||
:param context: AddressScopeContext instance describing the
|
||||
current state of the address scope, prior to the call to
|
||||
delete it.
|
||||
|
||||
Called after the transaction commits. Call can block, though
|
||||
will block the entire process so care should be taken to not
|
||||
drastically affect performance. Runtime errors are not
|
||||
expected, and will not prevent the resource from being
|
||||
deleted.
|
||||
"""
|
||||
pass
|
||||
|
||||
# REVISIT(rkukura): Add precommit/postcommit calls for other
|
||||
# resources implemented in ML2, such as security groups and
|
||||
# security group rules?
|
||||
|
||||
|
||||
# TODO(rkukura): Extend ExtensionDriver for address_scope,
|
||||
# subnet_pool, and other resources.
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ExtensionDriver(driver_api.ExtensionDriver):
|
||||
|
||||
def process_create_subnetpool(self, plugin_context, data, result):
|
||||
"""Process extended attributes for create subnet pool.
|
||||
|
||||
:param plugin_context: plugin request context
|
||||
:param data: dictionary of incoming subnet pool data
|
||||
:param result: subnet pool dictionary to extend
|
||||
|
||||
Called inside transaction context on plugin_context.session to
|
||||
validate and persist any extended subnet pool attributes
|
||||
defined by this driver. Extended attribute values must also be
|
||||
added to result.
|
||||
"""
|
||||
pass
|
||||
|
||||
def process_update_subnetpool(self, plugin_context, data, result):
|
||||
"""Process extended attributes for update subnet pool.
|
||||
|
||||
:param plugin_context: plugin request context
|
||||
:param data: dictionary of incoming subnet pool data
|
||||
:param result: subnet pool dictionary to extend
|
||||
|
||||
Called inside transaction context on plugin_context.session to
|
||||
validate and update any extended subnet pool attributes
|
||||
defined by this driver. Extended attribute values, whether
|
||||
updated or not, must also be added to result.
|
||||
"""
|
||||
pass
|
||||
|
||||
def extend_subnetpool_dict(self, session, base_model, result):
|
||||
"""Add extended attributes to subnet pool dictionary.
|
||||
|
||||
:param session: database session
|
||||
:param base_model: subnet pool model data
|
||||
:param result: subnet pool dictionary to extend
|
||||
|
||||
Called inside transaction context on session to add any
|
||||
extended attributes defined by this driver to a subnet pool
|
||||
dictionary to be used for mechanism driver calls and/or
|
||||
returned as the result of a subnet pool operation.
|
||||
"""
|
||||
pass
|
||||
|
||||
def process_create_address_scope(self, plugin_context, data, result):
|
||||
"""Process extended attributes for create address scope.
|
||||
|
||||
:param plugin_context: plugin request context
|
||||
:param data: dictionary of incoming address scope data
|
||||
:param result: address scope dictionary to extend
|
||||
|
||||
Called inside transaction context on plugin_context.session to
|
||||
validate and persist any extended address scope attributes
|
||||
defined by this driver. Extended attribute values must also be
|
||||
added to result.
|
||||
"""
|
||||
pass
|
||||
|
||||
def process_update_address_scope(self, plugin_context, data, result):
|
||||
"""Process extended attributes for update address scope.
|
||||
|
||||
:param plugin_context: plugin request context
|
||||
:param data: dictionary of incoming address scope data
|
||||
:param result: address scope dictionary to extend
|
||||
|
||||
Called inside transaction context on plugin_context.session to
|
||||
validate and update any extended address scope attributes
|
||||
defined by this driver. Extended attribute values, whether
|
||||
updated or not, must also be added to result.
|
||||
"""
|
||||
pass
|
||||
|
||||
def extend_address_scope_dict(self, session, base_model, result):
|
||||
"""Add extended attributes to address scope dictionary.
|
||||
|
||||
:param session: database session
|
||||
:param base_model: address scope model data
|
||||
:param result: address scope dictionary to extend
|
||||
|
||||
Called inside transaction context on session to add any
|
||||
extended attributes defined by this driver to an address scope
|
||||
dictionary to be used for mechanism driver calls and/or
|
||||
returned as the result of an address scope operation.
|
||||
"""
|
||||
pass
|
||||
|
54
gbpservice/neutron/plugins/ml2plus/driver_context.py
Normal file
54
gbpservice/neutron/plugins/ml2plus/driver_context.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (c) 2016 Cisco Systems 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.ml2 import driver_context as ml2_context
|
||||
|
||||
from gbpservice.neutron.plugins.ml2plus import driver_api as api
|
||||
|
||||
|
||||
class SubnetPoolContext(ml2_context.MechanismDriverContext,
|
||||
api.SubnetPoolContext):
|
||||
|
||||
def __init__(self, plugin, plugin_context, subnetpool,
|
||||
original_subnetpool=None):
|
||||
super(SubnetPoolContext, self).__init__(plugin, plugin_context)
|
||||
self._subnetpool = subnetpool
|
||||
self._original_subnetpool = original_subnetpool
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
return self._subnetpool
|
||||
|
||||
@property
|
||||
def original(self):
|
||||
return self._original_subnetpool
|
||||
|
||||
|
||||
class AddressScopeContext(ml2_context.MechanismDriverContext,
|
||||
api.AddressScopeContext):
|
||||
|
||||
def __init__(self, plugin, plugin_context, address_scope,
|
||||
original_address_scope=None):
|
||||
super(AddressScopeContext, self).__init__(plugin, plugin_context)
|
||||
self._address_scope = address_scope
|
||||
self._original_address_scope = original_address_scope
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
return self._address_scope
|
||||
|
||||
@property
|
||||
def original(self):
|
||||
return self._original_address_scope
|
@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2016 Cisco Systems 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.extensions import address_scope
|
||||
|
||||
|
||||
class Patched_address_scope(address_scope.Address_scope):
|
||||
def update_attributes_map(self, attributes):
|
||||
super(Patched_address_scope, self).update_attributes_map(
|
||||
attributes,
|
||||
extension_attrs_map=address_scope.RESOURCE_ATTRIBUTE_MAP)
|
@ -16,9 +16,11 @@
|
||||
from gbpservice.neutron.plugins.ml2plus import driver_api
|
||||
|
||||
from neutron._i18n import _LE
|
||||
from neutron._i18n import _LI
|
||||
from neutron.plugins.ml2.common import exceptions as ml2_exc
|
||||
from neutron.plugins.ml2 import managers
|
||||
from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@ -28,6 +30,37 @@ class MechanismManager(managers.MechanismManager):
|
||||
def __init__(self):
|
||||
super(MechanismManager, self).__init__()
|
||||
|
||||
def _call_on_extended_drivers(self, method_name, context,
|
||||
continue_on_failure=False):
|
||||
"""Call a method on all extended mechanism drivers.
|
||||
|
||||
:param method_name: name of the method to call
|
||||
:param context: context parameter to pass to each method call
|
||||
:param continue_on_failure: whether or not to continue to call
|
||||
all mechanism drivers once one has raised an exception
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver call fails.
|
||||
|
||||
"""
|
||||
error = False
|
||||
for driver in self.ordered_mech_drivers:
|
||||
if isinstance(driver.obj, driver_api.MechanismDriver):
|
||||
try:
|
||||
getattr(driver.obj, method_name)(context)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("Mechanism driver '%(name)s' failed in "
|
||||
"%(method)s"),
|
||||
{'name': driver.name, 'method': method_name}
|
||||
)
|
||||
error = True
|
||||
if not continue_on_failure:
|
||||
break
|
||||
if error:
|
||||
raise ml2_exc.MechanismDriverError(
|
||||
method=method_name
|
||||
)
|
||||
|
||||
def ensure_tenant(self, plugin_context, tenant_id):
|
||||
for driver in self.ordered_mech_drivers:
|
||||
if isinstance(driver.obj, driver_api.MechanismDriver):
|
||||
@ -37,3 +70,108 @@ class MechanismManager(managers.MechanismManager):
|
||||
LOG.exception(_LE("Mechanism driver '%s' failed in "
|
||||
"ensure_tenant"), driver.name)
|
||||
raise ml2_exc.MechanismDriverError(method="ensure_tenant")
|
||||
|
||||
def create_subnetpool_precommit(self, context):
|
||||
self._call_on_extended_drivers("create_subnetpool_precommit",
|
||||
context)
|
||||
|
||||
def create_subnetpool_postcommit(self, context):
|
||||
self._call_on_extended_drivers("create_subnetpool_postcommit",
|
||||
context)
|
||||
|
||||
def update_subnetpool_precommit(self, context):
|
||||
self._call_on_extended_drivers("update_subnetpool_precommit",
|
||||
context)
|
||||
|
||||
def update_subnetpool_postcommit(self, context):
|
||||
self._call_on_extended_drivers("update_subnetpool_postcommit",
|
||||
context)
|
||||
|
||||
def delete_subnetpool_precommit(self, context):
|
||||
self._call_on_extended_drivers("delete_subnetpool_precommit",
|
||||
context)
|
||||
|
||||
def delete_subnetpool_postcommit(self, context):
|
||||
self._call_on_extended_drivers("delete_subnetpool_postcommit",
|
||||
context)
|
||||
|
||||
def create_address_scope_precommit(self, context):
|
||||
self._call_on_extended_drivers("create_address_scope_precommit",
|
||||
context)
|
||||
|
||||
def create_address_scope_postcommit(self, context):
|
||||
self._call_on_extended_drivers("create_address_scope_postcommit",
|
||||
context)
|
||||
|
||||
def update_address_scope_precommit(self, context):
|
||||
self._call_on_extended_drivers("update_address_scope_precommit",
|
||||
context)
|
||||
|
||||
def update_address_scope_postcommit(self, context):
|
||||
self._call_on_extended_drivers("update_address_scope_postcommit",
|
||||
context)
|
||||
|
||||
def delete_address_scope_precommit(self, context):
|
||||
self._call_on_extended_drivers("delete_address_scope_precommit",
|
||||
context)
|
||||
|
||||
def delete_address_scope_postcommit(self, context):
|
||||
self._call_on_extended_drivers("delete_address_scope_postcommit",
|
||||
context)
|
||||
|
||||
|
||||
class ExtensionManager(managers.ExtensionManager):
|
||||
|
||||
def __init__(self):
|
||||
super(ExtensionManager, self).__init__()
|
||||
|
||||
def _call_on_extended_drivers(self, method_name, plugin_context, data,
|
||||
result):
|
||||
"""Call a method on all extended extension drivers."""
|
||||
for driver in self.ordered_ext_drivers:
|
||||
if isinstance(driver.obj, driver_api.ExtensionDriver):
|
||||
try:
|
||||
getattr(driver.obj, method_name)(plugin_context, data,
|
||||
result)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.info(_LI("Extension driver '%(name)s' failed in "
|
||||
"%(method)s"),
|
||||
{'name': driver.name, 'method': method_name})
|
||||
|
||||
def _call_on_dict_extended_drivers(self, method_name, session, base_model,
|
||||
result):
|
||||
for driver in self.ordered_ext_drivers:
|
||||
if isinstance(driver.obj, driver_api.ExtensionDriver):
|
||||
try:
|
||||
getattr(driver.obj, method_name)(session, base_model,
|
||||
result)
|
||||
except Exception:
|
||||
LOG.error(_LE("Extension driver '%(name)s' failed in "
|
||||
"%(method)s"),
|
||||
{'name': driver.name, 'method': method_name})
|
||||
raise ml2_exc.ExtensionDriverError(driver=driver.name)
|
||||
|
||||
def process_create_subnetpool(self, plugin_context, data, result):
|
||||
self._call_on_extended_drivers("process_create_subnetpool",
|
||||
plugin_context, data, result)
|
||||
|
||||
def process_update_subnetpool(self, plugin_context, data, result):
|
||||
self._call_on_extended_drivers("process_update_subnetpool",
|
||||
plugin_context, data, result)
|
||||
|
||||
def extend_subnetpool_dict(self, session, base_model, result):
|
||||
self._call_on_dict_extended_drivers("extend_subnetpool_dict",
|
||||
session, base_model, result)
|
||||
|
||||
def process_create_address_scope(self, plugin_context, data, result):
|
||||
self._call_on_extended_drivers("process_create_address_scope",
|
||||
plugin_context, data, result)
|
||||
|
||||
def process_update_address_scope(self, plugin_context, data, result):
|
||||
self._call_on_extended_drivers("process_update_address_scope",
|
||||
plugin_context, data, result)
|
||||
|
||||
def extend_address_scope_dict(self, session, base_model, result):
|
||||
self._call_on_dict_extended_drivers("extend_address_scope_dict",
|
||||
session, base_model, result)
|
||||
|
32
gbpservice/neutron/plugins/ml2plus/patch_neutron.py
Normal file
32
gbpservice/neutron/plugins/ml2plus/patch_neutron.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2016 Cisco Systems 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 import extensions
|
||||
|
||||
from gbpservice.neutron.plugins.ml2plus import extension_overrides
|
||||
|
||||
# Monkeypatch Neutron to allow overriding its own extension
|
||||
# descriptors. Note that extension descriptor classes cannot be
|
||||
# monkeypatched directly because they are loaded explicitly by file
|
||||
# name and then used immediately.
|
||||
|
||||
_real_get_extensions_path = extensions.get_extensions_path
|
||||
|
||||
|
||||
def get_extensions_path(service_plugins=None):
|
||||
path = _real_get_extensions_path(service_plugins)
|
||||
return extension_overrides.__path__[0] + ':' + path
|
||||
|
||||
extensions.get_extensions_path = get_extensions_path
|
@ -13,17 +13,24 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron._i18n import _LE
|
||||
from neutron._i18n import _LI
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import securitygroups_db
|
||||
from neutron.extensions import address_scope as as_ext
|
||||
from neutron.plugins.ml2.common import exceptions as ml2_exc
|
||||
from neutron.plugins.ml2 import managers as ml2_managers
|
||||
from neutron.plugins.ml2 import plugin as ml2_plugin
|
||||
from neutron.quota import resource_registry
|
||||
from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
from sqlalchemy import inspect
|
||||
|
||||
from gbpservice.neutron.plugins.ml2plus import driver_context
|
||||
from gbpservice.neutron.plugins.ml2plus import managers
|
||||
from gbpservice.neutron.plugins.ml2plus import patch_neutron # noqa
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@ -59,7 +66,7 @@ class Ml2PlusPlugin(ml2_plugin.Ml2Plugin):
|
||||
LOG.info(_LI("Ml2Plus initializing"))
|
||||
# First load drivers, then initialize DB, then initialize drivers
|
||||
self.type_manager = ml2_managers.TypeManager()
|
||||
self.extension_manager = ml2_managers.ExtensionManager()
|
||||
self.extension_manager = managers.ExtensionManager()
|
||||
self.mechanism_manager = managers.MechanismManager()
|
||||
super(ml2_plugin.Ml2Plugin, self).__init__()
|
||||
self.type_manager.initialize()
|
||||
@ -71,6 +78,12 @@ class Ml2PlusPlugin(ml2_plugin.Ml2Plugin):
|
||||
self._verify_service_plugins_requirements()
|
||||
LOG.info(_LI("Modular L2 Plugin (extended) initialization complete"))
|
||||
|
||||
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
|
||||
attributes.SUBNETPOOLS, ['_ml2_md_extend_subnetpool_dict'])
|
||||
|
||||
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
|
||||
as_ext.ADDRESS_SCOPES, ['_ml2_md_extend_address_scope_dict'])
|
||||
|
||||
def _ml2_md_extend_network_dict(self, result, netdb):
|
||||
session = inspect(netdb).session
|
||||
with session.begin(subtransactions=True):
|
||||
@ -87,6 +100,29 @@ class Ml2PlusPlugin(ml2_plugin.Ml2Plugin):
|
||||
self.extension_manager.extend_subnet_dict(
|
||||
session, subnetdb, result)
|
||||
|
||||
def _ml2_md_extend_subnetpool_dict(self, result, subnetpooldb):
|
||||
session = inspect(subnetpooldb).session
|
||||
with session.begin(subtransactions=True):
|
||||
self.extension_manager.extend_subnetpool_dict(
|
||||
session, subnetpooldb, result)
|
||||
|
||||
def _ml2_md_extend_address_scope_dict(self, result, address_scopedb):
|
||||
session = inspect(address_scopedb).session
|
||||
with session.begin(subtransactions=True):
|
||||
self.extension_manager.extend_address_scope_dict(
|
||||
session, address_scopedb, result)
|
||||
|
||||
# Base version does not call _apply_dict_extend_functions()
|
||||
def _make_address_scope_dict(self, address_scope, fields=None):
|
||||
res = {'id': address_scope['id'],
|
||||
'name': address_scope['name'],
|
||||
'tenant_id': address_scope['tenant_id'],
|
||||
'shared': address_scope['shared'],
|
||||
'ip_version': address_scope['ip_version']}
|
||||
self._apply_dict_extend_functions(as_ext.ADDRESS_SCOPES, res,
|
||||
address_scope)
|
||||
return self._fields(res, fields)
|
||||
|
||||
def create_network(self, context, network):
|
||||
self._ensure_tenant(context, network[attributes.NETWORK])
|
||||
return super(Ml2PlusPlugin, self).create_network(context, network)
|
||||
@ -117,9 +153,110 @@ class Ml2PlusPlugin(ml2_plugin.Ml2Plugin):
|
||||
return super(Ml2PlusPlugin, self).create_port_bulk(context,
|
||||
ports)
|
||||
|
||||
# TODO(rkukura): Override address_scope, subnet_pool, and any
|
||||
# other needed resources to ensure tenant and invoke mechanism and
|
||||
# extension drivers.
|
||||
def create_subnetpool(self, context, subnetpool):
|
||||
self._ensure_tenant(context, subnetpool[attributes.SUBNETPOOL])
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
result = super(Ml2PlusPlugin, self).create_subnetpool(context,
|
||||
subnetpool)
|
||||
self.extension_manager.process_create_subnetpool(
|
||||
context, subnetpool[attributes.SUBNETPOOL], result)
|
||||
mech_context = driver_context.SubnetPoolContext(
|
||||
self, context, result)
|
||||
self.mechanism_manager.create_subnetpool_precommit(mech_context)
|
||||
try:
|
||||
self.mechanism_manager.create_subnetpool_postcommit(mech_context)
|
||||
except ml2_exc.MechanismDriverError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("mechanism_manager.create_subnetpool_postcommit "
|
||||
"failed, deleting subnetpool '%s'"),
|
||||
result['id'])
|
||||
self.delete_subnetpool(context, result['id'])
|
||||
return result
|
||||
|
||||
# REVISIT(rkukura): Is create_subnetpool_bulk() needed?
|
||||
|
||||
def update_subnetpool(self, context, id, subnetpool):
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
original_subnetpool = super(Ml2PlusPlugin, self).get_subnetpool(
|
||||
context, id)
|
||||
updated_subnetpool = super(Ml2PlusPlugin, self).update_subnetpool(
|
||||
context, id, subnetpool)
|
||||
self.extension_manager.process_update_subnetpool(
|
||||
context, subnetpool[attributes.SUBNETPOOL],
|
||||
updated_subnetpool)
|
||||
mech_context = driver_context.SubnetPoolContext(
|
||||
self, context, updated_subnetpool,
|
||||
original_subnetpool=original_subnetpool)
|
||||
self.mechanism_manager.update_subnetpool_precommit(mech_context)
|
||||
self.mechanism_manager.update_subnetpool_postcommit(mech_context)
|
||||
return updated_subnetpool
|
||||
|
||||
def delete_subnetpool(self, context, id):
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
subnetpool = super(Ml2PlusPlugin, self).get_subnetpool(context, id)
|
||||
super(Ml2PlusPlugin, self).delete_subnetpool(context, id)
|
||||
mech_context = driver_context.SubnetPoolContext(
|
||||
self, context, subnetpool)
|
||||
self.mechanism_manager.delete_subnetpool_precommit(mech_context)
|
||||
self.mechanism_manager.delete_subnetpool_postcommit(mech_context)
|
||||
|
||||
def create_address_scope(self, context, address_scope):
|
||||
self._ensure_tenant(context, address_scope[as_ext.ADDRESS_SCOPE])
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
result = super(Ml2PlusPlugin, self).create_address_scope(
|
||||
context, address_scope)
|
||||
self.extension_manager.process_create_address_scope(
|
||||
context, address_scope[as_ext.ADDRESS_SCOPE], result)
|
||||
mech_context = driver_context.AddressScopeContext(
|
||||
self, context, result)
|
||||
self.mechanism_manager.create_address_scope_precommit(
|
||||
mech_context)
|
||||
try:
|
||||
self.mechanism_manager.create_address_scope_postcommit(
|
||||
mech_context)
|
||||
except ml2_exc.MechanismDriverError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("mechanism_manager.create_address_scope_"
|
||||
"postcommit failed, deleting address_scope"
|
||||
" '%s'"),
|
||||
result['id'])
|
||||
self.delete_address_scope(context, result['id'])
|
||||
return result
|
||||
|
||||
# REVISIT(rkukura): Is create_address_scope_bulk() needed?
|
||||
|
||||
def update_address_scope(self, context, id, address_scope):
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
original_address_scope = super(Ml2PlusPlugin,
|
||||
self).get_address_scope(context, id)
|
||||
updated_address_scope = super(Ml2PlusPlugin,
|
||||
self).update_address_scope(
|
||||
context, id, address_scope)
|
||||
self.extension_manager.process_update_address_scope(
|
||||
context, address_scope[as_ext.ADDRESS_SCOPE],
|
||||
updated_address_scope)
|
||||
mech_context = driver_context.AddressScopeContext(
|
||||
self, context, updated_address_scope,
|
||||
original_address_scope=original_address_scope)
|
||||
self.mechanism_manager.update_address_scope_precommit(mech_context)
|
||||
self.mechanism_manager.update_address_scope_postcommit(mech_context)
|
||||
return updated_address_scope
|
||||
|
||||
def delete_address_scope(self, context, id):
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
address_scope = super(Ml2PlusPlugin, self).get_address_scope(
|
||||
context, id)
|
||||
super(Ml2PlusPlugin, self).delete_address_scope(context, id)
|
||||
mech_context = driver_context.AddressScopeContext(
|
||||
self, context, address_scope)
|
||||
self.mechanism_manager.delete_address_scope_precommit(mech_context)
|
||||
self.mechanism_manager.delete_address_scope_postcommit(mech_context)
|
||||
|
||||
def _ensure_tenant(self, context, resource):
|
||||
tenant_id = resource['tenant_id']
|
||||
|
@ -0,0 +1,176 @@
|
||||
# Copyright (c) 2016 Cisco Systems 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 import extensions
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.db import address_scope_db as as_db
|
||||
from neutron.db import model_base
|
||||
from neutron.db import models_v2
|
||||
import oslo_db.sqlalchemy.session
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
|
||||
from gbpservice.neutron.plugins.ml2plus import driver_api
|
||||
from gbpservice.neutron.tests.unit.plugins.ml2plus import (
|
||||
extensions as test_extensions)
|
||||
|
||||
|
||||
class TestExtensionDriverBase(driver_api.ExtensionDriver):
|
||||
_supported_extension_aliases = 'ml2plus_fake_extension'
|
||||
|
||||
def initialize(self):
|
||||
# REVISIT(rkukura): Needed?
|
||||
extensions.append_api_extensions_path(test_extensions.__path__)
|
||||
|
||||
@property
|
||||
def extension_alias(self):
|
||||
return self._supported_extension_aliases
|
||||
|
||||
|
||||
class TestExtensionDriver(TestExtensionDriverBase):
|
||||
def initialize(self):
|
||||
super(TestExtensionDriver, self).initialize()
|
||||
self.subnetpool_extension = 'Test_SubnetPool_Extension'
|
||||
self.address_scope_extension = 'Test_AddressScope_Extension'
|
||||
|
||||
def _check_create(self, session, data, result):
|
||||
assert(isinstance(session, oslo_db.sqlalchemy.session.Session))
|
||||
assert(isinstance(data, dict))
|
||||
assert('id' not in data)
|
||||
assert(isinstance(result, dict))
|
||||
assert(result['id'] is not None)
|
||||
|
||||
def _check_update(self, session, data, result):
|
||||
assert(isinstance(session, oslo_db.sqlalchemy.session.Session))
|
||||
assert(isinstance(data, dict))
|
||||
assert(isinstance(result, dict))
|
||||
assert(result['id'] is not None)
|
||||
|
||||
def _check_extend(self, session, result, db_entry,
|
||||
expected_db_entry_class):
|
||||
assert(isinstance(session, oslo_db.sqlalchemy.session.Session))
|
||||
assert(isinstance(result, dict))
|
||||
assert(result['id'] is not None)
|
||||
assert(isinstance(db_entry, expected_db_entry_class))
|
||||
assert(db_entry.id == result['id'])
|
||||
|
||||
def process_create_subnetpool(self, plugin_context, data, result):
|
||||
session = plugin_context.session
|
||||
self._check_create(session, data, result)
|
||||
result['subnetpool_extension'] = self.subnetpool_extension + '_create'
|
||||
|
||||
def process_update_subnetpool(self, plugin_context, data, result):
|
||||
session = plugin_context.session
|
||||
self._check_update(session, data, result)
|
||||
self.subnetpool_extension = data['subnetpool_extension']
|
||||
result['subnetpool_extension'] = self.subnetpool_extension + '_update'
|
||||
|
||||
def extend_subnetpool_dict(self, session, subnetpool_db, result):
|
||||
self._check_extend(session, result, subnetpool_db,
|
||||
models_v2.SubnetPool)
|
||||
result['subnetpool_extension'] = self.subnetpool_extension + '_extend'
|
||||
|
||||
def process_create_address_scope(self, plugin_context, data, result):
|
||||
session = plugin_context.session
|
||||
self._check_create(session, data, result)
|
||||
result['address_scope_extension'] = (self.address_scope_extension +
|
||||
'_create')
|
||||
|
||||
def process_update_address_scope(self, plugin_context, data, result):
|
||||
session = plugin_context.session
|
||||
self._check_update(session, data, result)
|
||||
self.address_scope_extension = data['address_scope_extension']
|
||||
result['address_scope_extension'] = (self.address_scope_extension +
|
||||
'_update')
|
||||
|
||||
def extend_address_scope_dict(self, session, address_scope_db, result):
|
||||
self._check_extend(session, result, address_scope_db,
|
||||
as_db.AddressScope)
|
||||
result['address_scope_extension'] = (self.address_scope_extension +
|
||||
'_extend')
|
||||
|
||||
|
||||
class TestSubnetPoolExtension(model_base.BASEV2):
|
||||
subnetpool_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('subnetpools.id',
|
||||
ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
value = sa.Column(sa.String(64))
|
||||
subnetpool = orm.relationship(
|
||||
models_v2.SubnetPool,
|
||||
backref=orm.backref('extension', cascade='delete', uselist=False))
|
||||
|
||||
|
||||
class TestAddressScopeExtension(model_base.BASEV2):
|
||||
address_scope_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('address_scopes.id',
|
||||
ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
value = sa.Column(sa.String(64))
|
||||
address_scope = orm.relationship(
|
||||
as_db.AddressScope,
|
||||
backref=orm.backref('extension', cascade='delete', uselist=False))
|
||||
|
||||
|
||||
class TestDBExtensionDriver(TestExtensionDriverBase):
|
||||
def _get_value(self, data, key):
|
||||
value = data[key]
|
||||
if not attributes.is_attr_set(value):
|
||||
value = ''
|
||||
return value
|
||||
|
||||
def process_create_subnetpool(self, plugin_context, data, result):
|
||||
session = plugin_context.session
|
||||
value = self._get_value(data, 'subnetpool_extension')
|
||||
record = TestSubnetPoolExtension(subnetpool_id=result['id'],
|
||||
value=value)
|
||||
session.add(record)
|
||||
result['subnetpool_extension'] = value
|
||||
|
||||
def process_update_subnetpool(self, plugin_context, data, result):
|
||||
session = plugin_context.session
|
||||
record = (session.query(TestSubnetPoolExtension).
|
||||
filter_by(subnetpool_id=result['id']).one())
|
||||
value = data.get('subnetpool_extension')
|
||||
if value and value != record.value:
|
||||
record.value = value
|
||||
result['subnetpool_extension'] = record.value
|
||||
|
||||
def extend_subnetpool_dict(self, session, subnetpool_db, result):
|
||||
value = (subnetpool_db.extension.value
|
||||
if subnetpool_db.extension else '')
|
||||
result['subnetpool_extension'] = value
|
||||
|
||||
def process_create_address_scope(self, plugin_context, data, result):
|
||||
session = plugin_context.session
|
||||
value = self._get_value(data, 'address_scope_extension')
|
||||
record = TestAddressScopeExtension(address_scope_id=result['id'],
|
||||
value=value)
|
||||
session.add(record)
|
||||
result['address_scope_extension'] = value
|
||||
|
||||
def process_update_address_scope(self, plugin_context, data, result):
|
||||
session = plugin_context.session
|
||||
record = (session.query(TestAddressScopeExtension).
|
||||
filter_by(address_scope_id=result['id']).one())
|
||||
value = data.get('address_scope_extension')
|
||||
if value and value != record.value:
|
||||
record.value = value
|
||||
result['address_scope_extension'] = record.value
|
||||
|
||||
def extend_address_scope_dict(self, session, address_scope_db, result):
|
||||
value = (address_scope_db.extension.value
|
||||
if address_scope_db.extension else '')
|
||||
result['address_scope_extension'] = value
|
@ -35,3 +35,65 @@ class LoggerPlusMechanismDriver(driver_api.MechanismDriver,
|
||||
|
||||
def ensure_tenant(self, plugin_context, tenant_id):
|
||||
LOG.info(_LI("ensure_tenant called with tenant_id %s"), tenant_id)
|
||||
|
||||
def _log_subnetpool_call(self, method_name, context):
|
||||
LOG.info(_("%(method)s called with subnetpool settings %(current)s "
|
||||
"(original settings %(original)s)"),
|
||||
{'method': method_name,
|
||||
'current': context.current,
|
||||
'original': context.original})
|
||||
|
||||
def create_subnetpool_precommit(self, context):
|
||||
self._log_subnetpool_call("create_subnetpool_precommit",
|
||||
context)
|
||||
|
||||
def create_subnetpool_postcommit(self, context):
|
||||
self._log_subnetpool_call("create_subnetpool_postcommit",
|
||||
context)
|
||||
|
||||
def update_subnetpool_precommit(self, context):
|
||||
self._log_subnetpool_call("update_subnetpool_precommit",
|
||||
context)
|
||||
|
||||
def update_subnetpool_postcommit(self, context):
|
||||
self._log_subnetpool_call("update_subnetpool_postcommit",
|
||||
context)
|
||||
|
||||
def delete_subnetpool_precommit(self, context):
|
||||
self._log_subnetpool_call("delete_subnetpool_precommit",
|
||||
context)
|
||||
|
||||
def delete_subnetpool_postcommit(self, context):
|
||||
self._log_subnetpool_call("delete_subnetpool_postcommit",
|
||||
context)
|
||||
|
||||
def _log_address_scope_call(self, method_name, context):
|
||||
LOG.info(_("%(method)s called with address_scope settings %(current)s "
|
||||
"(original settings %(original)s)"),
|
||||
{'method': method_name,
|
||||
'current': context.current,
|
||||
'original': context.original})
|
||||
|
||||
def create_address_scope_precommit(self, context):
|
||||
self._log_address_scope_call("create_address_scope_precommit",
|
||||
context)
|
||||
|
||||
def create_address_scope_postcommit(self, context):
|
||||
self._log_address_scope_call("create_address_scope_postcommit",
|
||||
context)
|
||||
|
||||
def update_address_scope_precommit(self, context):
|
||||
self._log_address_scope_call("update_address_scope_precommit",
|
||||
context)
|
||||
|
||||
def update_address_scope_postcommit(self, context):
|
||||
self._log_address_scope_call("update_address_scope_postcommit",
|
||||
context)
|
||||
|
||||
def delete_address_scope_precommit(self, context):
|
||||
self._log_address_scope_call("delete_address_scope_precommit",
|
||||
context)
|
||||
|
||||
def delete_address_scope_postcommit(self, context):
|
||||
self._log_address_scope_call("delete_address_scope_postcommit",
|
||||
context)
|
||||
|
@ -0,0 +1,62 @@
|
||||
# Copyright (c) 2016 Cisco Systems 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._i18n import _
|
||||
from neutron.api import extensions
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.extensions import address_scope as as_ext
|
||||
|
||||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
attr.SUBNETPOOLS: {
|
||||
'subnetpool_extension': {'allow_post': True,
|
||||
'allow_put': True,
|
||||
'default': attr.ATTR_NOT_SPECIFIED,
|
||||
'is_visible': True,
|
||||
'enforce_policy': True},
|
||||
},
|
||||
as_ext.ADDRESS_SCOPES: {
|
||||
'address_scope_extension': {'allow_post': True,
|
||||
'allow_put': True,
|
||||
'default': attr.ATTR_NOT_SPECIFIED,
|
||||
'is_visible': True,
|
||||
'enforce_policy': True},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Fake_extension(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "ML2Plus fake extension"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "ml2plus_fake_extension"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return _("Adds test attributes to ML2Plus resources.")
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2016-08-02T10:00:00-00:00"
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return EXTENDED_ATTRIBUTES_2_0
|
||||
else:
|
||||
return {}
|
@ -0,0 +1,312 @@
|
||||
# Copyright (c) 2016 Cisco Systems 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.
|
||||
|
||||
import mock
|
||||
import uuid
|
||||
|
||||
from neutron import context
|
||||
from neutron import manager
|
||||
from neutron.plugins.ml2 import config
|
||||
|
||||
from gbpservice.neutron.tests.unit.plugins.ml2plus.drivers import (
|
||||
extension_test as ext_test)
|
||||
from gbpservice.neutron.tests.unit.plugins.ml2plus import test_plugin
|
||||
|
||||
|
||||
class ExtensionDriverTestCase(test_plugin.Ml2PlusPluginV2TestCase):
|
||||
|
||||
_extension_drivers = ['test_ml2plus']
|
||||
|
||||
def setUp(self):
|
||||
config.cfg.CONF.set_override('extension_drivers',
|
||||
self._extension_drivers,
|
||||
group='ml2')
|
||||
super(ExtensionDriverTestCase, self).setUp()
|
||||
self._plugin = manager.NeutronManager.get_plugin()
|
||||
self._ctxt = context.get_admin_context()
|
||||
|
||||
def _verify_subnetpool_create(self, code, exc_reason):
|
||||
tenant_id = str(uuid.uuid4())
|
||||
data = {'subnetpool': {'prefixes': ['10.0.0.0/8'],
|
||||
'name': 'sp1',
|
||||
'tenant_id': tenant_id}}
|
||||
req = self.new_create_request('subnetpools', data)
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(code, res.status_int)
|
||||
|
||||
subnetpool = self.deserialize(self.fmt, res)
|
||||
if exc_reason:
|
||||
self.assertEqual(exc_reason,
|
||||
subnetpool['NeutronError']['type'])
|
||||
|
||||
return (subnetpool, tenant_id)
|
||||
|
||||
def _verify_subnetpool_update(self, subnetpool, code, exc_reason):
|
||||
sp_id = subnetpool['subnetpool']['id']
|
||||
new_name = 'a_brand_new_name'
|
||||
data = {'subnetpool': {'name': new_name}}
|
||||
req = self.new_update_request('subnetpools', data, sp_id)
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(code, res.status_int)
|
||||
error = self.deserialize(self.fmt, res)
|
||||
self.assertEqual(exc_reason,
|
||||
error['NeutronError']['type'])
|
||||
|
||||
def test_faulty_process_create_subnetpool(self):
|
||||
with mock.patch.object(ext_test.TestExtensionDriver,
|
||||
'process_create_subnetpool',
|
||||
side_effect=TypeError):
|
||||
subnetpool, tenant_id = self._verify_subnetpool_create(
|
||||
500, 'HTTPInternalServerError')
|
||||
# Verify the operation is rolled back
|
||||
query_params = "tenant_id=%s" % tenant_id
|
||||
subnetpools = self._list('subnetpools', query_params=query_params)
|
||||
self.assertFalse(subnetpools['subnetpools'])
|
||||
|
||||
def test_faulty_process_update_subnetpool(self):
|
||||
with mock.patch.object(ext_test.TestExtensionDriver,
|
||||
'process_update_subnetpool',
|
||||
side_effect=TypeError):
|
||||
subnetpool, tid = self._verify_subnetpool_create(201, None)
|
||||
self._verify_subnetpool_update(subnetpool, 500,
|
||||
'HTTPInternalServerError')
|
||||
|
||||
def test_faulty_extend_subnetpool_dict(self):
|
||||
with mock.patch.object(ext_test.TestExtensionDriver,
|
||||
'extend_subnetpool_dict',
|
||||
side_effect=[None, None, TypeError]):
|
||||
subnetpool, tid = self._verify_subnetpool_create(201, None)
|
||||
self._verify_subnetpool_update(subnetpool, 400,
|
||||
'ExtensionDriverError')
|
||||
|
||||
def test_subnetpool_attr(self):
|
||||
with self.subnetpool(['10.0.0.0/8'], name='sp1',
|
||||
tenant_id='t1') as subnetpool:
|
||||
# Test create subnetpool
|
||||
ent = subnetpool['subnetpool'].get('subnetpool_extension')
|
||||
self.assertIsNotNone(ent)
|
||||
|
||||
# Test list subnetpools
|
||||
res = self._list('subnetpools')
|
||||
val = res['subnetpools'][0].get('subnetpool_extension')
|
||||
self.assertEqual('Test_SubnetPool_Extension_extend', val)
|
||||
|
||||
# Test subnetpool update
|
||||
data = {'subnetpool':
|
||||
{'subnetpool_extension':
|
||||
'Test_SubnetPool_Extension_Update'}}
|
||||
res = self._update('subnetpools', subnetpool['subnetpool']['id'],
|
||||
data)
|
||||
val = res['subnetpool'].get('subnetpool_extension')
|
||||
self.assertEqual('Test_SubnetPool_Extension_Update_update', val)
|
||||
|
||||
def test_extend_subnetpool_dict(self):
|
||||
with mock.patch.object(
|
||||
ext_test.TestExtensionDriver,
|
||||
'process_update_subnetpool') as pus, mock.patch.object(
|
||||
ext_test.TestExtensionDriver,
|
||||
'extend_subnetpool_dict') as esd, self.subnetpool(
|
||||
['10.0.0.0/8'], name='sp1',
|
||||
tenant_id='t1') as subnetpool:
|
||||
subnetpool_id = subnetpool['subnetpool']['id']
|
||||
subnetpool_data = {'subnetpool': {'id': subnetpool_id}}
|
||||
self._plugin.update_subnetpool(self._ctxt, subnetpool_id,
|
||||
subnetpool_data)
|
||||
self.assertTrue(pus.called)
|
||||
self.assertTrue(esd.called)
|
||||
|
||||
def _verify_address_scope_create(self, code, exc_reason):
|
||||
tenant_id = str(uuid.uuid4())
|
||||
data = {'address_scope': {'ip_version': 4,
|
||||
'name': 'as1',
|
||||
'tenant_id': tenant_id}}
|
||||
req = self.new_create_request('address-scopes', data)
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(code, res.status_int)
|
||||
|
||||
address_scope = self.deserialize(self.fmt, res)
|
||||
if exc_reason:
|
||||
self.assertEqual(exc_reason,
|
||||
address_scope['NeutronError']['type'])
|
||||
|
||||
return (address_scope, tenant_id)
|
||||
|
||||
def _verify_address_scope_update(self, address_scope, code, exc_reason):
|
||||
as_id = address_scope['address_scope']['id']
|
||||
new_name = 'a_brand_new_name'
|
||||
data = {'address_scope': {'name': new_name}}
|
||||
req = self.new_update_request('address-scopes', data, as_id)
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(code, res.status_int)
|
||||
error = self.deserialize(self.fmt, res)
|
||||
self.assertEqual(exc_reason,
|
||||
error['NeutronError']['type'])
|
||||
|
||||
def test_faulty_process_create_address_scope(self):
|
||||
with mock.patch.object(ext_test.TestExtensionDriver,
|
||||
'process_create_address_scope',
|
||||
side_effect=TypeError):
|
||||
address_scope, tenant_id = self._verify_address_scope_create(
|
||||
500, 'HTTPInternalServerError')
|
||||
# Verify the operation is rolled back
|
||||
query_params = "tenant_id=%s" % tenant_id
|
||||
address_scopes = self._list('address-scopes',
|
||||
query_params=query_params)
|
||||
self.assertFalse(address_scopes['address_scopes'])
|
||||
|
||||
def test_faulty_process_update_address_scope(self):
|
||||
with mock.patch.object(ext_test.TestExtensionDriver,
|
||||
'process_update_address_scope',
|
||||
side_effect=TypeError):
|
||||
address_scope, tid = self._verify_address_scope_create(201, None)
|
||||
self._verify_address_scope_update(address_scope, 500,
|
||||
'HTTPInternalServerError')
|
||||
|
||||
def test_faulty_extend_address_scope_dict(self):
|
||||
with mock.patch.object(ext_test.TestExtensionDriver,
|
||||
'extend_address_scope_dict',
|
||||
side_effect=[None, None, TypeError]):
|
||||
address_scope, tid = self._verify_address_scope_create(201, None)
|
||||
self._verify_address_scope_update(address_scope, 400,
|
||||
'ExtensionDriverError')
|
||||
|
||||
def test_address_scope_attr(self):
|
||||
with self.address_scope(4, name='as1',
|
||||
tenant_id='t1') as address_scope:
|
||||
# Test create address_scope
|
||||
ent = address_scope['address_scope'].get('address_scope_extension')
|
||||
self.assertIsNotNone(ent)
|
||||
|
||||
# Test list address_scopes
|
||||
res = self._list('address-scopes')
|
||||
val = res['address_scopes'][0].get('address_scope_extension')
|
||||
self.assertEqual('Test_AddressScope_Extension_extend', val)
|
||||
|
||||
# Test address_scope update
|
||||
data = {'address_scope':
|
||||
{'address_scope_extension':
|
||||
'Test_AddressScope_Extension_Update'}}
|
||||
res = self._update('address-scopes',
|
||||
address_scope['address_scope']['id'], data)
|
||||
val = res['address_scope'].get('address_scope_extension')
|
||||
self.assertEqual('Test_AddressScope_Extension_Update_update', val)
|
||||
|
||||
def test_extend_address_scope_dict(self):
|
||||
with mock.patch.object(
|
||||
ext_test.TestExtensionDriver,
|
||||
'process_update_address_scope') as puas, mock.patch.object(
|
||||
ext_test.TestExtensionDriver,
|
||||
'extend_address_scope_dict') as easd, self.address_scope(
|
||||
4, name='as1', tenant_id='t1') as address_scope:
|
||||
address_scope_id = address_scope['address_scope']['id']
|
||||
address_scope_data = {'address_scope': {'id': address_scope_id}}
|
||||
self._plugin.update_address_scope(self._ctxt, address_scope_id,
|
||||
address_scope_data)
|
||||
self.assertTrue(puas.called)
|
||||
self.assertTrue(easd.called)
|
||||
|
||||
|
||||
class DBExtensionDriverTestCase(test_plugin.Ml2PlusPluginV2TestCase):
|
||||
_extension_drivers = ['testdb_ml2plus']
|
||||
|
||||
def setUp(self):
|
||||
config.cfg.CONF.set_override('extension_drivers',
|
||||
self._extension_drivers,
|
||||
group='ml2')
|
||||
super(DBExtensionDriverTestCase, self).setUp()
|
||||
self._plugin = manager.NeutronManager.get_plugin()
|
||||
self._ctxt = context.get_admin_context()
|
||||
|
||||
def test_subnetpool_attr(self):
|
||||
with self.subnetpool(['10.0.0.0/8'], name='sp1',
|
||||
tenant_id='t1') as subnetpool:
|
||||
# Test create with default value.
|
||||
sp_id = subnetpool['subnetpool']['id']
|
||||
val = subnetpool['subnetpool']['subnetpool_extension']
|
||||
self.assertEqual("", val)
|
||||
res = self._show('subnetpools', sp_id)
|
||||
val = res['subnetpool']['subnetpool_extension']
|
||||
self.assertEqual("", val)
|
||||
|
||||
# Test list.
|
||||
res = self._list('subnetpools')
|
||||
val = res['subnetpools'][0]['subnetpool_extension']
|
||||
self.assertEqual("", val)
|
||||
|
||||
# Test create with explicit value.
|
||||
data = {'subnetpool':
|
||||
{'prefixes': ['10.0.0.0/8'],
|
||||
'name': 'sp2',
|
||||
'tenant_id': 't1',
|
||||
'subnetpool_extension': 'abc'}}
|
||||
req = self.new_create_request('subnetpools', data, self.fmt)
|
||||
res = req.get_response(self.api)
|
||||
subnetpool = self.deserialize(self.fmt, res)
|
||||
subnetpool_id = subnetpool['subnetpool']['id']
|
||||
val = subnetpool['subnetpool']['subnetpool_extension']
|
||||
self.assertEqual("abc", val)
|
||||
res = self._show('subnetpools', subnetpool_id)
|
||||
val = res['subnetpool']['subnetpool_extension']
|
||||
self.assertEqual("abc", val)
|
||||
|
||||
# Test update.
|
||||
data = {'subnetpool': {'subnetpool_extension': "def"}}
|
||||
res = self._update('subnetpools', subnetpool_id, data)
|
||||
val = res['subnetpool']['subnetpool_extension']
|
||||
self.assertEqual("def", val)
|
||||
res = self._show('subnetpools', subnetpool_id)
|
||||
val = res['subnetpool']['subnetpool_extension']
|
||||
self.assertEqual("def", val)
|
||||
|
||||
def test_address_scope_attr(self):
|
||||
with self.address_scope(4, name='as1',
|
||||
tenant_id='t1') as address_scope:
|
||||
# Test create with default value.
|
||||
as_id = address_scope['address_scope']['id']
|
||||
val = address_scope['address_scope']['address_scope_extension']
|
||||
self.assertEqual("", val)
|
||||
res = self._show('address-scopes', as_id)
|
||||
val = res['address_scope']['address_scope_extension']
|
||||
self.assertEqual("", val)
|
||||
|
||||
# Test list.
|
||||
res = self._list('address-scopes')
|
||||
val = res['address_scopes'][0]['address_scope_extension']
|
||||
self.assertEqual("", val)
|
||||
|
||||
# Test create with explicit value.
|
||||
data = {'address_scope':
|
||||
{'ip_version': 4,
|
||||
'name': 'as2',
|
||||
'tenant_id': 't1',
|
||||
'address_scope_extension': 'abc'}}
|
||||
req = self.new_create_request('address-scopes', data, self.fmt)
|
||||
res = req.get_response(self.ext_api)
|
||||
address_scope = self.deserialize(self.fmt, res)
|
||||
address_scope_id = address_scope['address_scope']['id']
|
||||
val = address_scope['address_scope']['address_scope_extension']
|
||||
self.assertEqual("abc", val)
|
||||
res = self._show('address-scopes', address_scope_id)
|
||||
val = res['address_scope']['address_scope_extension']
|
||||
self.assertEqual("abc", val)
|
||||
|
||||
# Test update.
|
||||
data = {'address_scope': {'address_scope_extension': "def"}}
|
||||
res = self._update('address-scopes', address_scope_id, data)
|
||||
val = res['address_scope']['address_scope_extension']
|
||||
self.assertEqual("def", val)
|
||||
res = self._show('address-scopes', address_scope_id)
|
||||
val = res['address_scope']['address_scope_extension']
|
||||
self.assertEqual("def", val)
|
@ -15,9 +15,12 @@
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.api import extensions
|
||||
from neutron import manager
|
||||
from neutron.plugins.ml2 import config
|
||||
from neutron.tests.unit.api import test_extensions
|
||||
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
|
||||
from neutron.tests.unit.extensions import test_address_scope
|
||||
|
||||
from gbpservice.neutron.tests.unit.plugins.ml2plus.drivers import (
|
||||
mechanism_logger as mech_logger)
|
||||
@ -28,7 +31,7 @@ PLUGIN_NAME = 'gbpservice.neutron.plugins.ml2plus.plugin.Ml2PlusPlugin'
|
||||
# This is just a quick sanity test that basic ML2 plugin functionality
|
||||
# is preserved.
|
||||
|
||||
class Ml2PlusPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
class Ml2PlusPluginV2TestCase(test_address_scope.AddressScopeTestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Enable the test mechanism driver to ensure that
|
||||
@ -41,6 +44,8 @@ class Ml2PlusPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
['physnet1:1000:1099'],
|
||||
group='ml2_type_vlan')
|
||||
super(Ml2PlusPluginV2TestCase, self).setUp(PLUGIN_NAME)
|
||||
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
|
||||
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
|
||||
self.port_create_status = 'DOWN'
|
||||
self.plugin = manager.NeutronManager.get_plugin()
|
||||
self.plugin.start_rpc_listeners()
|
||||
@ -136,6 +141,145 @@ class TestEnsureTenant(Ml2PlusPluginV2TestCase):
|
||||
any_order=True)
|
||||
self.assertEqual(2, et.call_count)
|
||||
|
||||
def test_subnetpool(self):
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'ensure_tenant') as et:
|
||||
self._make_subnetpool(self.fmt, ['10.0.0.0/8'], name='sp1',
|
||||
tenant_id='t1')
|
||||
et.assert_called_once_with(mock.ANY, 't1')
|
||||
|
||||
def test_address_scope(self):
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'ensure_tenant') as et:
|
||||
self._make_address_scope(self.fmt, 4, name='as1', tenant_id='t1')
|
||||
et.assert_called_once_with(mock.ANY, 't1')
|
||||
|
||||
|
||||
class TestSubnetPool(Ml2PlusPluginV2TestCase):
|
||||
def test_create(self):
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'create_subnetpool_precommit') as pre:
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'create_subnetpool_postcommit') as post:
|
||||
self._make_subnetpool(self.fmt, ['10.0.0.0/8'], name='sp1',
|
||||
tenant_id='t1')
|
||||
|
||||
self.assertEqual(1, pre.call_count)
|
||||
self.assertEqual('sp1',
|
||||
pre.call_args[0][0].current['name'])
|
||||
self.assertIsNone(pre.call_args[0][0].original)
|
||||
|
||||
self.assertEqual(1, post.call_count)
|
||||
self.assertEqual('sp1',
|
||||
post.call_args[0][0].current['name'])
|
||||
self.assertIsNone(post.call_args[0][0].original)
|
||||
|
||||
def test_update(self):
|
||||
subnetpool = self._make_subnetpool(
|
||||
self.fmt, ['10.0.0.0/8'], name='sp1', tenant_id='t1')['subnetpool']
|
||||
data = {'subnetpool': {'name': 'newnameforsubnetpool'}}
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'update_subnetpool_precommit') as pre:
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'update_subnetpool_postcommit') as post:
|
||||
res = self._update('subnetpools', subnetpool['id'],
|
||||
data)['subnetpool']
|
||||
self.assertEqual('newnameforsubnetpool', res['name'])
|
||||
|
||||
self.assertEqual(1, pre.call_count)
|
||||
self.assertEqual('newnameforsubnetpool',
|
||||
pre.call_args[0][0].current['name'])
|
||||
self.assertEqual('sp1',
|
||||
pre.call_args[0][0].original['name'])
|
||||
|
||||
self.assertEqual(1, post.call_count)
|
||||
self.assertEqual('newnameforsubnetpool',
|
||||
post.call_args[0][0].current['name'])
|
||||
self.assertEqual('sp1',
|
||||
post.call_args[0][0].original['name'])
|
||||
|
||||
def test_delete(self):
|
||||
subnetpool = self._make_subnetpool(
|
||||
self.fmt, ['10.0.0.0/8'], name='sp1', tenant_id='t1')['subnetpool']
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'delete_subnetpool_precommit') as pre:
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'delete_subnetpool_postcommit') as post:
|
||||
self._delete('subnetpools', subnetpool['id'])
|
||||
|
||||
self.assertEqual(1, pre.call_count)
|
||||
self.assertEqual('sp1',
|
||||
pre.call_args[0][0].current['name'])
|
||||
self.assertIsNone(pre.call_args[0][0].original)
|
||||
|
||||
self.assertEqual(1, post.call_count)
|
||||
self.assertEqual('sp1',
|
||||
post.call_args[0][0].current['name'])
|
||||
self.assertIsNone(post.call_args[0][0].original)
|
||||
|
||||
|
||||
class TestAddressScope(Ml2PlusPluginV2TestCase):
|
||||
def test_create(self):
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'create_address_scope_precommit') as pre:
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'create_address_scope_postcommit') as post:
|
||||
self._make_address_scope(self.fmt, 4, name='as1',
|
||||
tenant_id='t1')
|
||||
|
||||
self.assertEqual(1, pre.call_count)
|
||||
self.assertEqual('as1',
|
||||
pre.call_args[0][0].current['name'])
|
||||
self.assertIsNone(pre.call_args[0][0].original)
|
||||
|
||||
self.assertEqual(1, post.call_count)
|
||||
self.assertEqual('as1',
|
||||
post.call_args[0][0].current['name'])
|
||||
self.assertIsNone(post.call_args[0][0].original)
|
||||
|
||||
def test_update(self):
|
||||
address_scope = self._make_address_scope(
|
||||
self.fmt, 4, name='as1', tenant_id='t1')['address_scope']
|
||||
data = {'address_scope': {'name': 'newnameforaddress_scope'}}
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'update_address_scope_precommit') as pre:
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'update_address_scope_postcommit') as post:
|
||||
res = self._update('address-scopes', address_scope['id'],
|
||||
data)['address_scope']
|
||||
self.assertEqual('newnameforaddress_scope', res['name'])
|
||||
|
||||
self.assertEqual(1, pre.call_count)
|
||||
self.assertEqual('newnameforaddress_scope',
|
||||
pre.call_args[0][0].current['name'])
|
||||
self.assertEqual('as1',
|
||||
pre.call_args[0][0].original['name'])
|
||||
|
||||
self.assertEqual(1, post.call_count)
|
||||
self.assertEqual('newnameforaddress_scope',
|
||||
post.call_args[0][0].current['name'])
|
||||
self.assertEqual('as1',
|
||||
post.call_args[0][0].original['name'])
|
||||
|
||||
def test_delete(self):
|
||||
address_scope = self._make_address_scope(
|
||||
self.fmt, 4, name='as1', tenant_id='t1')['address_scope']
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'delete_address_scope_precommit') as pre:
|
||||
with mock.patch.object(mech_logger.LoggerPlusMechanismDriver,
|
||||
'delete_address_scope_postcommit') as post:
|
||||
self._delete('address-scopes', address_scope['id'])
|
||||
|
||||
self.assertEqual(1, pre.call_count)
|
||||
self.assertEqual('as1',
|
||||
pre.call_args[0][0].current['name'])
|
||||
self.assertIsNone(pre.call_args[0][0].original)
|
||||
|
||||
self.assertEqual(1, post.call_count)
|
||||
self.assertEqual('as1',
|
||||
post.call_args[0][0].current['name'])
|
||||
self.assertIsNone(post.call_args[0][0].original)
|
||||
|
||||
|
||||
class TestMl2BasicGet(test_plugin.TestBasicGet,
|
||||
Ml2PlusPluginV2TestCase):
|
||||
|
@ -78,6 +78,8 @@ neutron.ml2.mechanism_drivers =
|
||||
stitching_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.stitching.driver:TrafficStitchingMechanismGBPDriver
|
||||
neutron.ml2.extension_drivers =
|
||||
apic_aim = gbpservice.neutron.plugins.ml2plus.drivers.apic_aim.extension_driver:ApicExtensionDriver
|
||||
test_ml2plus = gbpservice.neutron.tests.unit.plugins.ml2plus.drivers.extension_test:TestExtensionDriver
|
||||
testdb_ml2plus = gbpservice.neutron.tests.unit.plugins.ml2plus.drivers.extension_test:TestDBExtensionDriver
|
||||
gbpservice.neutron.servicechain.servicechain_drivers =
|
||||
dummy = gbpservice.neutron.services.servicechain.plugins.msc.drivers.dummy_driver:NoopDriver
|
||||
simplechain_driver = gbpservice.neutron.services.servicechain.plugins.msc.drivers.simplechain_driver:SimpleChainDriver
|
||||
|
Loading…
Reference in New Issue
Block a user