]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Cisco Nexus: maximum recursion error in ConnectionContext.__del__
authorDane LeBlanc <leblancd@cisco.com>
Sat, 15 Mar 2014 00:32:10 +0000 (20:32 -0400)
committerGerrit Code Review <review@openstack.org>
Wed, 19 Mar 2014 00:20:43 +0000 (00:20 +0000)
If DevStack is configured for the Cisco Nexus plugin, the following
error is observed:

Exception RuntimeError: 'maximum recursion depth exceeded' in <bound
method ConnectionContext.__del__ of
<neutron.openstack.common.rpc.amqp.ConnectionContext object at
0x403a3d0>> ignored

The root cause of the problem is that the Cisco Nexus plugin's
PluginV2.__gettattr__ method, a model object is being passed
as a value for a unicode %s format mod. Because the neutron server
has "lazy gettext" (deferred interpretation of unicode objects) enabled,
this causes many layers of recursive calls to deepcopy.

The fix is to pass a string object for the unicode %s mod field.

Change-Id: I0a07a0ab417add68e44cb1bca722cb0b4a71205b
Closes-Bug: #1286565

neutron/plugins/cisco/network_plugin.py
neutron/tests/unit/cisco/test_network_plugin.py

index 7d500a3d590f68d7f0579a4944808a4e64b1af4c..6d9cdb1ce3cab73bf5fdd4499329c031029dee93 100644 (file)
@@ -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.
index fd3fac03106990f412bbabdfe89a27f3ffdef45f..b175c0afc39e71119ff6e1676f1394afc9566dec 100644 (file)
 # 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