blob: c95197219ff0d85e402720f3e71efbabcdea59a2 [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 Treinish1c517a22015-04-23 11:39:44 -040020import six
Andrea Frittoli (andreaf)c625bcf2015-10-09 12:09:05 +010021from tempest_lib import auth
Matthew Treinishc791ac42014-07-16 09:15:23 -040022import yaml
23
Matthew Treinishf83f35c2015-04-10 11:59:11 -040024from tempest import clients
Matthew Treinishc791ac42014-07-16 09:15:23 -040025from tempest.common import cred_provider
Matthew Treinishf83f35c2015-04-10 11:59:11 -040026from tempest.common import fixed_network
Matthew Treinishc791ac42014-07-16 09:15:23 -040027from tempest import config
28from tempest import exceptions
Matthew Treinishc791ac42014-07-16 09:15:23 -040029
30CONF = config.CONF
31LOG = logging.getLogger(__name__)
32
33
34def read_accounts_yaml(path):
Matthew Treinish2a346982015-07-16 14:24:56 -040035 with open(path, 'r') as yaml_file:
36 accounts = yaml.load(yaml_file)
Matthew Treinishc791ac42014-07-16 09:15:23 -040037 return accounts
38
39
Andrea Frittoli (andreaf)f9e01262015-05-22 10:24:12 -070040class PreProvisionedCredentialProvider(cred_provider.CredentialProvider):
Matthew Treinishc791ac42014-07-16 09:15:23 -040041
Andrea Frittoli (andreaf)1eb04962015-10-09 14:48:06 +010042 def __init__(self, identity_version, name=None, credentials_domain=None):
Andrea Frittoli (andreaf)f9e01262015-05-22 10:24:12 -070043 super(PreProvisionedCredentialProvider, self).__init__(
Andrea Frittoli (andreaf)1eb04962015-10-09 14:48:06 +010044 identity_version=identity_version, name=name,
45 credentials_domain=credentials_domain)
Aaron Rosen48070042015-03-30 16:17:11 -070046 if (CONF.auth.test_accounts_file and
47 os.path.isfile(CONF.auth.test_accounts_file)):
Matthew Treinishb19eeb82014-09-04 09:57:46 -040048 accounts = read_accounts_yaml(CONF.auth.test_accounts_file)
49 self.use_default_creds = False
50 else:
51 accounts = {}
52 self.use_default_creds = True
Matthew Treinishc791ac42014-07-16 09:15:23 -040053 self.hash_dict = self.get_hash_dict(accounts)
Matthew Treinishb503f032015-03-12 15:48:49 -040054 self.accounts_dir = os.path.join(lockutils.get_lock_path(CONF),
Doug Hellmann583ce2c2015-03-11 14:55:46 +000055 'test_accounts')
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -070056 self._creds = {}
Matthew Treinishc791ac42014-07-16 09:15:23 -040057
58 @classmethod
Matthew Treinish976e8df2014-12-19 14:21:54 -050059 def _append_role(cls, role, account_hash, hash_dict):
60 if role in hash_dict['roles']:
61 hash_dict['roles'][role].append(account_hash)
62 else:
63 hash_dict['roles'][role] = [account_hash]
64 return hash_dict
65
66 @classmethod
Matthew Treinishc791ac42014-07-16 09:15:23 -040067 def get_hash_dict(cls, accounts):
Matthew Treinishf83f35c2015-04-10 11:59:11 -040068 hash_dict = {'roles': {}, 'creds': {}, 'networks': {}}
Matthew Treinish976e8df2014-12-19 14:21:54 -050069 # Loop over the accounts read from the yaml file
Matthew Treinishc791ac42014-07-16 09:15:23 -040070 for account in accounts:
Matthew Treinish976e8df2014-12-19 14:21:54 -050071 roles = []
72 types = []
Matthew Treinishf83f35c2015-04-10 11:59:11 -040073 resources = []
Matthew Treinish976e8df2014-12-19 14:21:54 -050074 if 'roles' in account:
75 roles = account.pop('roles')
76 if 'types' in account:
77 types = account.pop('types')
Matthew Treinishf83f35c2015-04-10 11:59:11 -040078 if 'resources' in account:
79 resources = account.pop('resources')
Matthew Treinishc791ac42014-07-16 09:15:23 -040080 temp_hash = hashlib.md5()
Matthew Treinish1c517a22015-04-23 11:39:44 -040081 temp_hash.update(six.text_type(account).encode('utf-8'))
Matthew Treinish976e8df2014-12-19 14:21:54 -050082 temp_hash_key = temp_hash.hexdigest()
83 hash_dict['creds'][temp_hash_key] = account
84 for role in roles:
85 hash_dict = cls._append_role(role, temp_hash_key,
86 hash_dict)
87 # If types are set for the account append the matching role
88 # subdict with the hash
89 for type in types:
90 if type == 'admin':
91 hash_dict = cls._append_role(CONF.identity.admin_role,
92 temp_hash_key, hash_dict)
93 elif type == 'operator':
94 hash_dict = cls._append_role(
95 CONF.object_storage.operator_role, temp_hash_key,
96 hash_dict)
97 elif type == 'reseller_admin':
98 hash_dict = cls._append_role(
99 CONF.object_storage.reseller_admin_role,
100 temp_hash_key,
101 hash_dict)
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400102 # Populate the network subdict
103 for resource in resources:
104 if resource == 'network':
105 hash_dict['networks'][temp_hash_key] = resources[resource]
106 else:
Zhao Lei647cc182015-09-24 17:47:02 +0800107 LOG.warning('Unknown resource type %s, ignoring this field'
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400108 % resource)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400109 return hash_dict
110
Matthew Treinish09f17832014-08-15 15:22:50 -0400111 def is_multi_user(self):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +0100112 # Default credentials is not a valid option with locking Account
113 if self.use_default_creds:
114 raise exceptions.InvalidConfiguration(
115 "Account file %s doesn't exist" % CONF.auth.test_accounts_file)
116 else:
Matthew Treinish976e8df2014-12-19 14:21:54 -0500117 return len(self.hash_dict['creds']) > 1
Matthew Treinish09f17832014-08-15 15:22:50 -0400118
Yair Fried76488d72014-10-21 10:13:19 +0300119 def is_multi_tenant(self):
120 return self.is_multi_user()
121
Matthew Treinish09f17832014-08-15 15:22:50 -0400122 def _create_hash_file(self, hash_string):
123 path = os.path.join(os.path.join(self.accounts_dir, hash_string))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400124 if not os.path.isfile(path):
Matthew Treinish4041b262015-02-27 11:18:54 -0500125 with open(path, 'w') as fd:
126 fd.write(self.name)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400127 return True
128 return False
129
130 @lockutils.synchronized('test_accounts_io', external=True)
131 def _get_free_hash(self, hashes):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500132 # Cast as a list because in some edge cases a set will be passed in
133 hashes = list(hashes)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400134 if not os.path.isdir(self.accounts_dir):
135 os.mkdir(self.accounts_dir)
136 # Create File from first hash (since none are in use)
137 self._create_hash_file(hashes[0])
138 return hashes[0]
Matthew Treinish4041b262015-02-27 11:18:54 -0500139 names = []
Matthew Treinish09f17832014-08-15 15:22:50 -0400140 for _hash in hashes:
141 res = self._create_hash_file(_hash)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400142 if res:
Matthew Treinish09f17832014-08-15 15:22:50 -0400143 return _hash
Matthew Treinish4041b262015-02-27 11:18:54 -0500144 else:
145 path = os.path.join(os.path.join(self.accounts_dir,
146 _hash))
147 with open(path, 'r') as fd:
148 names.append(fd.read())
149 msg = ('Insufficient number of users provided. %s have allocated all '
150 'the credentials for this allocation request' % ','.join(names))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400151 raise exceptions.InvalidConfiguration(msg)
152
Matthew Treinish976e8df2014-12-19 14:21:54 -0500153 def _get_match_hash_list(self, roles=None):
154 hashes = []
155 if roles:
156 # Loop over all the creds for each role in the subdict and generate
157 # a list of cred lists for each role
158 for role in roles:
159 temp_hashes = self.hash_dict['roles'].get(role, None)
160 if not temp_hashes:
161 raise exceptions.InvalidConfiguration(
162 "No credentials with role: %s specified in the "
163 "accounts ""file" % role)
164 hashes.append(temp_hashes)
165 # Take the list of lists and do a boolean and between each list to
166 # find the creds which fall under all the specified roles
167 temp_list = set(hashes[0])
168 for hash_list in hashes[1:]:
169 temp_list = temp_list & set(hash_list)
170 hashes = temp_list
171 else:
172 hashes = self.hash_dict['creds'].keys()
173 # NOTE(mtreinish): admin is a special case because of the increased
174 # privlege set which could potentially cause issues on tests where that
175 # is not expected. So unless the admin role isn't specified do not
176 # allocate admin.
177 admin_hashes = self.hash_dict['roles'].get(CONF.identity.admin_role,
178 None)
179 if ((not roles or CONF.identity.admin_role not in roles) and
180 admin_hashes):
181 useable_hashes = [x for x in hashes if x not in admin_hashes]
182 else:
183 useable_hashes = hashes
184 return useable_hashes
185
Matthew Treinishfd683e82015-04-13 20:30:06 -0400186 def _sanitize_creds(self, creds):
187 temp_creds = creds.copy()
188 temp_creds.pop('password')
189 return temp_creds
190
Matthew Treinish976e8df2014-12-19 14:21:54 -0500191 def _get_creds(self, roles=None):
Matthew Treinishb19eeb82014-09-04 09:57:46 -0400192 if self.use_default_creds:
193 raise exceptions.InvalidConfiguration(
194 "Account file %s doesn't exist" % CONF.auth.test_accounts_file)
Matthew Treinish976e8df2014-12-19 14:21:54 -0500195 useable_hashes = self._get_match_hash_list(roles)
196 free_hash = self._get_free_hash(useable_hashes)
Matthew Treinishfd683e82015-04-13 20:30:06 -0400197 clean_creds = self._sanitize_creds(
198 self.hash_dict['creds'][free_hash])
199 LOG.info('%s allocated creds:\n%s' % (self.name, clean_creds))
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400200 return self._wrap_creds_with_network(free_hash)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400201
202 @lockutils.synchronized('test_accounts_io', external=True)
Matthew Treinish09f17832014-08-15 15:22:50 -0400203 def remove_hash(self, hash_string):
204 hash_path = os.path.join(self.accounts_dir, hash_string)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400205 if not os.path.isfile(hash_path):
206 LOG.warning('Expected an account lock file %s to remove, but '
Matthew Treinish53a2b4b2015-02-24 23:32:07 -0500207 'one did not exist' % hash_path)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400208 else:
209 os.remove(hash_path)
210 if not os.listdir(self.accounts_dir):
211 os.rmdir(self.accounts_dir)
212
213 def get_hash(self, creds):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500214 for _hash in self.hash_dict['creds']:
215 # Comparing on the attributes that are expected in the YAML
Andrea Frittoli (andreaf)f39f9f32015-04-13 20:55:31 +0100216 init_attributes = creds.get_init_attributes()
217 hash_attributes = self.hash_dict['creds'][_hash].copy()
218 if ('user_domain_name' in init_attributes and 'user_domain_name'
219 not in hash_attributes):
220 # Allow for the case of domain_name populated from config
Andrea Frittoli (andreaf)1eb04962015-10-09 14:48:06 +0100221 domain_name = self.credentials_domain
Andrea Frittoli (andreaf)f39f9f32015-04-13 20:55:31 +0100222 hash_attributes['user_domain_name'] = domain_name
223 if all([getattr(creds, k) == hash_attributes[k] for
224 k in init_attributes]):
Matthew Treinish09f17832014-08-15 15:22:50 -0400225 return _hash
Matthew Treinishc791ac42014-07-16 09:15:23 -0400226 raise AttributeError('Invalid credentials %s' % creds)
227
228 def remove_credentials(self, creds):
Matthew Treinish09f17832014-08-15 15:22:50 -0400229 _hash = self.get_hash(creds)
Matthew Treinishfd683e82015-04-13 20:30:06 -0400230 clean_creds = self._sanitize_creds(self.hash_dict['creds'][_hash])
Matthew Treinish09f17832014-08-15 15:22:50 -0400231 self.remove_hash(_hash)
Matthew Treinishfd683e82015-04-13 20:30:06 -0400232 LOG.info("%s returned allocated creds:\n%s" % (self.name, clean_creds))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400233
234 def get_primary_creds(self):
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700235 if self._creds.get('primary'):
236 return self._creds.get('primary')
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400237 net_creds = self._get_creds()
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700238 self._creds['primary'] = net_creds
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400239 return net_creds
Matthew Treinishc791ac42014-07-16 09:15:23 -0400240
241 def get_alt_creds(self):
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700242 if self._creds.get('alt'):
243 return self._creds.get('alt')
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400244 net_creds = self._get_creds()
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700245 self._creds['alt'] = net_creds
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400246 return net_creds
Matthew Treinishc791ac42014-07-16 09:15:23 -0400247
Matthew Treinish976e8df2014-12-19 14:21:54 -0500248 def get_creds_by_roles(self, roles, force_new=False):
249 roles = list(set(roles))
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700250 exist_creds = self._creds.get(six.text_type(roles).encode(
Matthew Treinish1c517a22015-04-23 11:39:44 -0400251 'utf-8'), None)
Matthew Treinish976e8df2014-12-19 14:21:54 -0500252 # The force kwarg is used to allocate an additional set of creds with
253 # the same role list. The index used for the previously allocation
Ken'ichi Ohmichiedff8862015-10-13 01:10:53 +0000254 # in the _creds dict will be moved.
Matthew Treinish976e8df2014-12-19 14:21:54 -0500255 if exist_creds and not force_new:
256 return exist_creds
257 elif exist_creds and force_new:
Matthew Treinish1c517a22015-04-23 11:39:44 -0400258 new_index = six.text_type(roles).encode('utf-8') + '-' + \
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700259 six.text_type(len(self._creds)).encode('utf-8')
260 self._creds[new_index] = exist_creds
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400261 net_creds = self._get_creds(roles=roles)
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700262 self._creds[six.text_type(roles).encode('utf-8')] = net_creds
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400263 return net_creds
Matthew Treinish976e8df2014-12-19 14:21:54 -0500264
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700265 def clear_creds(self):
266 for creds in self._creds.values():
Matthew Treinishc791ac42014-07-16 09:15:23 -0400267 self.remove_credentials(creds)
268
269 def get_admin_creds(self):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500270 return self.get_creds_by_roles([CONF.identity.admin_role])
271
Matthew Treinish4a596932015-03-06 20:37:01 -0500272 def is_role_available(self, role):
273 if self.use_default_creds:
Matthew Treinish976e8df2014-12-19 14:21:54 -0500274 return False
Matthew Treinish4a596932015-03-06 20:37:01 -0500275 else:
276 if self.hash_dict['roles'].get(role):
277 return True
278 return False
279
280 def admin_available(self):
281 return self.is_role_available(CONF.identity.admin_role)
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100282
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400283 def _wrap_creds_with_network(self, hash):
284 creds_dict = self.hash_dict['creds'][hash]
Andrea Frittoli (andreaf)c625bcf2015-10-09 12:09:05 +0100285 # Make sure a domain scope if defined for users in case of V3
286 creds_dict = self._extend_credentials(creds_dict)
287 # This just builds a Credentials object, it does not validate
288 # nor fill with missing fields.
289 credential = auth.get_credentials(
290 auth_url=None, fill_in=False,
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400291 identity_version=self.identity_version, **creds_dict)
292 net_creds = cred_provider.TestResources(credential)
293 net_clients = clients.Manager(credentials=credential)
John Warren9487a182015-09-14 18:12:56 -0400294 compute_network_client = net_clients.compute_networks_client
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400295 net_name = self.hash_dict['networks'].get(hash, None)
Matthew Treinishbe855fd2015-04-16 13:10:49 -0400296 try:
297 network = fixed_network.get_network_from_name(
298 net_name, compute_network_client)
299 except exceptions.InvalidConfiguration:
300 network = {}
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400301 net_creds.set_resources(network=network)
302 return net_creds
303
Andrea Frittoli (andreaf)c625bcf2015-10-09 12:09:05 +0100304 def _extend_credentials(self, creds_dict):
305 # In case of v3, adds a user_domain_name field to the creds
306 # dict if not defined
307 if self.identity_version == 'v3':
308 user_domain_fields = set(['user_domain_name', 'user_domain_id'])
309 if not user_domain_fields.intersection(set(creds_dict.keys())):
Andrea Frittoli (andreaf)1eb04962015-10-09 14:48:06 +0100310 creds_dict['user_domain_name'] = self.credentials_domain
Andrea Frittoli (andreaf)c625bcf2015-10-09 12:09:05 +0100311 return creds_dict
312
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100313
Andrea Frittoli (andreaf)f9e01262015-05-22 10:24:12 -0700314class NonLockingCredentialProvider(PreProvisionedCredentialProvider):
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100315 """Credentials provider which always returns the first and second
316 configured accounts as primary and alt users.
317 This credential provider can be used in case of serial test execution
318 to preserve the current behaviour of the serial tempest run.
319 """
320
Yair Fried76488d72014-10-21 10:13:19 +0300321 def _unique_creds(self, cred_arg=None):
322 """Verify that the configured credentials are valid and distinct """
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400323 try:
324 user = self.get_primary_creds()
325 alt_user = self.get_alt_creds()
326 return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
327 except exceptions.InvalidCredentials as ic:
328 msg = "At least one of the configured credentials is " \
329 "not valid: %s" % ic.message
330 raise exceptions.InvalidConfiguration(msg)
Andrea Frittoli8283b4e2014-07-17 13:28:58 +0100331
Yair Fried76488d72014-10-21 10:13:19 +0300332 def is_multi_user(self):
333 return self._unique_creds('username')
334
335 def is_multi_tenant(self):
336 return self._unique_creds('tenant_id')
337
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100338 def get_primary_creds(self):
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700339 if self._creds.get('primary'):
340 return self._creds.get('primary')
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400341 primary_credential = cred_provider.get_configured_credentials(
Andrea Frittoli (andreaf)c625bcf2015-10-09 12:09:05 +0100342 fill_in=False, credential_type='user',
343 identity_version=self.identity_version)
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700344 self._creds['primary'] = cred_provider.TestResources(
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400345 primary_credential)
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700346 return self._creds['primary']
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100347
348 def get_alt_creds(self):
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700349 if self._creds.get('alt'):
350 return self._creds.get('alt')
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400351 alt_credential = cred_provider.get_configured_credentials(
Andrea Frittoli (andreaf)c625bcf2015-10-09 12:09:05 +0100352 fill_in=False, credential_type='alt_user',
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400353 identity_version=self.identity_version)
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700354 self._creds['alt'] = cred_provider.TestResources(
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400355 alt_credential)
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700356 return self._creds['alt']
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100357
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700358 def clear_creds(self):
359 self._creds = {}
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100360
361 def get_admin_creds(self):
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400362 creds = cred_provider.get_configured_credentials(
363 "identity_admin", fill_in=False)
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700364 self._creds['admin'] = cred_provider.TestResources(creds)
365 return self._creds['admin']
Matthew Treinish976e8df2014-12-19 14:21:54 -0500366
367 def get_creds_by_roles(self, roles, force_new=False):
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400368 msg = "Credentials being specified through the config file can not be"\
369 " used with tests that specify using credentials by roles. "\
370 "Either exclude/skip the tests doing this or use either an "\
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700371 "test_accounts_file or dynamic credentials."
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400372 raise exceptions.InvalidConfiguration(msg)