# 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
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.
--- /dev/null
+# 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.
--- /dev/null
+# 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.")
"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.")),
]
# 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
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
"""
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
--- /dev/null
+# 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
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
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
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()
+
+ 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.
- # TODO(rkukura): Define mechanism dispatch methods
+ 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)
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
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
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"),
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)
# 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):
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)
-
- return 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)
+
+ # 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
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):
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
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)
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)
--- /dev/null
+# 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.
--- /dev/null
+# 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)
--- /dev/null
+# 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)
# 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
_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'
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