blob: 6d376d628412a8a81cbd0da7f6b0bcf3f0bcfcc8 [file] [log] [blame]
Matthew Treinishc791ac42014-07-16 09:15:23 -04001# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import hashlib
16import os
Matthew Treinish96e9e882014-06-09 18:37:19 -040017
Doug Hellmann583ce2c2015-03-11 14:55:46 +000018from oslo_concurrency import lockutils
19from oslo_log import log as logging
Matthew Treinishc791ac42014-07-16 09:15:23 -040020import yaml
21
Matthew Treinishc791ac42014-07-16 09:15:23 -040022from tempest.common import cred_provider
23from tempest import config
24from tempest import exceptions
Matthew Treinishc791ac42014-07-16 09:15:23 -040025
26CONF = config.CONF
27LOG = logging.getLogger(__name__)
28
29
30def read_accounts_yaml(path):
31 yaml_file = open(path, 'r')
32 accounts = yaml.load(yaml_file)
33 return accounts
34
35
36class Accounts(cred_provider.CredentialProvider):
37
Andrea Frittolic3280152015-02-26 12:42:34 +000038 def __init__(self, identity_version=None, name=None):
39 super(Accounts, self).__init__(identity_version=identity_version,
40 name=name)
Aaron Rosen48070042015-03-30 16:17:11 -070041 if (CONF.auth.test_accounts_file and
42 os.path.isfile(CONF.auth.test_accounts_file)):
Matthew Treinishb19eeb82014-09-04 09:57:46 -040043 accounts = read_accounts_yaml(CONF.auth.test_accounts_file)
44 self.use_default_creds = False
45 else:
46 accounts = {}
47 self.use_default_creds = True
Matthew Treinishc791ac42014-07-16 09:15:23 -040048 self.hash_dict = self.get_hash_dict(accounts)
Matthew Treinishb503f032015-03-12 15:48:49 -040049 self.accounts_dir = os.path.join(lockutils.get_lock_path(CONF),
Doug Hellmann583ce2c2015-03-11 14:55:46 +000050 'test_accounts')
Matthew Treinishc791ac42014-07-16 09:15:23 -040051 self.isolated_creds = {}
52
53 @classmethod
Matthew Treinish976e8df2014-12-19 14:21:54 -050054 def _append_role(cls, role, account_hash, hash_dict):
55 if role in hash_dict['roles']:
56 hash_dict['roles'][role].append(account_hash)
57 else:
58 hash_dict['roles'][role] = [account_hash]
59 return hash_dict
60
61 @classmethod
Matthew Treinishc791ac42014-07-16 09:15:23 -040062 def get_hash_dict(cls, accounts):
Matthew Treinish976e8df2014-12-19 14:21:54 -050063 hash_dict = {'roles': {}, 'creds': {}}
64 # Loop over the accounts read from the yaml file
Matthew Treinishc791ac42014-07-16 09:15:23 -040065 for account in accounts:
Matthew Treinish976e8df2014-12-19 14:21:54 -050066 roles = []
67 types = []
68 if 'roles' in account:
69 roles = account.pop('roles')
70 if 'types' in account:
71 types = account.pop('types')
Matthew Treinishc791ac42014-07-16 09:15:23 -040072 temp_hash = hashlib.md5()
73 temp_hash.update(str(account))
Matthew Treinish976e8df2014-12-19 14:21:54 -050074 temp_hash_key = temp_hash.hexdigest()
75 hash_dict['creds'][temp_hash_key] = account
76 for role in roles:
77 hash_dict = cls._append_role(role, temp_hash_key,
78 hash_dict)
79 # If types are set for the account append the matching role
80 # subdict with the hash
81 for type in types:
82 if type == 'admin':
83 hash_dict = cls._append_role(CONF.identity.admin_role,
84 temp_hash_key, hash_dict)
85 elif type == 'operator':
86 hash_dict = cls._append_role(
87 CONF.object_storage.operator_role, temp_hash_key,
88 hash_dict)
89 elif type == 'reseller_admin':
90 hash_dict = cls._append_role(
91 CONF.object_storage.reseller_admin_role,
92 temp_hash_key,
93 hash_dict)
Matthew Treinishc791ac42014-07-16 09:15:23 -040094 return hash_dict
95
Matthew Treinish09f17832014-08-15 15:22:50 -040096 def is_multi_user(self):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +010097 # Default credentials is not a valid option with locking Account
98 if self.use_default_creds:
99 raise exceptions.InvalidConfiguration(
100 "Account file %s doesn't exist" % CONF.auth.test_accounts_file)
101 else:
Matthew Treinish976e8df2014-12-19 14:21:54 -0500102 return len(self.hash_dict['creds']) > 1
Matthew Treinish09f17832014-08-15 15:22:50 -0400103
Yair Fried76488d72014-10-21 10:13:19 +0300104 def is_multi_tenant(self):
105 return self.is_multi_user()
106
Matthew Treinish09f17832014-08-15 15:22:50 -0400107 def _create_hash_file(self, hash_string):
108 path = os.path.join(os.path.join(self.accounts_dir, hash_string))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400109 if not os.path.isfile(path):
Matthew Treinish4041b262015-02-27 11:18:54 -0500110 with open(path, 'w') as fd:
111 fd.write(self.name)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400112 return True
113 return False
114
115 @lockutils.synchronized('test_accounts_io', external=True)
116 def _get_free_hash(self, hashes):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500117 # Cast as a list because in some edge cases a set will be passed in
118 hashes = list(hashes)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400119 if not os.path.isdir(self.accounts_dir):
120 os.mkdir(self.accounts_dir)
121 # Create File from first hash (since none are in use)
122 self._create_hash_file(hashes[0])
123 return hashes[0]
Matthew Treinish4041b262015-02-27 11:18:54 -0500124 names = []
Matthew Treinish09f17832014-08-15 15:22:50 -0400125 for _hash in hashes:
126 res = self._create_hash_file(_hash)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400127 if res:
Matthew Treinish09f17832014-08-15 15:22:50 -0400128 return _hash
Matthew Treinish4041b262015-02-27 11:18:54 -0500129 else:
130 path = os.path.join(os.path.join(self.accounts_dir,
131 _hash))
132 with open(path, 'r') as fd:
133 names.append(fd.read())
134 msg = ('Insufficient number of users provided. %s have allocated all '
135 'the credentials for this allocation request' % ','.join(names))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400136 raise exceptions.InvalidConfiguration(msg)
137
Matthew Treinish976e8df2014-12-19 14:21:54 -0500138 def _get_match_hash_list(self, roles=None):
139 hashes = []
140 if roles:
141 # Loop over all the creds for each role in the subdict and generate
142 # a list of cred lists for each role
143 for role in roles:
144 temp_hashes = self.hash_dict['roles'].get(role, None)
145 if not temp_hashes:
146 raise exceptions.InvalidConfiguration(
147 "No credentials with role: %s specified in the "
148 "accounts ""file" % role)
149 hashes.append(temp_hashes)
150 # Take the list of lists and do a boolean and between each list to
151 # find the creds which fall under all the specified roles
152 temp_list = set(hashes[0])
153 for hash_list in hashes[1:]:
154 temp_list = temp_list & set(hash_list)
155 hashes = temp_list
156 else:
157 hashes = self.hash_dict['creds'].keys()
158 # NOTE(mtreinish): admin is a special case because of the increased
159 # privlege set which could potentially cause issues on tests where that
160 # is not expected. So unless the admin role isn't specified do not
161 # allocate admin.
162 admin_hashes = self.hash_dict['roles'].get(CONF.identity.admin_role,
163 None)
164 if ((not roles or CONF.identity.admin_role not in roles) and
165 admin_hashes):
166 useable_hashes = [x for x in hashes if x not in admin_hashes]
167 else:
168 useable_hashes = hashes
169 return useable_hashes
170
171 def _get_creds(self, roles=None):
Matthew Treinishb19eeb82014-09-04 09:57:46 -0400172 if self.use_default_creds:
173 raise exceptions.InvalidConfiguration(
174 "Account file %s doesn't exist" % CONF.auth.test_accounts_file)
Matthew Treinish976e8df2014-12-19 14:21:54 -0500175 useable_hashes = self._get_match_hash_list(roles)
176 free_hash = self._get_free_hash(useable_hashes)
177 return self.hash_dict['creds'][free_hash]
Matthew Treinishc791ac42014-07-16 09:15:23 -0400178
179 @lockutils.synchronized('test_accounts_io', external=True)
Matthew Treinish09f17832014-08-15 15:22:50 -0400180 def remove_hash(self, hash_string):
181 hash_path = os.path.join(self.accounts_dir, hash_string)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400182 if not os.path.isfile(hash_path):
183 LOG.warning('Expected an account lock file %s to remove, but '
Matthew Treinish53a2b4b2015-02-24 23:32:07 -0500184 'one did not exist' % hash_path)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400185 else:
186 os.remove(hash_path)
187 if not os.listdir(self.accounts_dir):
188 os.rmdir(self.accounts_dir)
189
190 def get_hash(self, creds):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500191 for _hash in self.hash_dict['creds']:
192 # Comparing on the attributes that are expected in the YAML
193 if all([getattr(creds, k) == self.hash_dict['creds'][_hash][k] for
194 k in creds.get_init_attributes()]):
Matthew Treinish09f17832014-08-15 15:22:50 -0400195 return _hash
Matthew Treinishc791ac42014-07-16 09:15:23 -0400196 raise AttributeError('Invalid credentials %s' % creds)
197
198 def remove_credentials(self, creds):
Matthew Treinish09f17832014-08-15 15:22:50 -0400199 _hash = self.get_hash(creds)
200 self.remove_hash(_hash)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400201
202 def get_primary_creds(self):
Matthew Treinish09f17832014-08-15 15:22:50 -0400203 if self.isolated_creds.get('primary'):
204 return self.isolated_creds.get('primary')
Matthew Treinishc791ac42014-07-16 09:15:23 -0400205 creds = self._get_creds()
Andrea Frittolic3280152015-02-26 12:42:34 +0000206 primary_credential = cred_provider.get_credentials(
207 identity_version=self.identity_version, **creds)
Matthew Treinish09f17832014-08-15 15:22:50 -0400208 self.isolated_creds['primary'] = primary_credential
Matthew Treinishc791ac42014-07-16 09:15:23 -0400209 return primary_credential
210
211 def get_alt_creds(self):
Matthew Treinish09f17832014-08-15 15:22:50 -0400212 if self.isolated_creds.get('alt'):
213 return self.isolated_creds.get('alt')
Matthew Treinishc791ac42014-07-16 09:15:23 -0400214 creds = self._get_creds()
Andrea Frittolic3280152015-02-26 12:42:34 +0000215 alt_credential = cred_provider.get_credentials(
216 identity_version=self.identity_version, **creds)
Matthew Treinish09f17832014-08-15 15:22:50 -0400217 self.isolated_creds['alt'] = alt_credential
Matthew Treinishc791ac42014-07-16 09:15:23 -0400218 return alt_credential
219
Matthew Treinish976e8df2014-12-19 14:21:54 -0500220 def get_creds_by_roles(self, roles, force_new=False):
221 roles = list(set(roles))
222 exist_creds = self.isolated_creds.get(str(roles), None)
223 # The force kwarg is used to allocate an additional set of creds with
224 # the same role list. The index used for the previously allocation
225 # in the isolated_creds dict will be moved.
226 if exist_creds and not force_new:
227 return exist_creds
228 elif exist_creds and force_new:
229 new_index = str(roles) + '-' + str(len(self.isolated_creds))
230 self.isolated_creds[new_index] = exist_creds
231 creds = self._get_creds(roles=roles)
Andrea Frittolic3280152015-02-26 12:42:34 +0000232 role_credential = cred_provider.get_credentials(
233 identity_version=self.identity_version, **creds)
Matthew Treinish976e8df2014-12-19 14:21:54 -0500234 self.isolated_creds[str(roles)] = role_credential
235 return role_credential
236
Matthew Treinishc791ac42014-07-16 09:15:23 -0400237 def clear_isolated_creds(self):
Matthew Treinish09f17832014-08-15 15:22:50 -0400238 for creds in self.isolated_creds.values():
Matthew Treinishc791ac42014-07-16 09:15:23 -0400239 self.remove_credentials(creds)
240
241 def get_admin_creds(self):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500242 return self.get_creds_by_roles([CONF.identity.admin_role])
243
Matthew Treinish4a596932015-03-06 20:37:01 -0500244 def is_role_available(self, role):
245 if self.use_default_creds:
Matthew Treinish976e8df2014-12-19 14:21:54 -0500246 return False
Matthew Treinish4a596932015-03-06 20:37:01 -0500247 else:
248 if self.hash_dict['roles'].get(role):
249 return True
250 return False
251
252 def admin_available(self):
253 return self.is_role_available(CONF.identity.admin_role)
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100254
255
256class NotLockingAccounts(Accounts):
257 """Credentials provider which always returns the first and second
258 configured accounts as primary and alt users.
259 This credential provider can be used in case of serial test execution
260 to preserve the current behaviour of the serial tempest run.
261 """
262
Yair Fried76488d72014-10-21 10:13:19 +0300263 def _unique_creds(self, cred_arg=None):
264 """Verify that the configured credentials are valid and distinct """
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400265 try:
266 user = self.get_primary_creds()
267 alt_user = self.get_alt_creds()
268 return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
269 except exceptions.InvalidCredentials as ic:
270 msg = "At least one of the configured credentials is " \
271 "not valid: %s" % ic.message
272 raise exceptions.InvalidConfiguration(msg)
Andrea Frittoli8283b4e2014-07-17 13:28:58 +0100273
Yair Fried76488d72014-10-21 10:13:19 +0300274 def is_multi_user(self):
275 return self._unique_creds('username')
276
277 def is_multi_tenant(self):
278 return self._unique_creds('tenant_id')
279
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100280 def get_primary_creds(self):
281 if self.isolated_creds.get('primary'):
282 return self.isolated_creds.get('primary')
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400283 primary_credential = cred_provider.get_configured_credentials(
284 credential_type='user', identity_version=self.identity_version)
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100285 self.isolated_creds['primary'] = primary_credential
286 return primary_credential
287
288 def get_alt_creds(self):
289 if self.isolated_creds.get('alt'):
290 return self.isolated_creds.get('alt')
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400291 alt_credential = cred_provider.get_configured_credentials(
292 credential_type='alt_user',
293 identity_version=self.identity_version)
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100294 self.isolated_creds['alt'] = alt_credential
295 return alt_credential
296
297 def clear_isolated_creds(self):
298 self.isolated_creds = {}
299
300 def get_admin_creds(self):
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400301 creds = cred_provider.get_configured_credentials(
302 "identity_admin", fill_in=False)
303 self.isolated_creds['admin'] = creds
304 return creds
Matthew Treinish976e8df2014-12-19 14:21:54 -0500305
306 def get_creds_by_roles(self, roles, force_new=False):
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400307 msg = "Credentials being specified through the config file can not be"\
308 " used with tests that specify using credentials by roles. "\
309 "Either exclude/skip the tests doing this or use either an "\
310 "test_accounts_file or tenant isolation."
311 raise exceptions.InvalidConfiguration(msg)