subnet calls of ml2 mechanism driver

Implements blueprint ml2-mechanism-driver-subnet-calls

Change-Id: Ib9a8009975959692f46f80fb2a850fcc2b22debe
This commit is contained in:
Zang MingJie 2013-08-26 17:31:46 +08:00
parent 6ae42583eb
commit 317c8f318f
6 changed files with 351 additions and 18 deletions

View File

@ -164,6 +164,38 @@ class NetworkContext(object):
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.
@ -222,8 +254,6 @@ class MechanismDriver(object):
methods that are part of the database transaction.
"""
# TODO(apech): add calls for subnets
__metaclass__ = ABCMeta
@abstractmethod
@ -326,6 +356,96 @@ class MechanismDriver(object):
"""
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.

View File

@ -35,12 +35,15 @@ class NetworkContext(MechanismDriverContext, api.NetworkContext):
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(
@ -48,6 +51,22 @@ class NetworkContext(MechanismDriverContext, api.NetworkContext):
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,
@ -57,12 +76,15 @@ class PortContext(MechanismDriverContext, api.PortContext):
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:

View File

@ -270,6 +270,92 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
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.

View File

@ -311,6 +311,54 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
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

View File

@ -33,9 +33,9 @@ class LoggerMechanismDriver(api.MechanismDriver):
"(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)
@ -55,14 +55,39 @@ class LoggerMechanismDriver(api.MechanismDriver):
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)

View File

@ -25,12 +25,14 @@ class TestMechanismDriver(api.MechanismDriver):
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)
@ -50,15 +52,45 @@ class TestMechanismDriver(api.MechanismDriver):
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):