Retry user load profile on Windows

On Windows, the load user profile may fail on laggy systems, if the
Windows subsystems are not ready at that moment.

Retrying the load should fix the issue most of the times.

Change-Id: I28cc564ebeac6d901dcbbef7cebe882a5ccb41b1
This commit is contained in:
Adrian Vladu 2020-07-13 13:15:13 +03:00
parent 97295ba569
commit a9ffc62c11
4 changed files with 32 additions and 7 deletions

View File

@ -71,3 +71,14 @@ class WindowsCloudbaseInitException(CloudbaseInitException):
except TypeError:
formatted_msg = msg
super(WindowsCloudbaseInitException, self).__init__(formatted_msg)
class LoadUserProfileCloudbaseInitException(WindowsCloudbaseInitException):
"""Windows cannot load the newly created user profile.
The load user profile can fail if the Windows subsystems responsible for
the action are not ready. This usually happens on laggy systems and should
be retried.
"""
pass

View File

@ -642,6 +642,9 @@ class WindowsUtils(base.BaseOSUtils):
# User not found
pass
@retry_decorator.retry_decorator(
max_retry_count=3,
exceptions=exception.LoadUserProfileCloudbaseInitException)
def create_user_logon_session(self, username, password, domain='.',
load_profile=True,
logon_type=LOGON32_LOGON_INTERACTIVE):
@ -666,7 +669,7 @@ class WindowsUtils(base.BaseOSUtils):
ret_val = userenv.LoadUserProfileW(token, ctypes.byref(pi))
if not ret_val:
kernel32.CloseHandle(token)
raise exception.WindowsCloudbaseInitException(
raise exception.LoadUserProfileCloudbaseInitException(
"Cannot load user profile: %r")
return token

View File

@ -452,7 +452,9 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
self._test_create_user(fail=True)
@mock.patch('cloudbaseinit.osutils.windows.Win32_PROFILEINFO')
def _test_create_user_logon_session(self, mock_Win32_PROFILEINFO, logon,
@mock.patch('time.sleep')
def _test_create_user_logon_session(self, mock_time_sleep,
mock_Win32_PROFILEINFO, logon,
loaduser, load_profile=True,
last_error=None):
self._wintypes_mock.HANDLE = mock.MagicMock()
@ -474,7 +476,9 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
userenv.LoadUserProfileW.return_value = None
kernel32.CloseHandle.return_value = None
with self.assert_raises_windows_message(
"Cannot load user profile: %r", last_error):
"Cannot load user profile: %r", last_error,
get_last_error_called_times=4,
format_error_called_times=4):
self._winutils.create_user_logon_session(
self._USERNAME, self._PASSWORD, domain='.',
load_profile=load_profile)

View File

@ -159,7 +159,9 @@ class CloudbaseInitTestBase(unittest.TestCase):
@contextlib.contextmanager
def assert_raises_windows_message(
self, expected_msg, error_code,
exc=exception.WindowsCloudbaseInitException):
exc=exception.WindowsCloudbaseInitException,
get_last_error_called_times=1,
format_error_called_times=1):
"""Helper method for testing raised error messages
This assert method is similar to :meth:`~assertRaises`, but
@ -188,11 +190,16 @@ class CloudbaseInitTestBase(unittest.TestCase):
# This can be called when the error code is not given,
# but we don't have control over that, so test that
# it's actually called only once.
mock_get_last_error.assert_called_once_with()
mock_format_error.assert_called_once_with(
mock_get_last_error.assert_called()
self.assertEqual(mock_get_last_error.call_count,
get_last_error_called_times)
mock_format_error.assert_called_with(
mock_get_last_error.return_value)
else:
mock_format_error.assert_called_once_with(error_code)
mock_format_error.assert_called_with(error_code)
self.assertEqual(mock_format_error.call_count,
format_error_called_times)
expected_msg = expected_msg % mock_format_error.return_value
self.assertEqual(expected_msg, cm.exception.args[0])