pass
+class SubnetContext(object):
+ """Context passed to MechanismDrivers for changes to subnet resources.
+
+ A SubnetContext instance wraps a subnet 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 subnet.
+
+ Return the current state of the subnet, as defined by
+ NeutronPluginBaseV2.create_subnet and all extensions in the
+ ml2 plugin.
+ """
+ pass
+
+ @abstractproperty
+ def original(self):
+ """Return the original state of the subnet.
+
+ Return the original state of the subnet, prior to a call to
+ update_subnet. Method is only valid within calls to
+ update_subnet_precommit and update_subnet_postcommit.
+ """
+ pass
+
+
class PortContext(object):
"""Context passed to MechanismDrivers for changes to port resources.
methods that are part of the database transaction.
"""
- # TODO(apech): add calls for subnets
-
__metaclass__ = ABCMeta
@abstractmethod
"""
pass
+ def create_subnet_precommit(self, context):
+ """Allocate resources for a new subnet.
+
+ :param context: SubnetContext instance describing the new
+ subnet.
+
+ Create a new subnet, 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_subnet_postcommit(self, context):
+ """Create a subnet.
+
+ :param context: SubnetContext instance describing the new
+ subnet.
+
+ 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_subnet_precommit(self, context):
+ """Update resources of a subnet.
+
+ :param context: SubnetContext instance describing the new
+ state of the subnet, as well as the original state prior
+ to the update_subnet call.
+
+ Update values of a subnet, updating the associated resources
+ in the database. Called inside transaction context on session.
+ Raising an exception will result in rollback of the
+ transaction.
+
+ update_subnet_precommit is called for all changes to the
+ subnet 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_subnet_postcommit(self, context):
+ """Update a subnet.
+
+ :param context: SubnetContext instance describing the new
+ state of the subnet, as well as the original state prior
+ to the update_subnet 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_subnet_postcommit is called for all changes to the
+ subnet 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_subnet_precommit(self, context):
+ """Delete resources for a subnet.
+
+ :param context: SubnetContext instance describing the current
+ state of the subnet, prior to the call to delete it.
+
+ Delete subnet resources previously allocated by this
+ mechanism driver for a subnet. 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_subnet_postcommit(self, context):
+ """Delete a subnet.
+
+ :param context: SubnetContext instance describing the current
+ state of the subnet, 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.
self._original_network = original_network
self._segments = segments
+ @property
def current(self):
return self._network
+ @property
def original(self):
return self._original_network
+ @property
def network_segments(self):
if not self._segments:
self._segments = self._plugin.get_network_segments(
return self._segments
+class SubnetContext(MechanismDriverContext, api.SubnetContext):
+
+ def __init__(self, plugin, plugin_context, subnet, original_subnet=None):
+ super(SubnetContext, self).__init__(plugin, plugin_context)
+ self._subnet = subnet
+ self._original_subnet = original_subnet
+
+ @property
+ def current(self):
+ return self._subnet
+
+ @property
+ def original(self):
+ return self._original_subnet
+
+
class PortContext(MechanismDriverContext, api.PortContext):
def __init__(self, plugin, plugin_context, port,
self._original_port = original_port
self._network_context = None
+ @property
def current(self):
return self._port
+ @property
def original(self):
return self._original_port
+ @property
def network(self):
"""Return the NetworkContext associated with this port."""
if not self._network_context:
self._call_on_drivers("delete_network_postcommit", context,
continue_on_failure=True)
+ def create_subnet_precommit(self, context):
+ """Notify all mechanism drivers of a subnet creation.
+
+ :raises: neutron.plugins.ml2.common.MechanismDriverError
+ if any mechanism driver create_subnet_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_subnet_precommit", context)
+
+ def create_subnet_postcommit(self, context):
+ """Notify all mechanism drivers of subnet creation.
+
+ :raises: neutron.plugins.ml2.common.MechanismDriverError
+ if any mechanism driver create_subnet_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 subnet 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_subnet_postcommit", context)
+
+ def update_subnet_precommit(self, context):
+ """Notify all mechanism drivers of a subnet update.
+
+ :raises: neutron.plugins.ml2.common.MechanismDriverError
+ if any mechanism driver update_subnet_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_subnet_precommit", context)
+
+ def update_subnet_postcommit(self, context):
+ """Notify all mechanism drivers of a subnet update.
+
+ :raises: neutron.plugins.ml2.common.MechanismDriverError
+ if any mechanism driver update_subnet_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 subnet. There is no
+ guarantee that all mechanism drivers are called in this case.
+ """
+ self._call_on_drivers("update_subnet_postcommit", context)
+
+ def delete_subnet_precommit(self, context):
+ """Notify all mechanism drivers of a subnet deletion.
+
+ :raises: neutron.plugins.ml2.common.MechanismDriverError
+ if any mechanism driver delete_subnet_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_subnet_precommit", context)
+
+ def delete_subnet_postcommit(self, context):
+ """Notify all mechanism drivers of a subnet deletion.
+
+ :raises: neutron.plugins.ml2.common.MechanismDriverError
+ if any mechanism driver delete_subnet_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
+ subnet resource has already been deleted from the database
+ and it doesn't make sense to undo the action by recreating the
+ subnet.
+ """
+ self._call_on_drivers("delete_subnet_postcommit", context,
+ continue_on_failure=True)
+
def create_port_precommit(self, context):
"""Notify all mechanism drivers of a port creation.
pass
self.notifier.network_delete(context, id)
+ def create_subnet(self, context, subnet):
+ session = context.session
+ with session.begin(subtransactions=True):
+ result = super(Ml2Plugin, self).create_subnet(context, subnet)
+ mech_context = driver_context.SubnetContext(self, context, result)
+ self.mechanism_manager.create_subnet_precommit(mech_context)
+
+ try:
+ self.mechanism_manager.create_subnet_postcommit(mech_context)
+ except ml2_exc.MechanismDriverError:
+ with excutils.save_and_reraise_exception():
+ LOG.error(_("mechanism_manager.create_subnet failed, "
+ "deleting subnet '%s'"), result['id'])
+ self.delete_subnet(context, result['id'])
+ return result
+
+ def update_subnet(self, context, id, subnet):
+ session = context.session
+ with session.begin(subtransactions=True):
+ original_subnet = super(Ml2Plugin, self).get_subnet(context, id)
+ updated_subnet = super(Ml2Plugin, self).update_subnet(
+ context, id, subnet)
+ mech_context = driver_context.SubnetContext(
+ self, context, updated_subnet, original_subnet=original_subnet)
+ self.mechanism_manager.update_subnet_precommit(mech_context)
+
+ # TODO(apech) - handle errors raised by update_subnet, potentially
+ # by re-calling update_subnet 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_subnet_postcommit(mech_context)
+ return updated_subnet
+
+ def delete_subnet(self, context, id):
+ session = context.session
+ with session.begin(subtransactions=True):
+ subnet = self.get_subnet(context, id)
+ mech_context = driver_context.SubnetContext(self, context, subnet)
+ self.mechanism_manager.delete_subnet_precommit(mech_context)
+ super(Ml2Plugin, self).delete_subnet(context, id)
+ try:
+ self.mechanism_manager.delete_subnet_postcommit(mech_context)
+ except ml2_exc.MechanismDriverError:
+ # TODO(apech) - One or more mechanism driver failed to
+ # delete the subnet. Ideally we'd notify the caller of
+ # the fact that an error occurred.
+ pass
+
def create_port(self, context, port):
attrs = port['port']
attrs['status'] = const.PORT_STATUS_DOWN
"(original settings %(original)s) and "
"network segments %(segments)s"),
{'method': method_name,
- 'current': context.current(),
- 'original': context.original(),
- 'segments': context.network_segments()})
+ '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 delete_network_postcommit(self, context):
self._log_network_call("delete_network_postcommit", context)
+ def _log_subnet_call(self, method_name, context):
+ LOG.info(_("%(method)s called with subnet settings %(current)s "
+ "(original settings %(original)s)"),
+ {'method': method_name,
+ 'current': context.current,
+ 'original': context.original})
+
+ def create_subnet_precommit(self, context):
+ self._log_subnet_call("create_subnet_precommit", context)
+
+ def create_subnet_postcommit(self, context):
+ self._log_subnet_call("create_subnet_postcommit", context)
+
+ def update_subnet_precommit(self, context):
+ self._log_subnet_call("update_subnet_precommit", context)
+
+ def update_subnet_postcommit(self, context):
+ self._log_subnet_call("update_subnet_postcommit", context)
+
+ def delete_subnet_precommit(self, context):
+ self._log_subnet_call("delete_subnet_precommit", context)
+
+ def delete_subnet_postcommit(self, context):
+ self._log_subnet_call("delete_subnet_postcommit", context)
+
def _log_port_call(self, method_name, context):
- network_context = context.network()
+ 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()})
+ '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 _check_network_context(self, context, original_expected):
assert(isinstance(context, driver_context.NetworkContext))
- assert(context.current())
+ assert(isinstance(context.current, dict))
+ assert(context.current['id'] is not None)
if original_expected:
- assert(context.original())
+ assert(isinstance(context.original, dict))
+ assert(context.current['id'] == context.original['id'])
else:
- assert(not context.original())
- assert(context.network_segments())
+ assert(not context.original)
+ assert(context.network_segments)
def create_network_precommit(self, context):
self._check_network_context(context, False)
def delete_network_postcommit(self, context):
self._check_network_context(context, False)
+ def _check_subnet_context(self, context, original_expected):
+ assert(isinstance(context, driver_context.SubnetContext))
+ assert(isinstance(context.current, dict))
+ assert(context.current['id'] is not None)
+ if original_expected:
+ assert(isinstance(context.original, dict))
+ assert(context.current['id'] == context.original['id'])
+ else:
+ assert(not context.original)
+
+ def create_subnet_precommit(self, context):
+ self._check_subnet_context(context, False)
+
+ def create_subnet_postcommit(self, context):
+ self._check_subnet_context(context, False)
+
+ def update_subnet_precommit(self, context):
+ self._check_subnet_context(context, True)
+
+ def update_subnet_postcommit(self, context):
+ self._check_subnet_context(context, True)
+
+ def delete_subnet_precommit(self, context):
+ self._check_subnet_context(context, False)
+
+ def delete_subnet_postcommit(self, context):
+ self._check_subnet_context(context, False)
+
def _check_port_context(self, context, original_expected):
assert(isinstance(context, driver_context.PortContext))
- assert(context.current())
+ assert(isinstance(context.current, dict))
+ assert(context.current['id'] is not None)
if original_expected:
- assert(context.original())
+ assert(isinstance(context.original, dict))
+ assert(context.current['id'] == context.original['id'])
else:
- assert(not context.original())
- network_context = context.network()
- assert(network_context)
+ assert(not context.original)
+ network_context = context.network
+ assert(isinstance(network_context, driver_context.NetworkContext))
self._check_network_context(network_context, False)
def create_port_precommit(self, context):