Merge "Initial Modular L2 Mechanism Driver implementation."
This commit is contained in:
commit
3c7ad231b6
@ -13,6 +13,12 @@
|
||||
# tenant_network_types = local
|
||||
# Example: tenant_network_types = vlan,gre
|
||||
|
||||
# (ListOpt) Ordered list of networking mechanism driver entrypoints
|
||||
# to be loaded from the neutron.ml2.mechanism_drivers namespace.
|
||||
# mechanism_drivers =
|
||||
# Example: mechanism_drivers = arista
|
||||
# Example: mechanism_drivers = cisco,logger
|
||||
|
||||
[ml2_type_flat]
|
||||
# (ListOpt) List of physical_network names with which flat networks
|
||||
# can be created. Use * to allow flat networks with arbitrary
|
||||
|
@ -31,13 +31,13 @@ openvswitch and linuxbridge plugins' L2 agents, and should also work
|
||||
with the hyperv L2 agent. A modular agent may be developed as a
|
||||
follow-on effort.
|
||||
|
||||
Support for mechanism drivers is currently skeletal. The
|
||||
MechanismDriver interface is currently a stub, with details to be
|
||||
defined in future versions. MechanismDrivers will be called both
|
||||
inside and following DB transactions for network and port
|
||||
create/update/delete operations. They will also be called to establish
|
||||
a port binding, determining the VIF type and network segment to be
|
||||
used.
|
||||
Support for mechanism drivers is currently a work-in-progress in
|
||||
pre-release Havana versions, and the interface is subject to change
|
||||
before the release of Havana. MechanismDrivers are currently called
|
||||
both inside and following DB transactions for network and port
|
||||
create/update/delete operations. In a future version, they will also
|
||||
called to establish a port binding, determining the VIF type and
|
||||
network segment to be used.
|
||||
|
||||
The database schema and driver APIs support multi-segment networks,
|
||||
but the client API for multi-segment networks is not yet implemented.
|
||||
|
14
neutron/plugins/ml2/common/__init__.py
Normal file
14
neutron/plugins/ml2/common/__init__.py
Normal file
@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2013 OpenStack Foundation
|
||||
# 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.
|
23
neutron/plugins/ml2/common/exceptions.py
Normal file
23
neutron/plugins/ml2/common/exceptions.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2013 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""Exceptions used by ML2."""
|
||||
|
||||
from neutron.common import exceptions
|
||||
|
||||
|
||||
class MechanismDriverError(exceptions.NeutronException):
|
||||
"""Mechanism driver call failed."""
|
||||
message = _("%(method)s failed.")
|
@ -29,9 +29,9 @@ ml2_opts = [
|
||||
"networks.")),
|
||||
cfg.ListOpt('mechanism_drivers',
|
||||
default=[],
|
||||
help=_("List of networking mechanism driver entrypoints to "
|
||||
"be loaded from the neutron.ml2.mechanism_drivers "
|
||||
"namespace.")),
|
||||
help=_("An ordered list of networking mechanism driver "
|
||||
"entrypoints to be loaded from the "
|
||||
"neutron.ml2.mechanism_drivers namespace.")),
|
||||
]
|
||||
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from abc import ABCMeta, abstractmethod, abstractproperty
|
||||
|
||||
# The following keys are used in the segment dictionaries passed via
|
||||
# the driver API. These are defined separately from similar keys in
|
||||
@ -128,15 +128,103 @@ class TypeDriver(object):
|
||||
pass
|
||||
|
||||
|
||||
class NetworkContext(object):
|
||||
"""Context passed to MechanismDrivers for changes to network resources.
|
||||
|
||||
A NetworkContext instance wraps a network 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.
|
||||
"""
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractproperty
|
||||
def current(self):
|
||||
"""Return the current state of the network.
|
||||
|
||||
Return the current state of the network, as defined by
|
||||
NeutronPluginBaseV2.create_network and all extensions in the
|
||||
ml2 plugin.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def original(self):
|
||||
"""Return the original state of the network.
|
||||
|
||||
Return the original state of the network, prior to a call to
|
||||
update_network. Method is only valid within calls to
|
||||
update_network_precommit and update_network_postcommit.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def network_segments(self):
|
||||
"""Return the segments associated with this network resource."""
|
||||
pass
|
||||
|
||||
|
||||
class PortContext(object):
|
||||
"""Context passed to MechanismDrivers for changes to port resources.
|
||||
|
||||
A PortContext instance wraps a port 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.
|
||||
"""
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractproperty
|
||||
def current(self):
|
||||
"""Return the current state of the port.
|
||||
|
||||
Return the current state of the port, as defined by
|
||||
NeutronPluginBaseV2.create_port and all extensions in the ml2
|
||||
plugin.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def original(self):
|
||||
"""Return the original state of the port
|
||||
|
||||
Return the original state of the port, prior to a call to
|
||||
update_port. Method is only valid within calls to
|
||||
update_port_precommit and update_port_postcommit.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def network(self):
|
||||
"""Return the NetworkContext associated with this port."""
|
||||
pass
|
||||
|
||||
|
||||
class MechanismDriver(object):
|
||||
"""Define stable abstract interface for ML2 mechanism drivers.
|
||||
|
||||
Note that this is currently a stub class, but it is expected to be
|
||||
functional for the H-2 milestone. It currently serves mainly to
|
||||
help solidify the architectural distinction between TypeDrivers
|
||||
and MechanismDrivers.
|
||||
A mechanism driver is called on the creation, update, and deletion
|
||||
of networks and ports. For every event, there are two methods that
|
||||
get called - one within the database transaction (method suffix of
|
||||
_precommit), one right afterwards (method suffix of _postcommit).
|
||||
|
||||
Exceptions raised by methods called inside the transaction can
|
||||
rollback, but should not make any blocking calls (for example,
|
||||
REST requests to an outside controller). Methods called after
|
||||
transaction commits can make blocking external calls, though these
|
||||
will block the entire process. Exceptions raised in calls after
|
||||
the transaction commits may cause the associated resource to be
|
||||
deleted.
|
||||
|
||||
Because rollback outside of the transaction is not done in the
|
||||
update network/port case, all data validation must be done within
|
||||
methods that are part of the database transaction.
|
||||
"""
|
||||
|
||||
# TODO(apech): add calls for subnets
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractmethod
|
||||
@ -149,10 +237,177 @@ class MechanismDriver(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
# TODO(rkukura): Add methods called inside and after transaction
|
||||
# for create_network, update_network, delete_network, create_port,
|
||||
# update_port, delete_port, and maybe for port binding
|
||||
# changes. Exceptions raised by methods called inside transactions
|
||||
# can rollback, but shouldn't block. Methods called after
|
||||
# transaction commits can block, and exceptions may cause deletion
|
||||
# of resource.
|
||||
def create_network_precommit(self, context):
|
||||
"""Allocate resources for a new network.
|
||||
|
||||
:param context: NetworkContext instance describing the new
|
||||
network.
|
||||
|
||||
Create a new network, 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_network_postcommit(self, context):
|
||||
"""Create a network.
|
||||
|
||||
:param context: NetworkContext instance describing the new
|
||||
network.
|
||||
|
||||
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_network_precommit(self, context):
|
||||
"""Update resources of a network.
|
||||
|
||||
:param context: NetworkContext instance describing the new
|
||||
state of the network, as well as the original state prior
|
||||
to the update_network call.
|
||||
|
||||
Update values of a network, updating the associated resources
|
||||
in the database. Called inside transaction context on session.
|
||||
Raising an exception will result in rollback of the
|
||||
transaction.
|
||||
|
||||
update_network_precommit is called for all changes to the
|
||||
network 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_network_postcommit(self, context):
|
||||
"""Update a network.
|
||||
|
||||
:param context: NetworkContext instance describing the new
|
||||
state of the network, as well as the original state prior
|
||||
to the update_network 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_network_postcommit is called for all changes to the
|
||||
network 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_network_precommit(self, context):
|
||||
"""Delete resources for a network.
|
||||
|
||||
:param context: NetworkContext instance describing the current
|
||||
state of the network, prior to the call to delete it.
|
||||
|
||||
Delete network resources previously allocated by this
|
||||
mechanism driver for a network. 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_network_postcommit(self, context):
|
||||
"""Delete a network.
|
||||
|
||||
:param context: NetworkContext instance describing the current
|
||||
state of the network, 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_port_precommit(self, context):
|
||||
"""Allocate resources for a new port.
|
||||
|
||||
:param context: PortContext instance describing the port.
|
||||
|
||||
Create a new port, 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_port_postcommit(self, context):
|
||||
"""Create a port.
|
||||
|
||||
:param context: PortContext instance describing the port.
|
||||
|
||||
Called after the transaction completes. Call can block, though
|
||||
will block the entire process so care should be taken to not
|
||||
drastically affect performance. Raising an exception will
|
||||
result in the deletion of the resource.
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_port_precommit(self, context):
|
||||
"""Update resources of a port.
|
||||
|
||||
:param context: PortContext instance describing the new
|
||||
state of the port, as well as the original state prior
|
||||
to the update_port call.
|
||||
|
||||
Called inside transaction context on session to complete a
|
||||
port update as defined by this mechanism driver. Raising an
|
||||
exception will result in rollback of the transaction.
|
||||
|
||||
update_port_precommit is called for all changes to the port
|
||||
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_port_postcommit(self, context):
|
||||
"""Update a port.
|
||||
|
||||
:param context: PortContext instance describing the new
|
||||
state of the port, as well as the original state prior
|
||||
to the update_port call.
|
||||
|
||||
Called after the transaction completes. Call can block, though
|
||||
will block the entire process so care should be taken to not
|
||||
drastically affect performance. Raising an exception will
|
||||
result in the deletion of the resource.
|
||||
|
||||
update_port_postcommit is called for all changes to the port
|
||||
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_port_precommit(self, context):
|
||||
"""Delete resources of a port.
|
||||
|
||||
:param context: PortContext instance describing the current
|
||||
state of the port, prior to the call to delete it.
|
||||
|
||||
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_port_postcommit(self, context):
|
||||
"""Delete a port.
|
||||
|
||||
:param context: PortContext instance describing the current
|
||||
state of the port, prior to the call to delete it.
|
||||
|
||||
Called after the transaction completes. 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
|
||||
|
74
neutron/plugins/ml2/driver_context.py
Normal file
74
neutron/plugins/ml2/driver_context.py
Normal file
@ -0,0 +1,74 @@
|
||||
# Copyright (c) 2013 OpenStack Foundation
|
||||
# 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_api as api
|
||||
|
||||
|
||||
class MechanismDriverContext(object):
|
||||
"""MechanismDriver context base class."""
|
||||
def __init__(self, plugin, plugin_context):
|
||||
self._plugin = plugin
|
||||
# This temporarily creates a reference loop, but the
|
||||
# lifetime of PortContext is limited to a single
|
||||
# method call of the plugin.
|
||||
self._plugin_context = plugin_context
|
||||
|
||||
|
||||
class NetworkContext(MechanismDriverContext, api.NetworkContext):
|
||||
|
||||
def __init__(self, plugin, plugin_context, network,
|
||||
segments=None, original_network=None):
|
||||
super(NetworkContext, self).__init__(plugin, plugin_context)
|
||||
self._network = network
|
||||
self._original_network = original_network
|
||||
self._segments = segments
|
||||
|
||||
def current(self):
|
||||
return self._network
|
||||
|
||||
def original(self):
|
||||
return self._original_network
|
||||
|
||||
def network_segments(self):
|
||||
if not self._segments:
|
||||
self._segments = self._plugin.get_network_segments(
|
||||
self._plugin_context, self._network['id'])
|
||||
return self._segments
|
||||
|
||||
|
||||
class PortContext(MechanismDriverContext, api.PortContext):
|
||||
|
||||
def __init__(self, plugin, plugin_context, port,
|
||||
original_port=None):
|
||||
super(PortContext, self).__init__(plugin, plugin_context)
|
||||
self._port = port
|
||||
self._original_port = original_port
|
||||
self._network_context = None
|
||||
|
||||
def current(self):
|
||||
return self._port
|
||||
|
||||
def original(self):
|
||||
return self._original_port
|
||||
|
||||
def network(self):
|
||||
"""Return the NetworkContext associated with this port."""
|
||||
if not self._network_context:
|
||||
network = self._plugin.get_network(self._plugin_context,
|
||||
self._port["network_id"])
|
||||
self._network_context = NetworkContext(self._plugin,
|
||||
self._plugin_context,
|
||||
network)
|
||||
return self._network_context
|
@ -20,6 +20,7 @@ import stevedore
|
||||
|
||||
from neutron.common import exceptions as exc
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.ml2.common import exceptions as ml2_exc
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
|
||||
|
||||
@ -108,12 +109,18 @@ class TypeManager(stevedore.named.NamedExtensionManager):
|
||||
class MechanismManager(stevedore.named.NamedExtensionManager):
|
||||
"""Manage networking mechanisms using drivers.
|
||||
|
||||
Note that this is currently a stub class, but it is expected to be
|
||||
functional for the H-2 milestone. It currently serves mainly to
|
||||
help solidify the architectural distinction between TypeDrivers
|
||||
and MechanismDrivers.
|
||||
Note that this is still a work in progress, and the interface
|
||||
may change before the final release of Havana.
|
||||
"""
|
||||
|
||||
# TODO(apech): add calls for subnets
|
||||
|
||||
# Registered mechanism drivers, keyed by name.
|
||||
mech_drivers = {}
|
||||
# Ordered list of mechanism drivers, defining
|
||||
# the order in which the drivers are called.
|
||||
ordered_mech_drivers = []
|
||||
|
||||
def __init__(self):
|
||||
# REVISIT(rkukura): Need way to make stevedore use our logging
|
||||
# configuration. Currently, nothing is logged if loading a
|
||||
@ -125,9 +132,226 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
|
||||
cfg.CONF.ml2.mechanism_drivers,
|
||||
invoke_on_load=True)
|
||||
LOG.info(_("Loaded mechanism driver names: %s"), self.names())
|
||||
# TODO(rkukura): Register mechanisms.
|
||||
self._register_mechanisms()
|
||||
|
||||
def _register_mechanisms(self):
|
||||
"""Register all mechanism drivers.
|
||||
|
||||
This method should only be called once in the MechanismManager
|
||||
constructor.
|
||||
"""
|
||||
for ext in self:
|
||||
if ext.name in self.mech_drivers:
|
||||
LOG.error(_("Mechanism driver '%s' ignored because "
|
||||
"driver is already registered"),
|
||||
ext.name)
|
||||
else:
|
||||
self.mech_drivers[ext.name] = ext
|
||||
self.ordered_mech_drivers.append(ext)
|
||||
LOG.info(_("Registered mechanism drivers: %s"),
|
||||
[driver.name for driver in self.ordered_mech_drivers])
|
||||
|
||||
def initialize(self):
|
||||
pass
|
||||
for driver in self.ordered_mech_drivers:
|
||||
LOG.info(_("Initializing mechanism driver '%s'"), driver.name)
|
||||
driver.obj.initialize()
|
||||
|
||||
# TODO(rkukura): Define mechanism dispatch methods
|
||||
def _call_on_drivers(self, method_name, context,
|
||||
continue_on_failure=False):
|
||||
"""Helper method for calling a method across all 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:
|
||||
try:
|
||||
getattr(driver.obj, method_name)(context)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_("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 create_network_precommit(self, context):
|
||||
"""Notify all mechanism drivers of a network creation.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver create_network_precommit call fails.
|
||||
|
||||
Called within the database transaction. If a mechanism driver
|
||||
raises an exception, then a MechanismDriverError is propogated
|
||||
to the caller, triggering a rollback. There is no guarantee
|
||||
that all mechanism drivers are called in this case.
|
||||
"""
|
||||
self._call_on_drivers("create_network_precommit", context)
|
||||
|
||||
def create_network_postcommit(self, context):
|
||||
"""Notify all mechanism drivers of network creation.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver create_network_postcommit call fails.
|
||||
|
||||
Called after the database transaction. If a mechanism driver
|
||||
raises an exception, then a MechanismDriverError is propagated
|
||||
to the caller, where the network will be deleted, triggering
|
||||
any required cleanup. There is no guarantee that all mechanism
|
||||
drivers are called in this case.
|
||||
"""
|
||||
self._call_on_drivers("create_network_postcommit", context)
|
||||
|
||||
def update_network_precommit(self, context):
|
||||
"""Notify all mechanism drivers of a network update.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver update_network_precommit call fails.
|
||||
|
||||
Called within the database transaction. If a mechanism driver
|
||||
raises an exception, then a MechanismDriverError is propogated
|
||||
to the caller, triggering a rollback. There is no guarantee
|
||||
that all mechanism drivers are called in this case.
|
||||
"""
|
||||
self._call_on_drivers("update_network_precommit", context)
|
||||
|
||||
def update_network_postcommit(self, context):
|
||||
"""Notify all mechanism drivers of a network update.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver update_network_postcommit call fails.
|
||||
|
||||
Called after the database transaction. If a mechanism driver
|
||||
raises an exception, then a MechanismDriverError is propagated
|
||||
to the caller, where an error is returned to the user. The
|
||||
user is expected to take the appropriate action, whether by
|
||||
retrying the call or deleting the network. There is no
|
||||
guarantee that all mechanism drivers are called in this case.
|
||||
"""
|
||||
self._call_on_drivers("update_network_postcommit", context)
|
||||
|
||||
def delete_network_precommit(self, context):
|
||||
"""Notify all mechanism drivers of a network deletion.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver delete_network_precommit call fails.
|
||||
|
||||
Called within the database transaction. If a mechanism driver
|
||||
raises an exception, then a MechanismDriverError is propogated
|
||||
to the caller, triggering a rollback. There is no guarantee
|
||||
that all mechanism drivers are called in this case.
|
||||
"""
|
||||
self._call_on_drivers("delete_network_precommit", context)
|
||||
|
||||
def delete_network_postcommit(self, context):
|
||||
"""Notify all mechanism drivers of a network deletion.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver delete_network_postcommit call fails.
|
||||
|
||||
Called after the database transaction. If any mechanism driver
|
||||
raises an error, then the error is logged but we continue to
|
||||
call every other mechanism driver. A MechanismDriverError is
|
||||
then reraised at the end to notify the caller of a failure. In
|
||||
general we expect the caller to ignore the error, as the
|
||||
network resource has already been deleted from the database
|
||||
and it doesn't make sense to undo the action by recreating the
|
||||
network.
|
||||
"""
|
||||
self._call_on_drivers("delete_network_postcommit", context,
|
||||
continue_on_failure=True)
|
||||
|
||||
def create_port_precommit(self, context):
|
||||
"""Notify all mechanism drivers of a port creation.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver create_port_precommit call fails.
|
||||
|
||||
Called within the database transaction. If a mechanism driver
|
||||
raises an exception, then a MechanismDriverError is propogated
|
||||
to the caller, triggering a rollback. There is no guarantee
|
||||
that all mechanism drivers are called in this case.
|
||||
"""
|
||||
self._call_on_drivers("create_port_precommit", context)
|
||||
|
||||
def create_port_postcommit(self, context):
|
||||
"""Notify all mechanism drivers of port creation.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver create_port_postcommit call fails.
|
||||
|
||||
Called after the database transaction. Errors raised by
|
||||
mechanism drivers are left to propogate to the caller, where
|
||||
the port will be deleted, triggering any required
|
||||
cleanup. There is no guarantee that all mechanism drivers are
|
||||
called in this case.
|
||||
"""
|
||||
self._call_on_drivers("create_port_postcommit", context)
|
||||
|
||||
def update_port_precommit(self, context):
|
||||
"""Notify all mechanism drivers of a port update.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver update_port_precommit call fails.
|
||||
|
||||
Called within the database transaction. If a mechanism driver
|
||||
raises an exception, then a MechanismDriverError is propogated
|
||||
to the caller, triggering a rollback. There is no guarantee
|
||||
that all mechanism drivers are called in this case.
|
||||
"""
|
||||
self._call_on_drivers("update_port_precommit", context)
|
||||
|
||||
def update_port_postcommit(self, context):
|
||||
"""Notify all mechanism drivers of a port update.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver update_port_postcommit call fails.
|
||||
|
||||
Called after the database transaction. If a mechanism driver
|
||||
raises an exception, then a MechanismDriverError is propagated
|
||||
to the caller, where an error is returned to the user. The
|
||||
user is expected to take the appropriate action, whether by
|
||||
retrying the call or deleting the port. There is no
|
||||
guarantee that all mechanism drivers are called in this case.
|
||||
"""
|
||||
self._call_on_drivers("update_port_postcommit", context)
|
||||
|
||||
def delete_port_precommit(self, context):
|
||||
"""Notify all mechanism drivers of a port deletion.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver delete_port_precommit call fails.
|
||||
|
||||
Called within the database transaction. If a mechanism driver
|
||||
raises an exception, then a MechanismDriverError is propogated
|
||||
to the caller, triggering a rollback. There is no guarantee
|
||||
that all mechanism drivers are called in this case.
|
||||
"""
|
||||
self._call_on_drivers("delete_port_precommit", context)
|
||||
|
||||
def delete_port_postcommit(self, context):
|
||||
"""Notify all mechanism drivers of a port deletion.
|
||||
|
||||
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
||||
if any mechanism driver delete_port_postcommit call fails.
|
||||
|
||||
Called after the database transaction. If any mechanism driver
|
||||
raises an error, then the error is logged but we continue to
|
||||
call every other mechanism driver. A MechanismDriverError is
|
||||
then reraised at the end to notify the caller of a failure. In
|
||||
general we expect the caller to ignore the error, as the
|
||||
port resource has already been deleted from the database
|
||||
and it doesn't make sense to undo the action by recreating the
|
||||
port.
|
||||
"""
|
||||
self._call_on_drivers("delete_port_postcommit", context,
|
||||
continue_on_failure=True)
|
||||
|
@ -30,12 +30,15 @@ from neutron.db import quota_db # noqa
|
||||
from neutron.db import securitygroups_rpc_base as sg_db_rpc
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.extensions import providernet as provider
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common import importutils
|
||||
from neutron.openstack.common import log
|
||||
from neutron.openstack.common import rpc as c_rpc
|
||||
from neutron.plugins.ml2.common import exceptions as ml2_exc
|
||||
from neutron.plugins.ml2 import config # noqa
|
||||
from neutron.plugins.ml2 import db
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2 import driver_context
|
||||
from neutron.plugins.ml2 import managers
|
||||
from neutron.plugins.ml2 import rpc
|
||||
|
||||
@ -140,7 +143,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
def _extend_network_dict_provider(self, context, network):
|
||||
id = network['id']
|
||||
segments = db.get_network_segments(context.session, id)
|
||||
segments = self.get_network_segments(context, id)
|
||||
if not segments:
|
||||
LOG.error(_("Network %s has no segments"), id)
|
||||
network[provider.NETWORK_TYPE] = None
|
||||
@ -172,7 +175,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
network_id = port['network_id']
|
||||
segments = db.get_network_segments(session, network_id)
|
||||
segments = self.get_network_segments(context, network_id)
|
||||
if not segments:
|
||||
LOG.warning(_("In _notify_port_updated() for port %(port_id), "
|
||||
"network %(network_id) has no segments"),
|
||||
@ -186,6 +189,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
segment[api.SEGMENTATION_ID],
|
||||
segment[api.PHYSICAL_NETWORK])
|
||||
|
||||
# TODO(apech): Need to override bulk operations
|
||||
|
||||
def create_network(self, context, network):
|
||||
attrs = network['network']
|
||||
segment = self._process_provider_create(context, attrs)
|
||||
@ -205,7 +210,19 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
# to TypeManager.
|
||||
db.add_network_segment(session, id, segment)
|
||||
self._extend_network_dict_provider(context, result)
|
||||
mech_context = driver_context.NetworkContext(self,
|
||||
context,
|
||||
result,
|
||||
segments=[segment])
|
||||
self.mechanism_manager.create_network_precommit(mech_context)
|
||||
|
||||
try:
|
||||
self.mechanism_manager.create_network_postcommit(mech_context)
|
||||
except ml2_exc.MechanismDriverError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_("mechanism_manager.create_network failed, "
|
||||
"deleting network '%s'"), result['id'])
|
||||
self.delete_network(context, result['id'])
|
||||
return result
|
||||
|
||||
def update_network(self, context, id, network):
|
||||
@ -213,12 +230,24 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
result = super(Ml2Plugin, self).update_network(context, id,
|
||||
network)
|
||||
self._process_l3_update(context, result, network['network'])
|
||||
self._extend_network_dict_provider(context, result)
|
||||
original_network = super(Ml2Plugin, self).get_network(context, id)
|
||||
updated_network = super(Ml2Plugin, self).update_network(context,
|
||||
id,
|
||||
network)
|
||||
self._process_l3_update(context, updated_network,
|
||||
network['network'])
|
||||
self._extend_network_dict_provider(context, updated_network)
|
||||
mech_context = driver_context.NetworkContext(
|
||||
self, context, updated_network,
|
||||
original_network=original_network)
|
||||
self.mechanism_manager.update_network_precommit(mech_context)
|
||||
|
||||
return result
|
||||
# TODO(apech) - handle errors raised by update_network, potentially
|
||||
# by re-calling update_network with the previous attributes. For
|
||||
# now the error is propogated to the caller, which is expected to
|
||||
# either undo/retry the operation or delete the resource.
|
||||
self.mechanism_manager.update_network_postcommit(mech_context)
|
||||
return updated_network
|
||||
|
||||
def get_network(self, context, id, fields=None):
|
||||
session = context.session
|
||||
@ -243,16 +272,35 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
return [self._fields(net, fields) for net in nets]
|
||||
|
||||
def delete_network(self, context, id):
|
||||
def get_network_segments(self, context, id):
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
segments = db.get_network_segments(session, id)
|
||||
return segments
|
||||
|
||||
def delete_network(self, context, id):
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
network = self.get_network(context, id)
|
||||
segments = self.get_network_segments(context, id)
|
||||
mech_context = driver_context.NetworkContext(self,
|
||||
context,
|
||||
network,
|
||||
segments=segments)
|
||||
self.mechanism_manager.delete_network_precommit(mech_context)
|
||||
super(Ml2Plugin, self).delete_network(context, id)
|
||||
for segment in segments:
|
||||
self.type_manager.release_segment(session, segment)
|
||||
# The segment records are deleted via cascade from the
|
||||
# network record, so explicit removal is not necessary.
|
||||
|
||||
try:
|
||||
self.mechanism_manager.delete_network_postcommit(mech_context)
|
||||
except ml2_exc.MechanismDriverError:
|
||||
# TODO(apech) - One or more mechanism driver failed to
|
||||
# delete the network. Ideally we'd notify the caller of
|
||||
# the fact that an error occurred.
|
||||
pass
|
||||
self.notifier.network_delete(context, id)
|
||||
|
||||
def create_port(self, context, port):
|
||||
@ -268,7 +316,16 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
result)
|
||||
self._process_port_create_security_group(context, result, sgids)
|
||||
self._extend_port_dict_binding(context, result)
|
||||
mech_context = driver_context.PortContext(self, context, result)
|
||||
self.mechanism_manager.create_port_precommit(mech_context)
|
||||
|
||||
try:
|
||||
self.mechanism_manager.create_port_postcommit(mech_context)
|
||||
except ml2_exc.MechanismDriverError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_("mechanism_manager.create_port failed, "
|
||||
"deleting port '%s'"), result['id'])
|
||||
self.delete_port(context, result['id'])
|
||||
self.notify_security_groups_member_updated(context, result)
|
||||
return result
|
||||
|
||||
@ -287,6 +344,16 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
attrs,
|
||||
updated_port)
|
||||
self._extend_port_dict_binding(context, updated_port)
|
||||
mech_context = driver_context.PortContext(
|
||||
self, context, updated_port,
|
||||
original_port=original_port)
|
||||
self.mechanism_manager.update_port_precommit(mech_context)
|
||||
|
||||
# TODO(apech) - handle errors raised by update_port, potentially
|
||||
# by re-calling update_port with the previous attributes. For
|
||||
# now the error is propogated to the caller, which is expected to
|
||||
# either undo/retry the operation or delete the resource.
|
||||
self.mechanism_manager.update_port_postcommit(mech_context)
|
||||
|
||||
need_port_update_notify |= self.is_security_group_member_updated(
|
||||
context, original_port, updated_port)
|
||||
@ -328,7 +395,16 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
with session.begin(subtransactions=True):
|
||||
self.disassociate_floatingips(context, id)
|
||||
port = self.get_port(context, id)
|
||||
mech_context = driver_context.PortContext(self, context, port)
|
||||
self.mechanism_manager.delete_port_precommit(mech_context)
|
||||
self._delete_port_security_group_bindings(context, id)
|
||||
super(Ml2Plugin, self).delete_port(context, id)
|
||||
|
||||
try:
|
||||
self.mechanism_manager.delete_port_postcommit(mech_context)
|
||||
except ml2_exc.MechanismDriverError:
|
||||
# TODO(apech) - One or more mechanism driver failed to
|
||||
# delete the port. Ideally we'd notify the caller of the
|
||||
# fact that an error occurred.
|
||||
pass
|
||||
self.notify_security_groups_member_updated(context, port)
|
||||
|
14
neutron/tests/unit/ml2/drivers/__init__.py
Normal file
14
neutron/tests/unit/ml2/drivers/__init__.py
Normal file
@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2013 OpenStack Foundation
|
||||
# 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.
|
83
neutron/tests/unit/ml2/drivers/mechanism_logger.py
Normal file
83
neutron/tests/unit/ml2/drivers/mechanism_logger.py
Normal file
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2013 OpenStack Foundation
|
||||
# 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.openstack.common import log
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class LoggerMechanismDriver(api.MechanismDriver):
|
||||
"""Mechanism driver that logs all calls and parameters made.
|
||||
|
||||
Generally used for testing and debugging.
|
||||
"""
|
||||
|
||||
def initialize(self):
|
||||
pass
|
||||
|
||||
def _log_network_call(self, method_name, context):
|
||||
LOG.info(_("%(method)s called with network settings %(current)s "
|
||||
"(original settings %(original)s) and "
|
||||
"network segments %(segments)s"),
|
||||
{'method': method_name,
|
||||
'current': context.current(),
|
||||
'original': context.original(),
|
||||
'segments': context.network_segments()})
|
||||
|
||||
def create_network_precommit(self, context):
|
||||
self._log_network_call("create_network_precommit", context)
|
||||
|
||||
def create_network_postcommit(self, context):
|
||||
self._log_network_call("create_network_postcommit", context)
|
||||
|
||||
def update_network_precommit(self, context):
|
||||
self._log_network_call("update_network_precommit", context)
|
||||
|
||||
def update_network_postcommit(self, context):
|
||||
self._log_network_call("update_network_postcommit", context)
|
||||
|
||||
def delete_network_precommit(self, context):
|
||||
self._log_network_call("delete_network_precommit", context)
|
||||
|
||||
def delete_network_postcommit(self, context):
|
||||
self._log_network_call("delete_network_postcommit", context)
|
||||
|
||||
def _log_port_call(self, method_name, context):
|
||||
network_context = context.network()
|
||||
LOG.info(_("%(method)s called with port settings %(current)s "
|
||||
"(original settings %(original)s) on network %(network)s"),
|
||||
{'method': method_name,
|
||||
'current': context.current(),
|
||||
'original': context.original(),
|
||||
'network': network_context.current()})
|
||||
|
||||
def create_port_precommit(self, context):
|
||||
self._log_port_call("create_port_precommit", context)
|
||||
|
||||
def create_port_postcommit(self, context):
|
||||
self._log_port_call("create_port_postcommit", context)
|
||||
|
||||
def update_port_precommit(self, context):
|
||||
self._log_port_call("update_port_precommit", context)
|
||||
|
||||
def update_port_postcommit(self, context):
|
||||
self._log_port_call("update_port_postcommit", context)
|
||||
|
||||
def delete_port_precommit(self, context):
|
||||
self._log_port_call("delete_port_precommit", context)
|
||||
|
||||
def delete_port_postcommit(self, context):
|
||||
self._log_port_call("delete_port_postcommit", context)
|
80
neutron/tests/unit/ml2/drivers/mechanism_test.py
Normal file
80
neutron/tests/unit/ml2/drivers/mechanism_test.py
Normal file
@ -0,0 +1,80 @@
|
||||
# Copyright (c) 2013 OpenStack Foundation
|
||||
# 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_api as api
|
||||
from neutron.plugins.ml2 import driver_context
|
||||
|
||||
|
||||
class TestMechanismDriver(api.MechanismDriver):
|
||||
"""Test mechanism driver for testing mechanism driver api."""
|
||||
|
||||
def initialize(self):
|
||||
pass
|
||||
|
||||
def _check_network_context(self, context, original_expected):
|
||||
assert(isinstance(context, driver_context.NetworkContext))
|
||||
assert(context.current())
|
||||
if original_expected:
|
||||
assert(context.original())
|
||||
else:
|
||||
assert(not context.original())
|
||||
assert(context.network_segments())
|
||||
|
||||
def create_network_precommit(self, context):
|
||||
self._check_network_context(context, False)
|
||||
|
||||
def create_network_postcommit(self, context):
|
||||
self._check_network_context(context, False)
|
||||
|
||||
def update_network_precommit(self, context):
|
||||
self._check_network_context(context, True)
|
||||
|
||||
def update_network_postcommit(self, context):
|
||||
self._check_network_context(context, True)
|
||||
|
||||
def delete_network_precommit(self, context):
|
||||
self._check_network_context(context, False)
|
||||
|
||||
def delete_network_postcommit(self, context):
|
||||
self._check_network_context(context, False)
|
||||
|
||||
def _check_port_context(self, context, original_expected):
|
||||
assert(isinstance(context, driver_context.PortContext))
|
||||
assert(context.current())
|
||||
if original_expected:
|
||||
assert(context.original())
|
||||
else:
|
||||
assert(not context.original())
|
||||
network_context = context.network()
|
||||
assert(network_context)
|
||||
self._check_network_context(network_context, False)
|
||||
|
||||
def create_port_precommit(self, context):
|
||||
self._check_port_context(context, False)
|
||||
|
||||
def create_port_postcommit(self, context):
|
||||
self._check_port_context(context, False)
|
||||
|
||||
def update_port_precommit(self, context):
|
||||
self._check_port_context(context, True)
|
||||
|
||||
def update_port_postcommit(self, context):
|
||||
self._check_port_context(context, True)
|
||||
|
||||
def delete_port_precommit(self, context):
|
||||
self._check_port_context(context, False)
|
||||
|
||||
def delete_port_postcommit(self, context):
|
||||
self._check_port_context(context, False)
|
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.plugins.ml2 import config as config
|
||||
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||
from neutron.tests.unit import test_db_plugin as test_plugin
|
||||
|
||||
@ -25,6 +26,13 @@ class Ml2PluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
_plugin_name = PLUGIN_NAME
|
||||
|
||||
def setUp(self):
|
||||
# Enable the test mechanism driver to ensure that
|
||||
# we can successfully call through to all mechanism
|
||||
# driver apis.
|
||||
config.cfg.CONF.set_override('mechanism_drivers',
|
||||
['logger', 'test'],
|
||||
'ml2')
|
||||
self.addCleanup(config.cfg.CONF.reset)
|
||||
super(Ml2PluginV2TestCase, self).setUp(PLUGIN_NAME)
|
||||
self.port_create_status = 'DOWN'
|
||||
|
||||
|
@ -107,6 +107,9 @@ neutron.ml2.type_drivers =
|
||||
flat = neutron.plugins.ml2.drivers.type_flat:FlatTypeDriver
|
||||
local = neutron.plugins.ml2.drivers.type_local:LocalTypeDriver
|
||||
vlan = neutron.plugins.ml2.drivers.type_vlan:VlanTypeDriver
|
||||
neutron.ml2.mechanism_drivers =
|
||||
logger = neutron.tests.unit.ml2.drivers.mechanism_logger:LoggerMechanismDriver
|
||||
test = neutron.tests.unit.ml2.drivers.mechanism_test:TestMechanismDriver
|
||||
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user