Skip to content

Commit 9198d0b

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Retry user load profile on Windows"
2 parents da0c0dc + a9ffc62 commit 9198d0b

4 files changed

Lines changed: 32 additions & 7 deletions

File tree

cloudbaseinit/exception.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,14 @@ def __init__(self, msg="%r", error_code=None):
7171
except TypeError:
7272
formatted_msg = msg
7373
super(WindowsCloudbaseInitException, self).__init__(formatted_msg)
74+
75+
76+
class LoadUserProfileCloudbaseInitException(WindowsCloudbaseInitException):
77+
"""Windows cannot load the newly created user profile.
78+
79+
The load user profile can fail if the Windows subsystems responsible for
80+
the action are not ready. This usually happens on laggy systems and should
81+
be retried.
82+
"""
83+
84+
pass

cloudbaseinit/osutils/windows.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,9 @@ def get_user_sid(self, username):
642642
# User not found
643643
pass
644644

645+
@retry_decorator.retry_decorator(
646+
max_retry_count=3,
647+
exceptions=exception.LoadUserProfileCloudbaseInitException)
645648
def create_user_logon_session(self, username, password, domain='.',
646649
load_profile=True,
647650
logon_type=LOGON32_LOGON_INTERACTIVE):
@@ -666,7 +669,7 @@ def create_user_logon_session(self, username, password, domain='.',
666669
ret_val = userenv.LoadUserProfileW(token, ctypes.byref(pi))
667670
if not ret_val:
668671
kernel32.CloseHandle(token)
669-
raise exception.WindowsCloudbaseInitException(
672+
raise exception.LoadUserProfileCloudbaseInitException(
670673
"Cannot load user profile: %r")
671674

672675
return token

cloudbaseinit/tests/osutils/test_windows.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,9 @@ def test_create_user_fail(self):
452452
self._test_create_user(fail=True)
453453

454454
@mock.patch('cloudbaseinit.osutils.windows.Win32_PROFILEINFO')
455-
def _test_create_user_logon_session(self, mock_Win32_PROFILEINFO, logon,
455+
@mock.patch('time.sleep')
456+
def _test_create_user_logon_session(self, mock_time_sleep,
457+
mock_Win32_PROFILEINFO, logon,
456458
loaduser, load_profile=True,
457459
last_error=None):
458460
self._wintypes_mock.HANDLE = mock.MagicMock()
@@ -474,7 +476,9 @@ def _test_create_user_logon_session(self, mock_Win32_PROFILEINFO, logon,
474476
userenv.LoadUserProfileW.return_value = None
475477
kernel32.CloseHandle.return_value = None
476478
with self.assert_raises_windows_message(
477-
"Cannot load user profile: %r", last_error):
479+
"Cannot load user profile: %r", last_error,
480+
get_last_error_called_times=4,
481+
format_error_called_times=4):
478482
self._winutils.create_user_logon_session(
479483
self._USERNAME, self._PASSWORD, domain='.',
480484
load_profile=load_profile)

cloudbaseinit/tests/testutils.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ class CloudbaseInitTestBase(unittest.TestCase):
159159
@contextlib.contextmanager
160160
def assert_raises_windows_message(
161161
self, expected_msg, error_code,
162-
exc=exception.WindowsCloudbaseInitException):
162+
exc=exception.WindowsCloudbaseInitException,
163+
get_last_error_called_times=1,
164+
format_error_called_times=1):
163165
"""Helper method for testing raised error messages
164166
165167
This assert method is similar to :meth:`~assertRaises`, but
@@ -188,11 +190,16 @@ def assert_raises_windows_message(
188190
# This can be called when the error code is not given,
189191
# but we don't have control over that, so test that
190192
# it's actually called only once.
191-
mock_get_last_error.assert_called_once_with()
192-
mock_format_error.assert_called_once_with(
193+
mock_get_last_error.assert_called()
194+
self.assertEqual(mock_get_last_error.call_count,
195+
get_last_error_called_times)
196+
mock_format_error.assert_called_with(
193197
mock_get_last_error.return_value)
194198
else:
195-
mock_format_error.assert_called_once_with(error_code)
199+
mock_format_error.assert_called_with(error_code)
200+
201+
self.assertEqual(mock_format_error.call_count,
202+
format_error_called_times)
196203

197204
expected_msg = expected_msg % mock_format_error.return_value
198205
self.assertEqual(expected_msg, cm.exception.args[0])

0 commit comments

Comments
 (0)