diff --git a/nova/tests/fixtures/nova.py b/nova/tests/fixtures/nova.py index 322cc7bedf09..f415e7f84d00 100644 --- a/nova/tests/fixtures/nova.py +++ b/nova/tests/fixtures/nova.py @@ -17,6 +17,7 @@ """Fixtures for Nova tests.""" import collections +import contextlib from contextlib import contextmanager import functools import logging as std_logging @@ -28,6 +29,7 @@ import fixtures import futurist import mock from openstack import service_description +from oslo_concurrency import lockutils from oslo_config import cfg from oslo_db import exception as db_exc from oslo_db.sqlalchemy import enginefacade @@ -405,7 +407,7 @@ class CellDatabases(fixtures.Fixture): # to point to a cell, we need to take an exclusive lock to # prevent any other calls to get_context_manager() until we # reset to the default. - self._cell_lock = utils.ReaderWriterLock() + self._cell_lock = ReaderWriterLock() def _cache_schema(self, connection_str): # NOTE(melwitt): See the regular Database fixture for why @@ -1721,3 +1723,37 @@ class PropagateTestCaseIdToChildEventlets(fixtures.Fixture): # our initialization to the child eventlet self.useFixture( fixtures.MonkeyPatch('nova.utils.spawn_n', wrapped_spawn_n)) + + +class ReaderWriterLock(lockutils.ReaderWriterLock): + """Wrap oslo.concurrency lockutils.ReaderWriterLock to support eventlet. + + As of fasteners >= 0.15, the workaround code to use eventlet.getcurrent() + if eventlet patching is detected has been removed and + threading.current_thread is being used instead. Although we are running in + a greenlet in our test environment, we are not running in a greenlet of + type GreenThread. A GreenThread is created by calling eventlet.spawn() and + spawn() is not used to run our tests. At the time of this writing, the + eventlet patched threading.current_thread() method falls back to the + original unpatched current_thread() method if it is not called from a + GreenThead [1] and that breaks our tests involving this fixture. + + We can work around this by patching threading.current_thread() with + eventlet.getcurrent() during creation of the lock object, if we detect we + are eventlet patched. If we are not eventlet patched, we use a no-op + context manager. + + Note: this wrapper should be used for any ReaderWriterLock because any lock + may possibly be running inside a plain greenlet created by spawn_n(). + + See https://github.com/eventlet/eventlet/issues/731 for details. + + [1] https://github.com/eventlet/eventlet/blob/v0.32.0/eventlet/green/threading.py#L128 # noqa + """ + + def __init__(self, *a, **kw): + eventlet_patched = eventlet.patcher.is_monkey_patched('thread') + mpatch = fixtures.MonkeyPatch( + 'threading.current_thread', eventlet.getcurrent) + with mpatch if eventlet_patched else contextlib.ExitStack(): + super().__init__(*a, **kw) diff --git a/nova/utils.py b/nova/utils.py index 308cf6632b5e..ec5e6c924806 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -29,7 +29,6 @@ import shutil import tempfile import eventlet -import fixtures from keystoneauth1 import loading as ks_loading import netaddr from openstack import connection @@ -1144,37 +1143,3 @@ def run_once(message, logger, cleanup=None): wrapper.reset = functools.partial(reset, wrapper) return wrapper return outer_wrapper - - -class ReaderWriterLock(lockutils.ReaderWriterLock): - """Wrap oslo.concurrency lockutils.ReaderWriterLock to support eventlet. - - As of fasteners >= 0.15, the workaround code to use eventlet.getcurrent() - if eventlet patching is detected has been removed and - threading.current_thread is being used instead. Although we are running in - a greenlet in our test environment, we are not running in a greenlet of - type GreenThread. A GreenThread is created by calling eventlet.spawn() and - spawn() is not used to run our tests. At the time of this writing, the - eventlet patched threading.current_thread() method falls back to the - original unpatched current_thread() method if it is not called from a - GreenThead [1] and that breaks our tests involving this fixture. - - We can work around this by patching threading.current_thread() with - eventlet.getcurrent() during creation of the lock object, if we detect we - are eventlet patched. If we are not eventlet patched, we use a no-op - context manager. - - Note: this wrapper should be used for any ReaderWriterLock because any lock - may possibly be running inside a plain greenlet created by spawn_n(). - - See https://github.com/eventlet/eventlet/issues/731 for details. - - [1] https://github.com/eventlet/eventlet/blob/v0.32.0/eventlet/green/threading.py#L128 # noqa - """ - - def __init__(self, *a, **kw): - eventlet_patched = eventlet.patcher.is_monkey_patched('thread') - mpatch = fixtures.MonkeyPatch( - 'threading.current_thread', eventlet.getcurrent) - with mpatch if eventlet_patched else contextlib.ExitStack(): - return super().__init__(*a, **kw) diff --git a/requirements.txt b/requirements.txt index 95f468abb47d..a8bed744fba2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -68,4 +68,3 @@ futurist>=1.8.0 # Apache-2.0 openstacksdk>=0.35.0 # Apache-2.0 dataclasses>=0.7;python_version=='3.6' # Apache 2.0 License PyYAML>=5.1 # MIT -fixtures>=3.0.0 # Apache-2.0/BSD diff --git a/test-requirements.txt b/test-requirements.txt index 80104c298f80..44cb2bacf79d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,6 +7,7 @@ mypy>=0.761 # MIT types-paramiko>=0.1.3 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 ddt>=1.2.1 # MIT +fixtures>=3.0.0 # Apache-2.0/BSD mock>=3.0.0 # BSD psycopg2-binary>=2.8 # LGPL/ZPL PyMySQL>=0.8.0 # MIT License