[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
(cherry picked from commit a6b918934a)
This commit is contained in:
@@ -19,6 +19,70 @@ import six
|
|||||||
from neutron.plugins.ml2 import driver_api
|
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)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class MechanismDriver(driver_api.MechanismDriver):
|
class MechanismDriver(driver_api.MechanismDriver):
|
||||||
|
|
||||||
@@ -39,9 +103,280 @@ class MechanismDriver(driver_api.MechanismDriver):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# TODO(rkukura): Add precommit/postcommit calls for address_scope,
|
def create_subnetpool_precommit(self, context):
|
||||||
# subnet_pool, and other resources.
|
"""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,
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
# subnet_pool, and other resources.
|
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 gbpservice.neutron.plugins.ml2plus import driver_api
|
||||||
|
|
||||||
from neutron._i18n import _LE
|
from neutron._i18n import _LE
|
||||||
|
from neutron._i18n import _LI
|
||||||
from neutron.plugins.ml2.common import exceptions as ml2_exc
|
from neutron.plugins.ml2.common import exceptions as ml2_exc
|
||||||
from neutron.plugins.ml2 import managers
|
from neutron.plugins.ml2 import managers
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
from oslo_utils import excutils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@@ -28,6 +30,37 @@ class MechanismManager(managers.MechanismManager):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(MechanismManager, self).__init__()
|
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):
|
def ensure_tenant(self, plugin_context, tenant_id):
|
||||||
for driver in self.ordered_mech_drivers:
|
for driver in self.ordered_mech_drivers:
|
||||||
if isinstance(driver.obj, driver_api.MechanismDriver):
|
if isinstance(driver.obj, driver_api.MechanismDriver):
|
||||||
@@ -37,3 +70,108 @@ class MechanismManager(managers.MechanismManager):
|
|||||||
LOG.exception(_LE("Mechanism driver '%s' failed in "
|
LOG.exception(_LE("Mechanism driver '%s' failed in "
|
||||||
"ensure_tenant"), driver.name)
|
"ensure_tenant"), driver.name)
|
||||||
raise ml2_exc.MechanismDriverError(method="ensure_tenant")
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from neutron._i18n import _LE
|
||||||
from neutron._i18n import _LI
|
from neutron._i18n import _LI
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes
|
||||||
|
from neutron.db import db_base_plugin_v2
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
from neutron.db import securitygroups_db
|
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 managers as ml2_managers
|
||||||
from neutron.plugins.ml2 import plugin as ml2_plugin
|
from neutron.plugins.ml2 import plugin as ml2_plugin
|
||||||
from neutron.quota import resource_registry
|
from neutron.quota import resource_registry
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
from oslo_utils import excutils
|
||||||
from sqlalchemy import inspect
|
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 managers
|
||||||
|
from gbpservice.neutron.plugins.ml2plus import patch_neutron # noqa
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@@ -59,7 +66,7 @@ class Ml2PlusPlugin(ml2_plugin.Ml2Plugin):
|
|||||||
LOG.info(_LI("Ml2Plus initializing"))
|
LOG.info(_LI("Ml2Plus initializing"))
|
||||||
# First load drivers, then initialize DB, then initialize drivers
|
# First load drivers, then initialize DB, then initialize drivers
|
||||||
self.type_manager = ml2_managers.TypeManager()
|
self.type_manager = ml2_managers.TypeManager()
|
||||||
self.extension_manager = ml2_managers.ExtensionManager()
|
self.extension_manager = managers.ExtensionManager()
|
||||||
self.mechanism_manager = managers.MechanismManager()
|
self.mechanism_manager = managers.MechanismManager()
|
||||||
super(ml2_plugin.Ml2Plugin, self).__init__()
|
super(ml2_plugin.Ml2Plugin, self).__init__()
|
||||||
self.type_manager.initialize()
|
self.type_manager.initialize()
|
||||||
@@ -71,6 +78,12 @@ class Ml2PlusPlugin(ml2_plugin.Ml2Plugin):
|
|||||||
self._verify_service_plugins_requirements()
|
self._verify_service_plugins_requirements()
|
||||||
LOG.info(_LI("Modular L2 Plugin (extended) initialization complete"))
|
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):
|
def _ml2_md_extend_network_dict(self, result, netdb):
|
||||||
session = inspect(netdb).session
|
session = inspect(netdb).session
|
||||||
with session.begin(subtransactions=True):
|
with session.begin(subtransactions=True):
|
||||||
@@ -87,6 +100,29 @@ class Ml2PlusPlugin(ml2_plugin.Ml2Plugin):
|
|||||||
self.extension_manager.extend_subnet_dict(
|
self.extension_manager.extend_subnet_dict(
|
||||||
session, subnetdb, result)
|
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):
|
def create_network(self, context, network):
|
||||||
self._ensure_tenant(context, network[attributes.NETWORK])
|
self._ensure_tenant(context, network[attributes.NETWORK])
|
||||||
return super(Ml2PlusPlugin, self).create_network(context, 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,
|
return super(Ml2PlusPlugin, self).create_port_bulk(context,
|
||||||
ports)
|
ports)
|
||||||
|
|
||||||
# TODO(rkukura): Override address_scope, subnet_pool, and any
|
def create_subnetpool(self, context, subnetpool):
|
||||||
# other needed resources to ensure tenant and invoke mechanism and
|
self._ensure_tenant(context, subnetpool[attributes.SUBNETPOOL])
|
||||||
# extension drivers.
|
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):
|
def _ensure_tenant(self, context, resource):
|
||||||
tenant_id = resource['tenant_id']
|
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):
|
def ensure_tenant(self, plugin_context, tenant_id):
|
||||||
LOG.info(_LI("ensure_tenant called with tenant_id %s"), 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
|
import mock
|
||||||
|
|
||||||
|
from neutron.api import extensions
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.plugins.ml2 import config
|
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.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 (
|
from gbpservice.neutron.tests.unit.plugins.ml2plus.drivers import (
|
||||||
mechanism_logger as mech_logger)
|
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
|
# This is just a quick sanity test that basic ML2 plugin functionality
|
||||||
# is preserved.
|
# is preserved.
|
||||||
|
|
||||||
class Ml2PlusPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
class Ml2PlusPluginV2TestCase(test_address_scope.AddressScopeTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Enable the test mechanism driver to ensure that
|
# Enable the test mechanism driver to ensure that
|
||||||
@@ -41,6 +44,8 @@ class Ml2PlusPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
|||||||
['physnet1:1000:1099'],
|
['physnet1:1000:1099'],
|
||||||
group='ml2_type_vlan')
|
group='ml2_type_vlan')
|
||||||
super(Ml2PlusPluginV2TestCase, self).setUp(PLUGIN_NAME)
|
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.port_create_status = 'DOWN'
|
||||||
self.plugin = manager.NeutronManager.get_plugin()
|
self.plugin = manager.NeutronManager.get_plugin()
|
||||||
self.plugin.start_rpc_listeners()
|
self.plugin.start_rpc_listeners()
|
||||||
@@ -136,6 +141,145 @@ class TestEnsureTenant(Ml2PlusPluginV2TestCase):
|
|||||||
any_order=True)
|
any_order=True)
|
||||||
self.assertEqual(2, et.call_count)
|
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,
|
class TestMl2BasicGet(test_plugin.TestBasicGet,
|
||||||
Ml2PlusPluginV2TestCase):
|
Ml2PlusPluginV2TestCase):
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ neutron.ml2.mechanism_drivers =
|
|||||||
stitching_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.stitching.driver:TrafficStitchingMechanismGBPDriver
|
stitching_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.stitching.driver:TrafficStitchingMechanismGBPDriver
|
||||||
neutron.ml2.extension_drivers =
|
neutron.ml2.extension_drivers =
|
||||||
apic_aim = gbpservice.neutron.plugins.ml2plus.drivers.apic_aim.extension_driver:ApicExtensionDriver
|
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 =
|
gbpservice.neutron.servicechain.servicechain_drivers =
|
||||||
dummy = gbpservice.neutron.services.servicechain.plugins.msc.drivers.dummy_driver:NoopDriver
|
dummy = gbpservice.neutron.services.servicechain.plugins.msc.drivers.dummy_driver:NoopDriver
|
||||||
simplechain_driver = gbpservice.neutron.services.servicechain.plugins.msc.drivers.simplechain_driver:SimpleChainDriver
|
simplechain_driver = gbpservice.neutron.services.servicechain.plugins.msc.drivers.simplechain_driver:SimpleChainDriver
|
||||||
|
|||||||
Reference in New Issue
Block a user