diff --git a/neutron/plugins/cisco/network_plugin.py b/neutron/plugins/cisco/network_plugin.py index 7d500a3d590..6d9cdb1ce3c 100644 --- a/neutron/plugins/cisco/network_plugin.py +++ b/neutron/plugins/cisco/network_plugin.py @@ -67,7 +67,8 @@ class PluginV2(db_base_plugin_v2.NeutronDbPluginV2): def __init__(self): """Load the model class.""" - self._model = importutils.import_object(config.CISCO.model_class) + self._model_name = config.CISCO.model_class + self._model = importutils.import_object(self._model_name) native_bulk_attr_name = ("_%s__native_bulk_support" % self._model.__class__.__name__) self.__native_bulk_support = getattr(self._model, @@ -108,10 +109,10 @@ class PluginV2(db_base_plugin_v2.NeutronDbPluginV2): return getattr(self._model, name) else: # Must make sure we re-raise the error that led us here, since - # otherwise getattr() and even hasattr() doesn't work corretly. + # otherwise getattr() and even hasattr() doesn't work correctly. raise AttributeError( _("'%(model)s' object has no attribute '%(name)s'") % - {'model': self._model, 'name': name}) + {'model': self._model_name, 'name': name}) def _extend_fault_map(self): """Extend the Neutron Fault Map for Cisco exceptions. diff --git a/neutron/tests/unit/cisco/test_network_plugin.py b/neutron/tests/unit/cisco/test_network_plugin.py index fd3fac03106..b175c0afc39 100644 --- a/neutron/tests/unit/cisco/test_network_plugin.py +++ b/neutron/tests/unit/cisco/test_network_plugin.py @@ -14,10 +14,12 @@ # limitations under the License. import contextlib +import copy import inspect import logging import mock +import six import webob.exc as wexc from neutron.api import extensions @@ -30,6 +32,7 @@ from neutron.db import l3_db from neutron.extensions import portbindings from neutron.extensions import providernet as provider from neutron.manager import NeutronManager +from neutron.openstack.common import gettextutils from neutron.plugins.cisco.common import cisco_constants as const from neutron.plugins.cisco.common import cisco_exceptions as c_exc from neutron.plugins.cisco.common import config as cisco_config @@ -229,6 +232,38 @@ class CiscoNetworkPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase): self.assertEqual(status, expected_http) +class TestCiscoGetAttribute(CiscoNetworkPluginV2TestCase): + + def test_get_unsupported_attr_in_lazy_gettext_mode(self): + """Test get of unsupported attribute in lazy gettext mode. + + This test also checks that this operation does not cause + excessive nesting of calls to deepcopy. + """ + plugin = NeutronManager.get_plugin() + + def _lazy_gettext(msg): + return gettextutils.Message(msg, domain='neutron') + + with mock.patch.dict(six.moves.builtins.__dict__, + {'_': _lazy_gettext}): + self.nesting_count = 0 + + def _count_nesting(*args, **kwargs): + self.nesting_count += 1 + + with mock.patch.object(copy, 'deepcopy', + side_effect=_count_nesting, + wraps=copy.deepcopy): + self.assertRaises(AttributeError, getattr, plugin, + 'an_unsupported_attribute') + # If there were no nested calls to deepcopy, then the total + # number of calls to deepcopy should be 2 (1 call for + # each mod'd field in the AttributeError message raised + # by the plugin). + self.assertEqual(self.nesting_count, 2) + + class TestCiscoBasicGet(CiscoNetworkPluginV2TestCase, test_db_plugin.TestBasicGet): pass