Add mechanism driver error details to MechanismDriverError

Now the ML2 core plugin maps driver errors to MechanismDriverError
and hides the error details from the caller.

This patch change MechanismDriverError from an instance of
NeutronException to an instance of MultipleExceptions. Add add
exceptions from mechanism driver as inner_exceptions of
MultipleExceptions. As a result, the api layer will unwrap the
MechanismDriverError and return the real error to client.

Change-Id: I3a46932848d59f7f027640bfb598650f064b0a12
Closes-bug: #1273730
This commit is contained in:
Hong Hui Xiao 2016-08-20 09:06:10 -04:00
parent 665fd24623
commit 8398ec0d77
5 changed files with 56 additions and 28 deletions

View File

@ -18,11 +18,18 @@
from neutron_lib import exceptions
from neutron._i18n import _
from neutron.common import exceptions as n_exc
class MechanismDriverError(exceptions.NeutronException):
class MechanismDriverError(n_exc.MultipleExceptions):
"""Mechanism driver call failed."""
message = _("%(method)s failed.")
def __init__(self, method, errors=None):
# The message is not used by api, because api will unwrap
# MultipleExceptions and return inner exceptions. Keep it
# for backward-compatibility, in case other code use it.
self.message = _("%s failed.") % method
super(MechanismDriverError, self).__init__(errors)
class ExtensionDriverError(exceptions.InvalidInput):

View File

@ -14,13 +14,13 @@
# under the License.
from neutron_lib import constants as const
from neutron_lib import exceptions
from oslo_config import cfg
from oslo_log import log as logging
from neutron._i18n import _LW
from neutron._i18n import _, _LW
from neutron import context as n_context
from neutron.db import api as db_api
from neutron.plugins.ml2.common import exceptions as ml2_exc
from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2.drivers.l2pop import config # noqa
from neutron.plugins.ml2.drivers.l2pop import db as l2pop_db
@ -114,9 +114,9 @@ class L2populationMechanismDriver(api.MechanismDriver):
if (orig['mac_address'] != port['mac_address'] and
context.status == const.PORT_STATUS_ACTIVE):
LOG.warning(_LW("unable to modify mac_address of ACTIVE port "
"%s"), port['id'])
raise ml2_exc.MechanismDriverError(method='update_port_precommit')
msg = _("unable to modify mac_address of ACTIVE port "
"%s") % port['id']
raise exceptions.InvalidInput(error_message=msg)
def update_port_postcommit(self, context):
port = context.current

View File

