diff --git a/os_win/__init__.py b/os_win/__init__.py index f705f5e..121aadf 100644 --- a/os_win/__init__.py +++ b/os_win/__init__.py @@ -14,8 +14,16 @@ # License for the specific language governing permissions and limitations # under the License. +import sys + +from eventlet import patcher import pbr.version __version__ = pbr.version.VersionInfo( 'os_win').version_string() + +if sys.platform == 'win32': + import wmi + # We need to make sure that WMI uses the unpatched threading module. + wmi.threading = patcher.original('threading') diff --git a/os_win/tests/utils/compute/test_clusterutils.py b/os_win/tests/utils/compute/test_clusterutils.py index 6bb83eb..16d48c4 100644 --- a/os_win/tests/utils/compute/test_clusterutils.py +++ b/os_win/tests/utils/compute/test_clusterutils.py @@ -296,33 +296,37 @@ class ClusterUtilsTestCase(test_base.OsWinBaseTestCase): self._FAKE_HOST, [self._clusterutils._LIVE_MIGRATION_TYPE]) - def test_monitor_vm_failover_no_vm(self): + @mock.patch.object(clusterutils, 'tpool') + def test_monitor_vm_failover_no_vm(self, mock_tpool): self._clusterutils._watcher = mock.MagicMock() fake_prev = mock.MagicMock(OwnerNode=self._FAKE_PREV_HOST) fake_wmi_object = mock.MagicMock(OwnerNode=self._FAKE_HOST, Name='Virtual Machine', previous=fake_prev) - self._clusterutils._watcher.return_value = fake_wmi_object + mock_tpool.execute.return_value = fake_wmi_object fake_callback = mock.MagicMock() self._clusterutils.monitor_vm_failover(fake_callback) - self._clusterutils._watcher.assert_called_once_with( + mock_tpool.execute.assert_called_once_with( + self._clusterutils._watcher, self._clusterutils._WMI_EVENT_TIMEOUT_MS) fake_callback.assert_not_called() - def test_monitor_vm_failover(self): + @mock.patch.object(clusterutils, 'tpool') + def test_monitor_vm_failover(self, mock_tpool): self._clusterutils._watcher = mock.MagicMock() fake_prev = mock.MagicMock(OwnerNode=self._FAKE_PREV_HOST) fake_wmi_object = mock.MagicMock(OwnerNode=self._FAKE_HOST, Name=self._FAKE_RESOURCEGROUP_NAME, previous=fake_prev) - self._clusterutils._watcher.return_value = fake_wmi_object + mock_tpool.execute.return_value = fake_wmi_object fake_callback = mock.MagicMock() self._clusterutils.monitor_vm_failover(fake_callback) - self._clusterutils._watcher.assert_called_once_with( + mock_tpool.execute.assert_called_once_with( + self._clusterutils._watcher, self._clusterutils._WMI_EVENT_TIMEOUT_MS) fake_callback.assert_called_once_with(self._FAKE_VM_NAME, self._FAKE_PREV_HOST, diff --git a/os_win/tests/utils/compute/test_vmutils.py b/os_win/tests/utils/compute/test_vmutils.py index fa3b4ea..62ff2b1 100644 --- a/os_win/tests/utils/compute/test_vmutils.py +++ b/os_win/tests/utils/compute/test_vmutils.py @@ -942,7 +942,9 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase): self.assertEqual(watcher.return_value, listener) @mock.patch('time.sleep') - def test_vm_power_state_change_event_handler(self, mock_sleep): + @mock.patch.object(vmutils, 'tpool') + def test_vm_power_state_change_event_handler(self, mock_tpool, + mock_sleep): self._mock_wmi.x_wmi_timed_out = exceptions.HyperVException enabled_state = constants.HYPERV_VM_STATE_ENABLED @@ -953,9 +955,9 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase): fake_listener = ( self._vmutils._conn.Msvm_ComputerSystem.watch_for.return_value) - fake_listener.side_effect = (self._mock_wmi.x_wmi_timed_out, - fake_event, Exception, - KeyboardInterrupt) + mock_tpool.execute.side_effect = (self._mock_wmi.x_wmi_timed_out, + fake_event, Exception, + KeyboardInterrupt) handler = self._vmutils.get_vm_power_state_change_listener( get_handler=True) @@ -965,7 +967,8 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase): fake_callback.assert_called_once_with(mock.sentinel.vm_name, enabled_state) - fake_listener.assert_has_calls( + mock_tpool.execute.assert_has_calls( + fake_listener, [mock.call(self._vmutils._DEFAULT_EVENT_TIMEOUT_MS)] * 4) mock_sleep.assert_called_once_with( self._vmutils._DEFAULT_EVENT_TIMEOUT_MS / 1000) diff --git a/os_win/tests/utils/network/test_networkutils.py b/os_win/tests/utils/network/test_networkutils.py index d590b0a..7fddbf4 100644 --- a/os_win/tests/utils/network/test_networkutils.py +++ b/os_win/tests/utils/network/test_networkutils.py @@ -143,14 +143,16 @@ class NetworkUtilsTestCase(test_base.OsWinBaseTestCase): mock.sentinel.switch_port_name) self.assertEqual(mock.sentinel.mac_address, actual_mac_address) + @mock.patch.object(networkutils.tpool, 'execute') @mock.patch.object(networkutils, 'wmi', create=True) @mock.patch.object(networkutils.NetworkUtils, '_get_event_wql_query') - def test_get_vnic_event_listener(self, mock_get_event_query, mock_wmi): + def test_get_vnic_event_listener(self, mock_get_event_query, mock_wmi, + mock_execute): mock_wmi.x_wmi_timed_out = ValueError event = mock.MagicMock() port_class = self.netutils._conn.Msvm_SyntheticEthernetPortSettingData wmi_event_listener = port_class.watch_for.return_value - wmi_event_listener.side_effect = [mock_wmi.x_wmi_timed_out, event] + mock_execute.side_effect = [mock_wmi.x_wmi_timed_out, event] # callback will raise an exception in order to stop iteration in the # listener. @@ -166,8 +168,9 @@ class NetworkUtilsTestCase(test_base.OsWinBaseTestCase): timeframe=2) port_class.watch_for.assert_called_once_with( mock_get_event_query.return_value) - wmi_event_listener.assert_has_calls( - [mock.call(self.netutils._VNIC_LISTENER_TIMEOUT_MS)] * 2) + mock_execute.assert_has_calls( + [mock.call(wmi_event_listener, + self.netutils._VNIC_LISTENER_TIMEOUT_MS)] * 2) callback.assert_called_once_with(event.ElementName) def test_get_event_wql_query(self): diff --git a/os_win/utils/compute/clusterutils.py b/os_win/utils/compute/clusterutils.py index ed4c75b..ba3bfad 100644 --- a/os_win/utils/compute/clusterutils.py +++ b/os_win/utils/compute/clusterutils.py @@ -23,7 +23,9 @@ import sys if sys.platform == 'win32': import wmi +from eventlet import tpool from oslo_log import log as logging + from os_win._i18n import _, _LE from os_win import exceptions from os_win.utils import baseutils @@ -209,7 +211,8 @@ class ClusterUtils(baseutils.BaseUtils): new_host = None try: # wait for new event for _WMI_EVENT_TIMEOUT_MS miliseconds. - wmi_object = self._watcher(self._WMI_EVENT_TIMEOUT_MS) + wmi_object = tpool.execute(self._watcher, + self._WMI_EVENT_TIMEOUT_MS) old_host = wmi_object.previous.OwnerNode new_host = wmi_object.OwnerNode # wmi_object.Name field is of the form: diff --git a/os_win/utils/compute/vmutils.py b/os_win/utils/compute/vmutils.py index f7c25ed..9429f6c 100644 --- a/os_win/utils/compute/vmutils.py +++ b/os_win/utils/compute/vmutils.py @@ -27,6 +27,7 @@ import uuid if sys.platform == 'win32': import wmi +from eventlet import tpool from oslo_config import cfg from oslo_log import log as logging from oslo_utils import uuidutils @@ -821,7 +822,12 @@ class VMUtils(baseutils.BaseUtilsVirt): try: # Retrieve one by one all the events that occurred in # the checked interval. - event = listener(event_timeout) + # + # We use eventlet.tpool for retrieving the events in + # order to avoid issues caused by greenthread/thread + # communication. Note that PyMI must use the unpatched + # threading module. + event = tpool.execute(listener, event_timeout) vm_name = event.ElementName vm_state = event.EnabledState diff --git a/os_win/utils/network/networkutils.py b/os_win/utils/network/networkutils.py index cae5de4..a6a7361 100644 --- a/os_win/utils/network/networkutils.py +++ b/os_win/utils/network/networkutils.py @@ -21,6 +21,7 @@ Hyper-V Server / Windows Server 2012. import re from eventlet import greenthread +from eventlet import tpool import sys if sys.platform == 'win32': @@ -219,7 +220,8 @@ class NetworkUtils(baseutils.BaseUtilsVirt): # Retrieve one by one all the events that occurred in # the checked interval. try: - event = listener(self._VNIC_LISTENER_TIMEOUT_MS) + event = tpool.execute(listener, + self._VNIC_LISTENER_TIMEOUT_MS) callback(event.ElementName) except wmi.x_wmi_timed_out: # no new event published.