blob: 93c8bcf798378d6522f60e214aeab0b2c75a905a [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 Treinishf83f35c2015-04-10 11:59:11 -040022from tempest import clients
Matthew Treinishc791ac42014-07-16 09:15:23 -040023from tempest.common import cred_provider
Matthew Treinishf83f35c2015-04-10 11:59:11 -040024from tempest.common import fixed_network
Matthew Treinishc791ac42014-07-16 09:15:23 -040025from tempest import config
26from tempest import exceptions
Matthew Treinishc791ac42014-07-16 09:15:23 -040027
28CONF = config.CONF
29LOG = logging.getLogger(__name__)
30
31
32def read_accounts_yaml(path):
33 yaml_file = open(path, 'r')
34 accounts = yaml.load(yaml_file)
35 return accounts
36
37
38class Accounts(cred_provider.CredentialProvider):
39
Andrea Frittolic3280152015-02-26 12:42:34 +000040 def __init__(self, identity_version=None, name=None):
41 super(Accounts, self).__init__(identity_version=identity_version,
42 name=name)
Aaron Rosen48070042015-03-30 16:17:11 -070043 if (CONF.auth.test_accounts_file and
44 os.path.isfile(CONF.auth.test_accounts_file)):
Matthew Treinishb19eeb82014-09-04 09:57:46 -040045 accounts = read_accounts_yaml(CONF.auth.test_accounts_file)
46 self.use_default_creds = False
47 else:
48 accounts = {}
49 self.use_default_creds = True
Matthew Treinishc791ac42014-07-16 09:15:23 -040050 self.hash_dict = self.get_hash_dict(accounts)
Matthew Treinishb503f032015-03-12 15:48:49 -040051 self.accounts_dir = os.path.join(lockutils.get_lock_path(CONF),
Doug Hellmann583ce2c2015-03-11 14:55:46 +000052 'test_accounts')
Matthew Treinishc791ac42014-07-16 09:15:23 -040053 self.isolated_creds = {}
54
55 @classmethod
Matthew Treinish976e8df2014-12-19 14:21:54 -050056 def _append_role(cls, role, account_hash, hash_dict):
57 if role in hash_dict['roles']:
58 hash_dict['roles'][role].append(account_hash)
59 else:
60 hash_dict['roles'][role] = [account_hash]
61 return hash_dict
62
63 @classmethod
Matthew Treinishc791ac42014-07-16 09:15:23 -040064 def get_hash_dict(cls, accounts):
Matthew Treinishf83f35c2015-04-10 11:59:11 -040065 hash_dict = {'roles': {}, 'creds': {}, 'networks': {}}
Matthew Treinish976e8df2014-12-19 14:21:54 -050066 # Loop over the accounts read from the yaml file
Matthew Treinishc791ac42014-07-16 09:15:23 -040067 for account in accounts:
Matthew Treinish976e8df2014-12-19 14:21:54 -050068 roles = []
69 types = []
Matthew Treinishf83f35c2015-04-10 11:59:11 -040070 resources = []
Matthew Treinish976e8df2014-12-19 14:21:54 -050071 if 'roles' in account:
72 roles = account.pop('roles')
73 if 'types' in account:
74 types = account.pop('types')
Matthew Treinishf83f35c2015-04-10 11:59:11 -040075 if 'resources' in account:
76 resources = account.pop('resources')
Matthew Treinishc791ac42014-07-16 09:15:23 -040077 temp_hash = hashlib.md5()
78 temp_hash.update(str(account))
Matthew Treinish976e8df2014-12-19 14:21:54 -050079 temp_hash_key = temp_hash.hexdigest()
80 hash_dict['creds'][temp_hash_key] = account
81 for role in roles:
82 hash_dict = cls._append_role(role, temp_hash_key,
83 hash_dict)
84 # If types are set for the account append the matching role
85 # subdict with the hash
86 for type in types:
87 if type == 'admin':
88 hash_dict = cls._append_role(CONF.identity.admin_role,
89 temp_hash_key, hash_dict)
90 elif type == 'operator':
91 hash_dict = cls._append_role(
92 CONF.object_storage.operator_role, temp_hash_key,
93 hash_dict)
94 elif type == 'reseller_admin':
95 hash_dict = cls._append_role(
96 CONF.object_storage.reseller_admin_role,
97 temp_hash_key,
98 hash_dict)
Matthew Treinishf83f35c2015-04-10 11:59:11 -040099 # Populate the network subdict
100 for resource in resources:
101 if resource == 'network':
102 hash_dict['networks'][temp_hash_key] = resources[resource]
103 else:
104 LOG.warning('Unkown resource type %s, ignoring this field'
105 % resource)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400106 return hash_dict
107
Matthew Treinish09f17832014-08-15 15:22:50 -0400108 def is_multi_user(self):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +0100109 # Default credentials is not a valid option with locking Account
110 if self.use_default_creds:
111 raise exceptions.InvalidConfiguration(
112 "Account file %s doesn't exist" % CONF.auth.test_accounts_file)
113 else:
Matthew Treinish976e8df2014-12-19 14:21:54 -0500114 return len(self.hash_dict['creds']) > 1
Matthew Treinish09f17832014-08-15 15:22:50 -0400115
Yair Fried76488d72014-10-21 10:13:19 +0300116 def is_multi_tenant(self):
117 return self.is_multi_user()
118
Matthew Treinish09f17832014-08-15 15:22:50 -0400119 def _create_hash_file(self, hash_string):
120 path = os.path.join(os.path.join(self.accounts_dir, hash_string))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400121 if not os.path.isfile(path):
Matthew Treinish4041b262015-02-27 11:18:54 -0500122 with open(path, 'w') as fd:
123 fd.write(self.name)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400124 return True
125 return False
126
127 @lockutils.synchronized('test_accounts_io', external=True)
128 def _get_free_hash(self, hashes):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500129 # Cast as a list because in some edge cases a set will be passed in
130 hashes = list(hashes)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400131 if not os.path.isdir(self.accounts_dir):
132 os.mkdir(self.accounts_dir)
133 # Create File from first hash (since none are in use)
134 self._create_hash_file(hashes[0])
135 return hashes[0]
Matthew Treinish4041b262015-02-27 11:18:54 -0500136 names = []
Matthew Treinish09f17832014-08-15 15:22:50 -0400137 for _hash in hashes:
138 res = self._create_hash_file(_hash)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400139 if res:
Matthew Treinish09f17832014-08-15 15:22:50 -0400140 return _hash
Matthew Treinish4041b262015-02-27 11:18:54 -0500141 else:
142 path = os.path.join(os.path.join(self.accounts_dir,
143 _hash))
144 with open(path, 'r') as fd:
145 names.append(fd.read())
146 msg = ('Insufficient number of users provided. %s have allocated all '
147 'the credentials for this allocation request' % ','.join(names))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400148 raise exceptions.InvalidConfiguration(msg)
149
Matthew Treinish976e8df2014-12-19 14:21:54 -0500150 def _get_match_hash_list(self, roles=None):
151 hashes = []
152 if roles:
153 # Loop over all the creds for each role in the subdict and generate
154 # a list of cred lists for each role
155 for role in roles:
156 temp_hashes = self.hash_dict['roles'].get(role, None)
157 if not temp_hashes:
158 raise exceptions.InvalidConfiguration(
159 "No credentials with role: %s specified in the "
160 "accounts ""file" % role)
161 hashes.append(temp_hashes)
162 # Take the list of lists and do a boolean and between each list to
163 # find the creds which fall under all the specified roles
164 temp_list = set(hashes[0])
165 for hash_list in hashes[1:]:
166 temp_list = temp_list & set(hash_list)
167 hashes = temp_list
168 else:
169 hashes = self.hash_dict['creds'].keys()
170 # NOTE(mtreinish): admin is a special case because of the increased
171 # privlege set which could potentially cause issues on tests where that
172 # is not expected. So unless the admin role isn't specified do not
173 # allocate admin.
174 admin_hashes = self.hash_dict['roles'].get(CONF.identity.admin_role,
175 None)
176 if ((not roles or CONF.identity.admin_role not in roles) and
177 admin_hashes):
178 useable_hashes = [x for x in hashes if x not in admin_hashes]
179 else:
180 useable_hashes = hashes
181 return useable_hashes
182
Matthew Treinishfd683e82015-04-13 20:30:06 -0400183 def _sanitize_creds(self, creds):
184 temp_creds = creds.copy()
185 temp_creds.pop('password')
186 return temp_creds
187
Matthew Treinish976e8df2014-12-19 14:21:54 -0500188 def _get_creds(self, roles=None):
Matthew Treinishb19eeb82014-09-04 09:57:46 -0400189 if self.use_default_creds:
190 raise exceptions.InvalidConfiguration(
191 "Account file %s doesn't exist" % CONF.auth.test_accounts_file)
Matthew Treinish976e8df2014-12-19 14:21:54 -0500192 useable_hashes = self._get_match_hash_list(roles)
193 free_hash = self._get_free_hash(useable_hashes)
Matthew Treinishfd683e82015-04-13 20:30:06 -0400194 clean_creds = self._sanitize_creds(
195 self.hash_dict['creds'][free_hash])
196 LOG.info('%s allocated creds:\n%s' % (self.name, clean_creds))
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400197 return self._wrap_creds_with_network(free_hash)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400198
199 @lockutils.synchronized('test_accounts_io', external=True)
Matthew Treinish09f17832014-08-15 15:22:50 -0400200 def remove_hash(self, hash_string):
201 hash_path = os.path.join(self.accounts_dir, hash_string)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400202 if not os.path.isfile(hash_path):
203 LOG.warning('Expected an account lock file %s to remove, but '
Matthew Treinish53a2b4b2015-02-24 23:32:07 -0500204 'one did not exist' % hash_path)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400205 else:
206 os.remove(hash_path)
207 if not os.listdir(self.accounts_dir):
208 os.rmdir(self.accounts_dir)
209
210 def get_hash(self, creds):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500211 for _hash in self.hash_dict['creds']:
212 # Comparing on the attributes that are expected in the YAML
Andrea Frittoli (andreaf)f39f9f32015-04-13 20:55:31 +0100213 init_attributes = creds.get_init_attributes()
214 hash_attributes = self.hash_dict['creds'][_hash].copy()
215 if ('user_domain_name' in init_attributes and 'user_domain_name'
216 not in hash_attributes):
217 # Allow for the case of domain_name populated from config
218 domain_name = CONF.identity.admin_domain_name
219 hash_attributes['user_domain_name'] = domain_name
220 if all([getattr(creds, k) == hash_attributes[k] for
221 k in init_attributes]):
Matthew Treinish09f17832014-08-15 15:22:50 -0400222 return _hash
Matthew Treinishc791ac42014-07-16 09:15:23 -0400223 raise AttributeError('Invalid credentials %s' % creds)
224
225 def remove_credentials(self, creds):
Matthew Treinish09f17832014-08-15 15:22:50 -0400226 _hash = self.get_hash(creds)
Matthew Treinishfd683e82015-04-13 20:30:06 -0400227 clean_creds = self._sanitize_creds(self.hash_dict['creds'][_hash])
Matthew Treinish09f17832014-08-15 15:22:50 -0400228 self.remove_hash(_hash)
Matthew Treinishfd683e82015-04-13 20:30:06 -0400229 LOG.info("%s returned allocated creds:\n%s" % (self.name, clean_creds))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400230
231 def get_primary_creds(self):
Matthew Treinish09f17832014-08-15 15:22:50 -0400232 if self.isolated_creds.get('primary'):
233 return self.isolated_creds.get('primary')
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400234 net_creds = self._get_creds()
235 self.isolated_creds['primary'] = net_creds
236 return net_creds
Matthew Treinishc791ac42014-07-16 09:15:23 -0400237
238 def get_alt_creds(self):
Matthew Treinish09f17832014-08-15 15:22:50 -0400239 if self.isolated_creds.get('alt'):
240 return self.isolated_creds.get('alt')
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400241 net_creds = self._get_creds()
242 self.isolated_creds['alt'] = net_creds
243 return net_creds
Matthew Treinishc791ac42014-07-16 09:15:23 -0400244
Matthew Treinish976e8df2014-12-19 14:21:54 -0500245 def get_creds_by_roles(self, roles, force_new=False):
246 roles = list(set(roles))
247 exist_creds = self.isolated_creds.get(str(roles), None)
248 # The force kwarg is used to allocate an additional set of creds with
249 # the same role list. The index used for the previously allocation
250 # in the isolated_creds dict will be moved.
251 if exist_creds and not force_new:
252 return exist_creds
253 elif exist_creds and force_new:
254 new_index = str(roles) + '-' + str(len(self.isolated_creds))
255 self.isolated_creds[new_index] = exist_creds
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400256 net_creds = self._get_creds(roles=roles)
257 self.isolated_creds[str(roles)] = net_creds
258 return net_creds
Matthew Treinish976e8df2014-12-19 14:21:54 -0500259
Matthew Treinishc791ac42014-07-16 09:15:23 -0400260 def clear_isolated_creds(self):
Matthew Treinish09f17832014-08-15 15:22:50 -0400261 for creds in self.isolated_creds.values():
Matthew Treinishc791ac42014-07-16 09:15:23 -0400262 self.remove_credentials(creds)
263
264 def get_admin_creds(self):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500265 return self.get_creds_by_roles([CONF.identity.admin_role])
266
Matthew Treinish4a596932015-03-06 20:37:01 -0500267 def is_role_available(self, role):
268 if self.use_default_creds:
Matthew Treinish976e8df2014-12-19 14:21:54 -0500269 return False
Matthew Treinish4a596932015-03-06 20:37:01 -0500270 else:
271 if self.hash_dict['roles'].get(role):
272 return True
273 return False
274
275 def admin_available(self):
276 return self.is_role_available(CONF.identity.admin_role)
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100277
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400278 def _wrap_creds_with_network(self, hash):
279 creds_dict = self.hash_dict['creds'][hash]
280 credential = cred_provider.get_credentials(
281 identity_version=self.identity_version, **creds_dict)
282 net_creds = cred_provider.TestResources(credential)
283 net_clients = clients.Manager(credentials=credential)
284 compute_network_client = net_clients.networks_client
285 net_name = self.hash_dict['networks'].get(hash, None)
286 network = fixed_network.get_network_from_name(
287 net_name, compute_network_client)
288 net_creds.set_resources(network=network)
289 return net_creds
290
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100291
292class NotLockingAccounts(Accounts):
293 """Credentials provider which always returns the first and second
294 configured accounts as primary and alt users.
295 This credential provider can be used in case of serial test execution
296 to preserve the current behaviour of the serial tempest run.
297 """
298
Yair Fried76488d72014-10-21 10:13:19 +0300299 def _unique_creds(self, cred_arg=None):
300 """Verify that the configured credentials are valid and distinct """
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400301 try:
302 user = self.get_primary_creds()
303 alt_user = self.get_alt_creds()
304 return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
305 except exceptions.InvalidCredentials as ic:
306 msg = "At least one of the configured credentials is " \
307 "not valid: %s" % ic.message
308 raise exceptions.InvalidConfiguration(msg)
Andrea Frittoli8283b4e2014-07-17 13:28:58 +0100309
Yair Fried76488d72014-10-21 10:13:19 +0300310 def is_multi_user(self):
311 return self._unique_creds('username')
312
313 def is_multi_tenant(self):
314 return self._unique_creds('tenant_id')
315
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100316 def get_primary_creds(self):
317 if self.isolated_creds.get('primary'):
318 return self.isolated_creds.get('primary')
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400319 primary_credential = cred_provider.get_configured_credentials(
320 credential_type='user', identity_version=self.identity_version)
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400321 self.isolated_creds['primary'] = cred_provider.TestResources(
322 primary_credential)
323 return self.isolated_creds['primary']
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100324
325 def get_alt_creds(self):
326 if self.isolated_creds.get('alt'):
327 return self.isolated_creds.get('alt')
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400328 alt_credential = cred_provider.get_configured_credentials(
329 credential_type='alt_user',
330 identity_version=self.identity_version)
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400331 self.isolated_creds['alt'] = cred_provider.TestResources(
332 alt_credential)
333 return self.isolated_creds['alt']
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100334
335 def clear_isolated_creds(self):
336 self.isolated_creds = {}
337
338 def get_admin_creds(self):
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400339 creds = cred_provider.get_configured_credentials(
340 "identity_admin", fill_in=False)
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400341 self.isolated_creds['admin'] = cred_provider.TestResources(creds)
342 return self.isolated_creds['admin']
Matthew Treinish976e8df2014-12-19 14:21:54 -0500343
344 def get_creds_by_roles(self, roles, force_new=False):
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400345 msg = "Credentials being specified through the config file can not be"\
346 " used with tests that specify using credentials by roles. "\
347 "Either exclude/skip the tests doing this or use either an "\
348 "test_accounts_file or tenant isolation."
349 raise exceptions.InvalidConfiguration(msg)