Transport context to all threads

The nova.utils.spawn and spawn_n methods transport
the context (and profiling information) to the
newly created threads. But the same isn't done
when submitting work to thread-pools in the
ComputeManager.

The code doing that for spawn and spawn_n
is extracted to a new function
and called to submit the work to the thread-pools.

Closes-Bug: #1962574
Change-Id: I9085deaa8cf0b167d87db68e4afc4a463c00569c
This commit is contained in:
Fabian Wiesel 2022-02-02 10:13:06 +01:00 committed by Rajesh Tailor
parent 3b4378c189
commit 646fc51732
4 changed files with 37 additions and 29 deletions

View File

@ -8621,7 +8621,8 @@ class ComputeManager(manager.Manager):
# in order to be able to track and abort it in the future.
self._waiting_live_migrations[instance.uuid] = (None, None)
try:
future = self._live_migration_executor.submit(
future = nova.utils.pass_context(
self._live_migration_executor.submit,
self._do_live_migration, context, dest, instance,
block_migration, migration, migrate_data)
self._waiting_live_migrations[instance.uuid] = (migration, future)
@ -9866,7 +9867,9 @@ class ComputeManager(manager.Manager):
else:
LOG.debug('Triggering sync for uuid %s', uuid)
self._syncs_in_progress[uuid] = True
self._sync_power_pool.spawn_n(_sync, db_instance)
nova.utils.pass_context(self._sync_power_pool.spawn_n,
_sync,
db_instance)
def _query_driver_power_state_and_sync(self, context, db_instance):
if db_instance.task_state is not None:

View File

@ -2049,8 +2049,8 @@ class ComputeTaskManager:
skipped_host(target_ctxt, host, image_ids)
continue
fetch_pool.spawn_n(wrap_cache_images, target_ctxt, host,
image_ids)
utils.pass_context(fetch_pool.spawn_n, wrap_cache_images,
target_ctxt, host, image_ids)
# Wait until all those things finish
fetch_pool.waitall()

View File

@ -9154,9 +9154,15 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase,
self.assertEqual(driver_console.get_connection_info.return_value,
console)
@mock.patch('nova.utils.pass_context')
@mock.patch('nova.compute.manager.ComputeManager.'
'_do_live_migration')
def _test_max_concurrent_live(self, mock_lm):
def _test_max_concurrent_live(self, mock_lm, mock_pass_context):
# pass_context wraps the function, which doesn't work with a mock
# So we simply mock it too
def _mock_pass_context(runner, func, *args, **kwargs):
return runner(func, *args, **kwargs)
mock_pass_context.side_effect = _mock_pass_context
@mock.patch('nova.objects.Migration.save')
def _do_it(mock_mig_save):

View File

@ -632,15 +632,13 @@ def _serialize_profile_info():
return trace_info
def spawn(func, *args, **kwargs):
"""Passthrough method for eventlet.spawn.
def pass_context(runner, func, *args, **kwargs):
"""Generalised passthrough method
This utility exists so that it can be stubbed for testing without
interfering with the service spawns.
It will also grab the context from the threadlocal store and add it to
the store on the new thread. This allows for continuity in logging the
context when using this method to spawn a new thread.
It will grab the context from the threadlocal store and add it to
the store on the runner. This allows for continuity in logging the
context when using this method to spawn a new thread through the
runner function
"""
_context = common_context.get_current()
profiler_info = _serialize_profile_info()
@ -655,7 +653,21 @@ def spawn(func, *args, **kwargs):
profiler.init(**profiler_info)
return func(*args, **kwargs)
return eventlet.spawn(context_wrapper, *args, **kwargs)
return runner(context_wrapper, *args, **kwargs)
def spawn(func, *args, **kwargs):
"""Passthrough method for eventlet.spawn.
This utility exists so that it can be stubbed for testing without
interfering with the service spawns.
It will also grab the context from the threadlocal store and add it to
the store on the new thread. This allows for continuity in logging the
context when using this method to spawn a new thread.
"""
return pass_context(eventlet.spawn, func, *args, **kwargs)
def spawn_n(func, *args, **kwargs):
@ -668,25 +680,12 @@ def spawn_n(func, *args, **kwargs):
the store on the new thread. This allows for continuity in logging the
context when using this method to spawn a new thread.
"""
_context = common_context.get_current()
profiler_info = _serialize_profile_info()
@functools.wraps(func)
def context_wrapper(*args, **kwargs):
# NOTE: If update_store is not called after spawn_n it won't be
# available for the logger to pull from threadlocal storage.
if _context is not None:
_context.update_store()
if profiler_info and profiler:
profiler.init(**profiler_info)
func(*args, **kwargs)
eventlet.spawn_n(context_wrapper, *args, **kwargs)
pass_context(eventlet.spawn_n, func, *args, **kwargs)
def tpool_execute(func, *args, **kwargs):
"""Run func in a native thread"""
tpool.execute(func, *args, **kwargs)
return pass_context(tpool.execute, func, *args, **kwargs)
def is_none_string(val):