Cisco Nexus: maximum recursion error in ConnectionContext.__del__

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
This commit is contained in:
Dane LeBlanc 2014-03-14 20:32:10 -04:00 committed by Gerrit Code Review
parent 0009e474e2
commit c36ddaf930
2 changed files with 39 additions and 3 deletions

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

View File

@ -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