From eda67926322cf1df1bb4a78e58a7847d52169d9f Mon Sep 17 00:00:00 2001 From: Balazs Gibizer Date: Sun, 27 Jul 2025 16:05:12 +0200 Subject: [PATCH] Make libvirt Tpool proxying conditional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running in eventlet mode we keep the original eventlet.tpool usage but when running in threading mode we call the functions directly on the thread of the caller. The patch_tpool_proxy() logic is removed from the libvirt driver as it was only needed for python old style classes which is not in use any more in python3 and the issue is not reproducible any more with virConnect even without the patching. ❯ python3 Python 3.12.10 (main, Apr 9 2025, 04:44:59) [GCC 14.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import eventlet >>> eventlet.monkey_patch() >>> from nova.virt.libvirt import host >>> h = host.Host(uri="qemu:///system") >>> h.get_connection() libvirt: error : internal error: could not initialize domain event timer URI qemu:///system does not support events: internal error: could not initialize domain event timer >>> c = h.get_connection() >>> str(c) '' >>> Signed-off-by: Balazs Gibizer Change-Id: Ic60ab78cec2a9f9ba177568b69e738425e56cae1 --- nova/tests/unit/virt/libvirt/test_driver.py | 17 ++++++++ nova/tests/unit/virt/libvirt/test_host.py | 7 ++++ nova/utils.py | 4 +- nova/virt/libvirt/driver.py | 19 --------- nova/virt/libvirt/host.py | 3 +- threading_unit_test_excludes.txt | 45 ++------------------- 6 files changed, 30 insertions(+), 65 deletions(-) diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index debd2c4b24..72ad73193b 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -29998,6 +29998,16 @@ class LibvirtNonblockingTestCase(test.NoDBTestCase): group='libvirt') def test_connection_to_primitive(self): + if utils.concurrency_mode_threading(): + self.skipTest( + "In threading mode nova does not use eventlet.tpool to " + "wrap the libvirt calls. This test case asserts that the " + "libvirt connection having eventlet.tpool.Proxy objects " + "are serializable. See " + "https://bugs.launchpad.net/nova/+bug/962840. " + "As Proxy object is not present in threading mode this test " + "is not valid. ") + # Test bug 962840. import nova.virt.libvirt.driver as libvirt_driver drvr = libvirt_driver.LibvirtDriver('') @@ -30007,6 +30017,13 @@ class LibvirtNonblockingTestCase(test.NoDBTestCase): @mock.patch.object(eventlet.tpool, 'execute') @mock.patch.object(objects.Service, 'get_by_compute_host') def test_tpool_execute_calls_libvirt(self, mock_svc, mock_execute): + if utils.concurrency_mode_threading(): + self.skipTest( + "In threading mode nova does not use eventlet.tpool to " + "wrap the libvirt calls. This test case asserts that libvirt " + "connect goes through the tpool, so in threading mode this " + "test case is invalid.") + conn = fakelibvirt.virConnect() conn.is_expected = True diff --git a/nova/tests/unit/virt/libvirt/test_host.py b/nova/tests/unit/virt/libvirt/test_host.py index ec80178454..417b250509 100644 --- a/nova/tests/unit/virt/libvirt/test_host.py +++ b/nova/tests/unit/virt/libvirt/test_host.py @@ -2298,6 +2298,13 @@ class TestLibvirtSEVESSupported(TestLibvirtSEV): class LibvirtTpoolProxyTestCase(test.NoDBTestCase): def setUp(self): + if utils.concurrency_mode_threading(): + self.skipTest( + "In threading mode nova does not use eventlet.tpool.Proxy to " + "wrap the libvirt calls. This test asserts that such calls " + "are returning Proxy objects. So these test cases are not " + "valid.") + super(LibvirtTpoolProxyTestCase, self).setUp() self.useFixture(nova_fixtures.LibvirtFixture()) diff --git a/nova/utils.py b/nova/utils.py index 792095dd10..41ad9388e0 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -1290,11 +1290,11 @@ def _log_executor_stats(executor): executor._delayed_work.unfinished_tasks, stats) -def tpool_wrap(target): +def tpool_wrap(target, autowrap=()): """Wrap the target into an eventlet Tpool Proxy object if running in eventlet mode. In threading mode no wrapping is applied. """ if concurrency_mode_threading(): return target else: - return tpool.Proxy(target) + return tpool.Proxy(target, autowrap=autowrap) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 4f51be8c1b..e28de694a5 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -48,7 +48,6 @@ import uuid from castellan import key_manager from copy import deepcopy -from eventlet import tpool from lxml import etree from os_brick import encryptors from os_brick.encryptors import luks as luks_encryptor @@ -193,24 +192,6 @@ VOLUME_DRIVERS = { } -def patch_tpool_proxy(): - """eventlet.tpool.Proxy doesn't work with old-style class in __str__() - or __repr__() calls. See bug #962840 for details. - We perform a monkey patch to replace those two instance methods. - """ - - def str_method(self): - return str(self._obj) - - def repr_method(self): - return repr(self._obj) - - tpool.Proxy.__str__ = str_method - tpool.Proxy.__repr__ = repr_method - - -patch_tpool_proxy() - # For information about when MIN_{LIBVIRT,QEMU}_VERSION and # NEXT_MIN_{LIBVIRT,QEMU}_VERSION can be changed, consult the following: # diff --git a/nova/virt/libvirt/host.py b/nova/virt/libvirt/host.py index dd366f226c..c29f883fce 100644 --- a/nova/virt/libvirt/host.py +++ b/nova/virt/libvirt/host.py @@ -41,7 +41,6 @@ import typing as ty from eventlet import greenio from eventlet import greenthread from eventlet import patcher -from eventlet import tpool from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_utils import excutils @@ -195,7 +194,7 @@ class Host(object): # eventlet's event loop, starving all other greenthreads until # completion. eventlet's tpool.Proxy handles this situation for us by # executing proxied calls in a native thread. - return tpool.Proxy(obj, autowrap=self._libvirt_proxy_classes) + return utils.tpool_wrap(obj, autowrap=self._libvirt_proxy_classes) def _native_thread(self): """Receives async events coming in from libvirtd. diff --git a/threading_unit_test_excludes.txt b/threading_unit_test_excludes.txt index 4d88fcfab8..9a6f4406a9 100644 --- a/threading_unit_test_excludes.txt +++ b/threading_unit_test_excludes.txt @@ -2,9 +2,11 @@ nova.tests.unit.console.rfb.test_authvencrypt.RFBAuthSchemeVeNCryptTestCase.test nova.tests.unit.console.rfb.test_authvencrypt.RFBAuthSchemeVeNCryptTestCase.test_security_handshake_without_x509 nova.tests.unit.console.rfb.test_authvencrypt.RFBAuthSchemeVeNCryptTestCase.test_security_handshake_with_x509 nova.tests.unit.test_context.ContextTestCase.test_scatter_gather_cells_queued_task_cancelled + +# Wait until eventlet.Event is removed by https://review.opendev.org/c/openstack/nova/+/949754 nova.tests.unit.virt.libvirt.test_driver.CacheConcurrencyTestCase.test_different_fname_concurrency nova.tests.unit.virt.libvirt.test_driver.CacheConcurrencyTestCase.test_same_fname_concurrency -nova.tests.unit.virt.libvirt.test_driver.LibvirtDriverTestCase.test_rescue + nova.tests.unit.virt.libvirt.test_driver.TestUpdateProviderTree.test_image_cache_disk_reservation nova.tests.unit.virt.libvirt.test_driver.TestUpdateProviderTree.test_update_provider_tree nova.tests.unit.virt.libvirt.test_driver.TestUpdateProviderTree.test_update_provider_tree_for_pcpu_reshape @@ -18,47 +20,6 @@ nova.tests.unit.virt.libvirt.test_driver.TestUpdateProviderTree.test_update_prov nova.tests.unit.virt.libvirt.volume.test_mount.HostMountStateTestCase.test_mount_concurrent nova.tests.unit.virt.libvirt.volume.test_mount.HostMountStateTestCase.test_mount_concurrent_no_interfere nova.tests.unit.virt.libvirt.volume.test_mount.MountManagerTestCase.test_host_up_waits_for_completion -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_attach_detach_different_power_states -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_attach_detach_volume -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_block_stats -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_destroy_instance -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_force_hard_reboot -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_get_console_output -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_get_diagnostics -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_get_info -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_get_instance_diagnostics -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_get_instance_disk_info -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_get_mks_console -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_get_serial_console -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_get_spice_console -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_get_vnc_console -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_live_migration -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_live_migration_force_complete -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_pause -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_poll_rebooting_instances -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_power_off -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_power_on_powered_off -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_power_on_running -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_reboot -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_rescue -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_restore_running -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_restore_soft_deleted -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_resume_state_on_host_boot -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_resume_suspended_instance -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_resume_unsuspended_instance -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_set_admin_password -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_snapshot_running -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_soft_delete -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_spawn -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_suspend -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_swap_volume -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_trigger_crash_dump -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_unpause_paused_instance -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_unpause_unpaused_instance -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_unplug_vifs_with_destroy_vifs_false -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_unplug_vifs_with_destroy_vifs_true -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_unrescue_rescued_instance -nova.tests.unit.virt.test_virt_drivers.LibvirtConnTestCase.test_unrescue_unrescued_instance nova.tests.unit.virt.vmwareapi.test_vm_util.VMwareVMUtilTestCase.test_create_vm_invalid_guestid # Independent failure ~10% with multiple possible error: # - sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) not an error