Pass the extension driver exception to plugin

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
This commit is contained in:
Yalei Wang 2015-08-06 01:03:20 +08:00
parent 6ee7b12d94
commit 73845d564c
3 changed files with 82 additions and 16 deletions

View 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.")

View 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)

View 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