Use unique projects for static accounts.
If tests are launched in several threads,
it's possible that the same project is used
in several tests simultaneously.
It causes bad side effects and tests fail.
Patch provides that every static account
has unique project.
Related-PROD: https://mirantis.jira.com/browse/PRODX-19111
Change-Id: Id2e721a33640f4438e2db57b1808e745d506add4
(cherry picked from commit 9471ee64bd346583d8b869624de96fd87df3e89d)
(cherry picked from commit 68e07d7f31df3377e994ae2b731718b23c8c1560)
diff --git a/tempest/lib/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
index e327b99..86defb2 100644
--- a/tempest/lib/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -93,6 +93,8 @@
object_storage_reseller_admin_role)
self.accounts_dir = accounts_lock_dir
self._creds = {}
+ self._used_projects_file = os.path.join(self.accounts_dir,
+ 'used_projects')
@classmethod
def _append_role(cls, role, account_hash, hash_dict):
@@ -197,19 +199,47 @@
return True
return False
+ def _process_project(self, hash_, used_projects=None, use=True):
+ if used_projects is None:
+ used_projects = self._get_used_projects()
+ if 'tenant_name' in self.hash_dict['creds'][hash_]:
+ project = self.hash_dict['creds'][hash_]['tenant_name']
+ else:
+ project = self.hash_dict['creds'][hash_]['project_name']
+ method = 'append' if use else 'remove'
+ getattr(used_projects, method)(project)
+ with open(self._used_projects_file, 'w') as file:
+ file.write('\n'.join(used_projects) + '\n')
+
+ def _get_used_projects(self):
+ used_projects = []
+ try:
+ with open(self._used_projects_file) as file:
+ for line in file:
+ line = line.strip()
+ if line:
+ used_projects.append(line)
+ except FileNotFoundError:
+ pass
+ return used_projects
+
@lockutils.synchronized('test_accounts_io', external=True)
def _get_free_hash(self, hashes):
+ used_projects = self._get_used_projects()
+ hashes = self._exclude_used_projects(hashes, used_projects)
# Cast as a list because in some edge cases a set will be passed in
hashes = list(hashes)
if not os.path.isdir(self.accounts_dir):
os.mkdir(self.accounts_dir)
# Create File from first hash (since none are in use)
self._create_hash_file(hashes[0])
+ self._process_project(hashes[0], used_projects)
return hashes[0]
names = []
for _hash in hashes:
res = self._create_hash_file(_hash)
if res:
+ self._process_project(_hash, used_projects)
return _hash
else:
path = os.path.join(self.accounts_dir, _hash)
@@ -247,7 +277,6 @@
hashes = temp_list
else:
hashes = self.hash_dict['creds'].keys()
- hashes = self._exclude_used_projects(hashes)
# NOTE(mtreinish): admin is a special case because of the increased
# privilege set which could potentially cause issues on tests where
# that is not expected. So unless the admin role isn't specified do
@@ -279,6 +308,7 @@
@lockutils.synchronized('test_accounts_io', external=True)
def remove_hash(self, hash_string):
+ self._process_project(hash_string, use=False)
hash_path = os.path.join(self.accounts_dir, hash_string)
if not os.path.isfile(hash_path):
LOG.warning('Expected an account lock file %s to remove, but '
@@ -402,7 +432,7 @@
# TODO(gmann): Implement alt reader hash.
return
- def get_creds_by_roles(self, roles, force_new=False, scope=None):
+ def get_creds_by_roles(self, roles, force_new=True, scope=None):
roles = list(set(roles))
exist_creds = self._creds.get(str(roles).encode(
'utf-8'), None)
@@ -487,8 +517,8 @@
hash_dict['projects'][key_to_add].append(account_hash)
return hash_dict
- def _exclude_used_projects(self, hashes):
+ def _exclude_used_projects(self, hashes, used_projects):
excluded_accounts = []
- for project in [cred.tenant_name for cred in self._creds.values()]:
+ for project in used_projects:
excluded_accounts.extend(self.hash_dict['projects'][project])
- return hashes - set(excluded_accounts)
+ return set(hashes) - set(excluded_accounts)
diff --git a/tempest/tests/lib/common/test_preprov_creds.py b/tempest/tests/lib/common/test_preprov_creds.py
index f2131dc..2a7b97a 100644
--- a/tempest/tests/lib/common/test_preprov_creds.py
+++ b/tempest/tests/lib/common/test_preprov_creds.py
@@ -113,6 +113,17 @@
hash_list.append(temp_hash)
return hash_list
+ def _remove_hash(self, hash_list, hash_index):
+ test_account_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ remove_mock = self.useFixture(fixtures.MockPatch('os.remove'))
+ rmdir_mock = self.useFixture(fixtures.MockPatch('os.rmdir'))
+ test_account_class.remove_hash(hash_list[hash_index])
+ hash_path = os.path.join(self.fixed_params['accounts_lock_dir'],
+ hash_list[hash_index])
+ return {'remove_mock': remove_mock, 'hash_path': hash_path,
+ 'rmdir_mock': rmdir_mock}
+
def test_get_hash(self):
# Test with all accounts to make sure we try all combinations
# and hide no race conditions
@@ -179,8 +190,12 @@
create=True) as open_mock:
test_account_class._get_free_hash(hash_list)
lock_path = os.path.join(self.fixed_params['accounts_lock_dir'],
- hash_list[0])
- open_mock.assert_called_once_with(lock_path, 'w')
+ list(set(hash_list))[0])
+ accounts_lock_dir_calls = \
+ [mock.call(test_account_class._used_projects_file),
+ mock.call(lock_path, 'w'),
+ mock.call(test_account_class._used_projects_file, 'w')]
+ open_mock.assert_has_calls(accounts_lock_dir_calls, any_order=True)
mkdir_path = os.path.join(self.fixed_params['accounts_lock_dir'])
mkdir_mock.mock.assert_called_once_with(mkdir_path)
@@ -221,43 +236,33 @@
hash_list[3])
open_mock.assert_has_calls([mock.call(lock_path, 'w')])
- @mock.patch('oslo_concurrency.lockutils.lock')
- def test_remove_hash_last_account(self, lock_mock):
+ @mock.patch('tempest.lib.common.preprov_creds.'
+ 'PreProvisionedCredentialProvider._process_project')
+ def test_remove_hash_last_account(self, _process_project_mock):
hash_list = self._get_hash_list(self.test_accounts)
# Pretend the pseudo-lock is there
self.useFixture(
fixtures.MockPatch('os.path.isfile', return_value=True))
# Pretend the lock dir is empty
self.useFixture(fixtures.MockPatch('os.listdir', return_value=[]))
- test_account_class = preprov_creds.PreProvisionedCredentialProvider(
- **self.fixed_params)
- remove_mock = self.useFixture(fixtures.MockPatch('os.remove'))
- rmdir_mock = self.useFixture(fixtures.MockPatch('os.rmdir'))
- test_account_class.remove_hash(hash_list[2])
- hash_path = os.path.join(self.fixed_params['accounts_lock_dir'],
- hash_list[2])
+ result = self._remove_hash(hash_list, 2)
lock_path = self.fixed_params['accounts_lock_dir']
- remove_mock.mock.assert_called_once_with(hash_path)
- rmdir_mock.mock.assert_called_once_with(lock_path)
+ result['remove_mock'].mock.assert_called_once_with(result['hash_path'])
+ result['rmdir_mock'].mock.assert_called_once_with(lock_path)
- @mock.patch('oslo_concurrency.lockutils.lock')
- def test_remove_hash_not_last_account(self, lock_mock):
+ @mock.patch('tempest.lib.common.preprov_creds.'
+ 'PreProvisionedCredentialProvider._process_project')
+ def test_remove_hash_not_last_account(self, _process_project_mock):
hash_list = self._get_hash_list(self.test_accounts)
# Pretend the pseudo-lock is there
self.useFixture(fixtures.MockPatch(
'os.path.isfile', return_value=True))
- # Pretend the lock dir is empty
+ # Pretend the lock dir is not empty
self.useFixture(fixtures.MockPatch('os.listdir', return_value=[
hash_list[1], hash_list[4]]))
- test_account_class = preprov_creds.PreProvisionedCredentialProvider(
- **self.fixed_params)
- remove_mock = self.useFixture(fixtures.MockPatch('os.remove'))
- rmdir_mock = self.useFixture(fixtures.MockPatch('os.rmdir'))
- test_account_class.remove_hash(hash_list[2])
- hash_path = os.path.join(self.fixed_params['accounts_lock_dir'],
- hash_list[2])
- remove_mock.mock.assert_called_once_with(hash_path)
- rmdir_mock.mock.assert_not_called()
+ result = self._remove_hash(hash_list, 2)
+ result['remove_mock'].mock.assert_called_once_with(result['hash_path'])
+ result['rmdir_mock'].mock.assert_not_called()
def test_is_multi_user(self):
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(