]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Pass the extension driver exception to plugin
authorYalei Wang <yalei.wang@intel.com>
Wed, 5 Aug 2015 17:03:20 +0000 (01:03 +0800)
committerYalei Wang <yalei.wang@intel.com>
Fri, 7 Aug 2015 09:16:17 +0000 (17:16 +0800)
The extension driver is intercepted by driver manager currently. It will
cover the errors/exceptions happened in extension drivers. The extension
process will continue even if preceding extension driver get a wrong/useless
extended result, or even no result.
This patch make process_[create|update]_<resource>() and
extend_<resource>_dict() methods return the exception, and log it with proper
level respectively, and also include a minor optimization for the
extend_<resource>_dict() methods.

Change-Id: I20a249c47b58292125476bc44b2372ca959509e3
Closes-Bug: #1468990

neutron/plugins/ml2/common/exceptions.py
neutron/plugins/ml2/managers.py
neutron/tests/unit/plugins/ml2/test_extension_driver_api.py

index ed94b1e1f14072d328d3daa0c24b804798d64652..166711d8ee9cf6ca288dbb645191bfb3e1d6d3dd 100644 (file)
@@ -21,3 +21,8 @@ from neutron.common import exceptions
 class MechanismDriverError(exceptions.NeutronException):
     """Mechanism driver call failed."""
     message = _("%(method)s failed.")
+
+
+class ExtensionDriverError(exceptions.InvalidInput):
+    """Extension driver call failed."""
+    message = _("Extension %(driver)s failed.")
index ef5b868cfc623088edab4dc7281ac9279aaa6f75..4f678b2265abd2356f0482c4b97f3a242b1ef450 100644 (file)
@@ -15,6 +15,7 @@
 
 from oslo_config import cfg
 from oslo_log import log
+from oslo_utils import excutils
 import six
 import stevedore
 
@@ -764,10 +765,10 @@ class ExtensionManager(stevedore.named.NamedExtensionManager):
             try:
                 getattr(driver.obj, method_name)(plugin_context, data, result)
             except Exception:
-                LOG.exception(
-                    _LE("Extension driver '%(name)s' failed in %(method)s"),
-                    {'name': driver.name, 'method': method_name}
-                )
+                with excutils.save_and_reraise_exception():
+                    LOG.info(_LI("Extension driver '%(name)s' failed in "
+                             "%(method)s"),
+                             {'name': driver.name, 'method': method_name})
 
     def process_create_network(self, plugin_context, data, result):
         """Notify all extension drivers during network creation."""
@@ -799,23 +800,30 @@ class ExtensionManager(stevedore.named.NamedExtensionManager):
         self._call_on_ext_drivers("process_update_port", plugin_context,
                                   data, result)
 
+    def _call_on_dict_driver(self, method_name, session, base_model, result):
+        for driver in self.ordered_ext_drivers:
+            try:
+                getattr(driver.obj, method_name)(session, base_model, result)
+            except Exception:
+                LOG.error(_LE("Extension driver '%(name)s' failed in "
+                          "%(method)s"),
+                          {'name': driver.name, 'method': method_name})
+                raise ml2_exc.ExtensionDriverError(driver=driver.name)
+
+            LOG.debug("%(method)s succeeded for driver %(driver)s",
+                      {'method': method_name, 'driver': driver.name})
+
     def extend_network_dict(self, session, base_model, result):
         """Notify all extension drivers to extend network dictionary."""
-        for driver in self.ordered_ext_drivers:
-            driver.obj.extend_network_dict(session, base_model, result)
-            LOG.debug("Extended network dict for driver '%(drv)s'",
-                      {'drv': driver.name})
+        self._call_on_dict_driver("extend_network_dict", session, base_model,
+                                  result)
 
     def extend_subnet_dict(self, session, base_model, result):
         """Notify all extension drivers to extend subnet dictionary."""
-        for driver in self.ordered_ext_drivers:
-            driver.obj.extend_subnet_dict(session, base_model, result)
-            LOG.debug("Extended subnet dict for driver '%(drv)s'",
-                      {'drv': driver.name})
+        self._call_on_dict_driver("extend_subnet_dict", session, base_model,
+                                  result)
 
     def extend_port_dict(self, session, base_model, result):
         """Notify all extension drivers to extend port dictionary."""
-        for driver in self.ordered_ext_drivers:
-            driver.obj.extend_port_dict(session, base_model, result)
-            LOG.debug("Extended port dict for driver '%(drv)s'",
-                      {'drv': driver.name})
+        self._call_on_dict_driver("extend_port_dict", session, base_model,
+                                  result)
index bff70fecb58f6e964464b2b650da4cb91325a07d..78d635338581b6c57207ac1d96e39b3465888ece 100644 (file)
@@ -11,6 +11,7 @@
 #    under the License.
 
 import mock
+import uuid
 
 from neutron import context
 from neutron import manager
@@ -31,6 +32,58 @@ class ExtensionDriverTestCase(test_plugin.Ml2PluginV2TestCase):
         self._plugin = manager.NeutronManager.get_plugin()
         self._ctxt = context.get_admin_context()
 
+    def _verify_network_create(self, code, exc_reason):
+        tenant_id = str(uuid.uuid4())
+        data = {'network': {'name': 'net1',
+                            'tenant_id': tenant_id}}
+        req = self.new_create_request('networks', data)
+        res = req.get_response(self.api)
+        self.assertEqual(code, res.status_int)
+
+        network = self.deserialize(self.fmt, res)
+        if exc_reason:
+            self.assertEqual(exc_reason,
+                             network['NeutronError']['type'])
+
+        return (network, tenant_id)
+
+    def _verify_network_update(self, network, code, exc_reason):
+        net_id = network['network']['id']
+        new_name = 'a_brand_new_name'
+        data = {'network': {'name': new_name}}
+        req = self.new_update_request('networks', data, net_id)
+        res = req.get_response(self.api)
+        self.assertEqual(code, res.status_int)
+        error = self.deserialize(self.fmt, res)
+        self.assertEqual(exc_reason,
+                         error['NeutronError']['type'])
+
+    def test_faulty_process_create(self):
+        with mock.patch.object(ext_test.TestExtensionDriver,
+                               'process_create_network',
+                               side_effect=TypeError):
+            net, tenant_id = self._verify_network_create(500,
+                                                    'HTTPInternalServerError')
+            # Verify the operation is rolled back
+            query_params = "tenant_id=%s" % tenant_id
+            nets = self._list('networks', query_params=query_params)
+            self.assertFalse(nets['networks'])
+
+    def test_faulty_process_update(self):
+        with mock.patch.object(ext_test.TestExtensionDriver,
+                               'process_update_network',
+                               side_effect=TypeError):
+            network, tid = self._verify_network_create(201, None)
+            self._verify_network_update(network, 500,
+                                        'HTTPInternalServerError')
+
+    def test_faulty_extend_dict(self):
+        with mock.patch.object(ext_test.TestExtensionDriver,
+                               'extend_network_dict',
+                               side_effect=TypeError):
+            network, tid = self._verify_network_create(201, None)
+            self._verify_network_update(network, 400, 'ExtensionDriverError')
+
     def test_network_attr(self):
         with self.network() as network:
             # Test create network