b6ce9b33dd
After a reboot, the Failover Cluster WMI provider may be unavailable. This change will wait for it when initializing clusterutils, having a configurable timeout defaulting to infinite. Closes-Bug: #1841779 Change-Id: Id7f2d65cb0307bc952f1e6a2724295ea524835f3
151 lines
5.8 KiB
Python
151 lines
5.8 KiB
Python
# Copyright 2015 Cloudbase Solutions Srl
|
|
#
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import ctypes
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from os_win import _utils
|
|
from os_win import exceptions
|
|
from os_win.utils.winapi import constants as w_const
|
|
from os_win.utils.winapi import libs as w_lib
|
|
from os_win.utils.winapi import wintypes
|
|
|
|
kernel32 = w_lib.get_shared_lib_handle(w_lib.KERNEL32)
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class Win32Utils(object):
|
|
def __init__(self):
|
|
self._kernel32_lib_func_opts = dict(error_on_nonzero_ret_val=False,
|
|
ret_val_is_err_code=False)
|
|
|
|
def run_and_check_output(self, *args, **kwargs):
|
|
eventlet_nonblocking_mode = kwargs.pop(
|
|
'eventlet_nonblocking_mode', True)
|
|
|
|
if eventlet_nonblocking_mode:
|
|
# We have to make sure that the invoked function as well as the
|
|
# subsequent error handling are performed within the same thread.
|
|
return _utils.avoid_blocking_call(
|
|
self._run_and_check_output, *args, **kwargs)
|
|
else:
|
|
return self._run_and_check_output(*args, **kwargs)
|
|
|
|
def _run_and_check_output(self, func, *args, **kwargs):
|
|
"""Convenience helper method for running Win32 API methods."""
|
|
kernel32_lib_func = kwargs.pop('kernel32_lib_func', False)
|
|
if kernel32_lib_func:
|
|
kwargs['error_ret_vals'] = kwargs.get('error_ret_vals', [0])
|
|
kwargs.update(self._kernel32_lib_func_opts)
|
|
|
|
ignored_error_codes = kwargs.pop('ignored_error_codes', [])
|
|
|
|
# A list of return values signaling that the operation failed.
|
|
error_ret_vals = kwargs.pop('error_ret_vals', [])
|
|
error_on_nonzero_ret_val = kwargs.pop('error_on_nonzero_ret_val', True)
|
|
# Note(lpetrut): In the future, we may use functions that can
|
|
# return information/warning HRESULTs that should not be
|
|
# treated as exceptions.
|
|
ret_val_is_err_code = kwargs.pop('ret_val_is_err_code', True)
|
|
|
|
# The exception raised when the Win32 API function fails. The
|
|
# exception must inherit Win32Exception.
|
|
failure_exc = kwargs.pop('failure_exc', exceptions.Win32Exception)
|
|
# Expects a dict containing error codes as keys and the
|
|
# according error message codes as values. If the error code is
|
|
# not present in this dict, this method will search the System
|
|
# message table.
|
|
error_msg_src = kwargs.pop('error_msg_src', {})
|
|
|
|
ret_val = func(*args, **kwargs)
|
|
|
|
func_failed = (error_on_nonzero_ret_val and ret_val) or (
|
|
ret_val in error_ret_vals)
|
|
|
|
if func_failed:
|
|
error_code = (ret_val
|
|
if ret_val_is_err_code else self.get_last_error())
|
|
error_code = ctypes.c_ulong(error_code).value
|
|
if error_code not in ignored_error_codes:
|
|
error_message = error_msg_src.get(error_code,
|
|
self.get_error_message(
|
|
error_code))
|
|
func_name = getattr(func, '__name__', '')
|
|
raise failure_exc(error_code=error_code,
|
|
error_message=error_message,
|
|
func_name=func_name)
|
|
return ret_val
|
|
|
|
@staticmethod
|
|
def get_error_message(error_code):
|
|
message_buffer = ctypes.c_char_p()
|
|
|
|
kernel32.FormatMessageA(
|
|
w_const.FORMAT_MESSAGE_FROM_SYSTEM |
|
|
w_const.FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
w_const.FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
None, error_code, 0, ctypes.byref(message_buffer), 0, None)
|
|
|
|
error_message = message_buffer.value
|
|
kernel32.LocalFree(message_buffer)
|
|
return error_message
|
|
|
|
def get_last_error(self):
|
|
error_code = kernel32.GetLastError()
|
|
kernel32.SetLastError(0)
|
|
return error_code
|
|
|
|
def local_free(self, handle):
|
|
try:
|
|
self._run_and_check_output(kernel32.LocalFree, handle)
|
|
except exceptions.Win32Exception:
|
|
LOG.exception("Could not deallocate memory. "
|
|
"There could be a memory leak.")
|
|
|
|
def close_handle(self, handle):
|
|
kernel32.CloseHandle(handle)
|
|
|
|
def wait_for_multiple_objects(self, handles, wait_all=True,
|
|
milliseconds=w_const.INFINITE):
|
|
handle_array = (wintypes.HANDLE * len(handles))(*handles)
|
|
ret_val = self.run_and_check_output(
|
|
kernel32.WaitForMultipleObjects,
|
|
len(handles),
|
|
handle_array,
|
|
wait_all,
|
|
milliseconds,
|
|
kernel32_lib_func=True,
|
|
error_ret_vals=[w_const.WAIT_FAILED])
|
|
if ret_val == w_const.ERROR_WAIT_TIMEOUT:
|
|
raise exceptions.Timeout()
|
|
|
|
return ret_val
|
|
|
|
def wait_for_single_object(self, handle,
|
|
milliseconds=w_const.INFINITE):
|
|
ret_val = self.run_and_check_output(
|
|
kernel32.WaitForSingleObject,
|
|
handle,
|
|
milliseconds,
|
|
kernel32_lib_func=True,
|
|
error_ret_vals=[w_const.WAIT_FAILED])
|
|
if ret_val == w_const.ERROR_WAIT_TIMEOUT:
|
|
raise exceptions.Timeout()
|
|
|
|
return ret_val
|