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
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.")
from oslo_config import cfg
from oslo_log import log
+from oslo_utils import excutils
import six
import stevedore
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."""
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)
# under the License.
import mock
+import uuid
from neutron import context
from neutron import manager
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