@ -404,7 +404,7 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
raise_db_retriable=False. See neutron.db.api.is_retriable for
what db exception is retriable
"""
error = False
errors = []
for driver in self.ordered_mech_drivers:
try:
getattr(driver.obj, method_name)(context)
@ -419,12 +419,13 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
_LE("Mechanism driver '%(name)s' failed in %(method)s"),
{'name': driver.name, 'method': method_name}
)
error = True
errors.append(e)
if not continue_on_failure:
break
if error:
if errors:
raise ml2_exc.MechanismDriverError(
method=method_name
method=method_name,
errors=errors
)
def create_network_precommit(self, context):

View File

@ -15,6 +15,7 @@
import mock
from neutron_lib import constants
from neutron_lib import exceptions
from oslo_serialization import jsonutils
import testtools
@ -24,7 +25,6 @@ from neutron import context
from neutron.extensions import portbindings
from neutron.extensions import providernet as pnet
from neutron import manager
from neutron.plugins.ml2.common import exceptions as ml2_exc
from neutron.plugins.ml2 import driver_context
from neutron.plugins.ml2.drivers.l2pop import db as l2pop_db
from neutron.plugins.ml2.drivers.l2pop import mech_driver as l2pop_mech_driver
@ -1022,5 +1022,5 @@ class TestL2PopulationMechDriver(base.BaseTestCase):
original_port=original_port)
mech_driver = l2pop_mech_driver.L2populationMechanismDriver()
with testtools.ExpectedException(ml2_exc.MechanismDriverError):
with testtools.ExpectedException(exceptions.InvalidInput):
mech_driver.update_port_precommit(ctx)

View File

@ -1991,18 +1991,22 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
def test_create_network_faulty(self):
err_msg = "Some errors"
with mock.patch.object(mech_test.TestMechanismDriver,
'create_network_postcommit',
side_effect=ml2_exc.MechanismDriverError):
side_effect=(exc.InvalidInput(
error_message=err_msg))):
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(500, res.status_int)
self.assertEqual(400, res.status_int)
error = self.deserialize(self.fmt, res)
self.assertEqual('MechanismDriverError',
self.assertEqual('InvalidInput',
error['NeutronError']['type'])
# Check the client can see the root cause of error.
self.assertIn(err_msg, error['NeutronError']['message'])
query_params = "tenant_id=%s" % tenant_id
nets = self._list('networks', query_params=query_params)
self.assertFalse(nets['networks'])
@ -2032,9 +2036,11 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
def test_update_network_faulty(self):
err_msg = "Some errors"
with mock.patch.object(mech_test.TestMechanismDriver,
'update_network_postcommit',
side_effect=ml2_exc.MechanismDriverError):
side_effect=(exc.InvalidInput(
error_message=err_msg))):
with mock.patch.object(mech_logger.LoggerMechanismDriver,
'update_network_postcommit') as unp:
@ -2050,10 +2056,12 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
data = {'network': {'name': new_name}}
req = self.new_update_request('networks', data, net_id)
res = req.get_response(self.api)
self.assertEqual(500, res.status_int)
self.assertEqual(400, res.status_int)
error = self.deserialize(self.fmt, res)
self.assertEqual('MechanismDriverError',
self.assertEqual('InvalidInput',
error['NeutronError']['type'])
# Check the client can see the root cause of error.
self.assertIn(err_msg, error['NeutronError']['message'])
# Test if other mechanism driver was called
self.assertTrue(unp.called)
net = self._show('networks', net_id)
@ -2063,9 +2071,11 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
def test_create_subnet_faulty(self):
err_msg = "Some errors"
with mock.patch.object(mech_test.TestMechanismDriver,
'create_subnet_postcommit',
side_effect=ml2_exc.MechanismDriverError):
side_effect=(exc.InvalidInput(
error_message=err_msg))):
with self.network() as network:
net_id = network['network']['id']
@ -2078,10 +2088,12 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
'gateway_ip': '10.0.20.1'}}
req = self.new_create_request('subnets', data)
res = req.get_response(self.api)
self.assertEqual(500, res.status_int)
self.assertEqual(400, res.status_int)
error = self.deserialize(self.fmt, res)
self.assertEqual('MechanismDriverError',
self.assertEqual('InvalidInput',
error['NeutronError']['type'])
# Check the client can see the root cause of error.
self.assertIn(err_msg, error['NeutronError']['message'])
query_params = "network_id=%s" % net_id
subnets = self._list('subnets', query_params=query_params)
self.assertFalse(subnets['subnets'])
@ -2119,9 +2131,11 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
def test_update_subnet_faulty(self):
err_msg = "Some errors"
with mock.patch.object(mech_test.TestMechanismDriver,
'update_subnet_postcommit',
side_effect=ml2_exc.MechanismDriverError):
side_effect=(exc.InvalidInput(
error_message=err_msg))):
with mock.patch.object(mech_logger.LoggerMechanismDriver,
'update_subnet_postcommit') as usp:
@ -2143,10 +2157,12 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
data = {'subnet': {'name': new_name}}
req = self.new_update_request('subnets', data, subnet_id)
res = req.get_response(self.api)
self.assertEqual(500, res.status_int)
self.assertEqual(400, res.status_int)
error = self.deserialize(self.fmt, res)
self.assertEqual('MechanismDriverError',
self.assertEqual('InvalidInput',
error['NeutronError']['type'])
# Check the client can see the root cause of error.
self.assertIn(err_msg, error['NeutronError']['message'])
# Test if other mechanism driver was called
self.assertTrue(usp.called)
subnet = self._show('subnets', subnet_id)
@ -2156,9 +2172,11 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
def test_create_port_faulty(self):
err_msg = "Some errors"
with mock.patch.object(mech_test.TestMechanismDriver,
'create_port_postcommit',
side_effect=ml2_exc.MechanismDriverError):
side_effect=(exc.InvalidInput(
error_message=err_msg))):
with self.network() as network:
net_id = network['network']['id']
@ -2170,10 +2188,12 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
'fixed_ips': []}}
req = self.new_create_request('ports', data)
res = req.get_response(self.api)
self.assertEqual(500, res.status_int)
self.assertEqual(400, res.status_int)
error = self.deserialize(self.fmt, res)
self.assertEqual('MechanismDriverError',
self.assertEqual('InvalidInput',
error['NeutronError']['type'])
# Check the client can see the root cause of error.
self.assertIn(err_msg, error['NeutronError']['message'])
query_params = "network_id=%s" % net_id
ports = self._list('ports', query_params=query_params)
self.assertFalse(ports['ports'])