]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
subnet calls of ml2 mechanism driver
authorZang MingJie <zealot0630@gmail.com>
Mon, 26 Aug 2013 09:31:46 +0000 (17:31 +0800)
committerZang MingJie <zealot0630@gmail.com>
Tue, 27 Aug 2013 11:24:55 +0000 (19:24 +0800)
Implements blueprint ml2-mechanism-driver-subnet-calls

Change-Id: Ib9a8009975959692f46f80fb2a850fcc2b22debe

neutron/plugins/ml2/driver_api.py
neutron/plugins/ml2/driver_context.py
neutron/plugins/ml2/managers.py
neutron/plugins/ml2/plugin.py
neutron/tests/unit/ml2/drivers/mechanism_logger.py
neutron/tests/unit/ml2/drivers/mechanism_test.py

index 5bc7ca8393f6c309e8bf41741d9ac99471b6ae18..546c23c940d22542216c26eac18dea6f7f21ff8f 100644 (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.
 
index a5cf6b743d5c6522f5665bd19466233063b5b13f..1f552d2d82d7e3870ec293d6cb8dc4037a48c172 100644 (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:
index 3e30e534c8805d6cb4a3280d0ff018fcee954d16..36c4ab988d06a99eb9aa5a1b3e9d54ef1a34e7ca 100644 (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.
 
index bb42aab76b9a2ca724ffdd3f0f510fc7a601a1dc..c14243dddb6ac5de648f7c8ca4e96a57fb932c43 100644 (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
index 03b4316e6be2feb3fdd3d40840c0cf6bbb490eb1..c900fdfe10836edd76e383484604d0d72970e1dc 100644 (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)
index bd496722675722fadd95157edc3545d90a3f439c..bc1ce6f6df6bc745542e5760132feea840c7898f 100644 (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):