From 1a5cb2437b7e55f2c4652ca8c6e1fc88133894d0 Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Tue, 14 Apr 2020 11:25:18 +0300 Subject: [PATCH] windows: fix terminating processes The neutron Windows exec call helper doesn't properly handle the situation in which the process that it's trying to kill was already terminated, in which case using wmi to fetch the process can raise an exception. This change ensures that those WMI exceptions will be properly handled. Closes-Bug: 1872663 Change-Id: I00c810fe541ac5e1e9923155fe90eb07a0b4b3dd (cherry picked from commit 8fd3e884c7953645862663400af181ba233ec28f) --- neutron/agent/windows/utils.py | 15 +++++++++-- .../tests/unit/agent/windows/test_utils.py | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/neutron/agent/windows/utils.py b/neutron/agent/windows/utils.py index cc0163dba72..6b6b8fa8dc9 100644 --- a/neutron/agent/windows/utils.py +++ b/neutron/agent/windows/utils.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import ctypes import io import os @@ -37,6 +38,8 @@ LOG = logging.getLogger(__name__) subprocess = eventlet.patcher.original('subprocess') subprocess.threading = eventlet.patcher.original('threading') +ERROR_KEY_DELETED = 0x03FA + def create_process(cmd, run_as_root=False, addl_env=None, tpool_proxy=True): @@ -70,8 +73,16 @@ def _get_wmi_process(pid): if not pid: return None - conn = wmi.WMI() - processes = conn.Win32_Process(ProcessId=pid) + try: + conn = wmi.WMI() + processes = conn.Win32_Process(ProcessId=pid) + except wmi.x_wmi as exc: + hresult = exc.com_error.hresult + err_code = ctypes.c_uint(hresult).value & 0xFFFF + if err_code == ERROR_KEY_DELETED: + return None + raise + if processes: return processes[0] return None diff --git a/neutron/tests/unit/agent/windows/test_utils.py b/neutron/tests/unit/agent/windows/test_utils.py index 3942cd1bcc9..c67130f9c76 100644 --- a/neutron/tests/unit/agent/windows/test_utils.py +++ b/neutron/tests/unit/agent/windows/test_utils.py @@ -26,6 +26,13 @@ from neutron.common import exceptions from neutron.tests import base +class x_wmi(Exception): + def __init__(self, info='', com_error=None): + super(x_wmi, self).__init__(info) + self.info = info + self.com_error = com_error + + @ddt.ddt class WindowsUtilsTestCase(base.BaseTestCase): @mock.patch('os.environ', {mock.sentinel.key0: mock.sentinel.val0}) @@ -87,6 +94,25 @@ class WindowsUtilsTestCase(base.BaseTestCase): if pid: mock_conn.Win32_Process.assert_called_once_with(ProcessId=pid) + @ddt.data({}, + {"hresult": 0xff, + "expect_exc": True}) + @ddt.unpack + @mock.patch.object(utils, 'wmi', create=True) + def test_get_wmi_process_exc(self, mock_wmi, expect_exc=False, + hresult=0x800703FA): + mock_conn = mock_wmi.WMI.return_value + mock_wmi.x_wmi = x_wmi + com_error = mock.Mock(hresult=hresult) + exc = x_wmi(com_error=com_error) + mock_conn.Win32_Process.side_effect = exc + + if expect_exc: + self.assertRaises( + x_wmi, utils._get_wmi_process, mock.sentinel.pid) + else: + self.assertIsNone(utils._get_wmi_process(mock.sentinel.pid)) + @ddt.data(True, False) @mock.patch.object(utils, '_get_wmi_process') def test_kill_process(self, process_exists, mock_get_process):