blob: 78e0e72657f47b8e22fd74bb5a7eb6b3834dddbf [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
Matthew Treinishc791ac42014-07-16 09:15:23 -040021import yaml
22
Matthew Treinishf83f35c2015-04-10 11:59:11 -040023from tempest import clients
Matthew Treinishc791ac42014-07-16 09:15:23 -040024from tempest.common import cred_provider
Matthew Treinishf83f35c2015-04-10 11:59:11 -040025from tempest.common import fixed_network
Matthew Treinishc791ac42014-07-16 09:15:23 -040026from tempest import config
27from tempest import exceptions
Matthew Treinishc791ac42014-07-16 09:15:23 -040028
29CONF = config.CONF
30LOG = logging.getLogger(__name__)
31
32
33def read_accounts_yaml(path):
Matthew Treinish2a346982015-07-16 14:24:56 -040034 with open(path, 'r') as yaml_file:
35 accounts = yaml.load(yaml_file)
Matthew Treinishc791ac42014-07-16 09:15:23 -040036 return accounts
37
38
39class Accounts(cred_provider.CredentialProvider):
40
Andrea Frittolic3280152015-02-26 12:42:34 +000041 def __init__(self, identity_version=None, name=None):
42 super(Accounts, self).__init__(identity_version=identity_version,
43 name=name)
Aaron Rosen48070042015-03-30 16:17:11 -070044 if (CONF.auth.test_accounts_file and
45 os.path.isfile(CONF.auth.test_accounts_file)):
Matthew Treinishb19eeb82014-09-04 09:57:46 -040046 accounts = read_accounts_yaml(CONF.auth.test_accounts_file)
47 self.use_default_creds = False
48 else:
49 accounts = {}
50 self.use_default_creds = True
Matthew Treinishc791ac42014-07-16 09:15:23 -040051 self.hash_dict = self.get_hash_dict(accounts)
Matthew Treinishb503f032015-03-12 15:48:49 -040052 self.accounts_dir = os.path.join(lockutils.get_lock_path(CONF),
Doug Hellmann583ce2c2015-03-11 14:55:46 +000053 'test_accounts')
Matthew Treinishc791ac42014-07-16 09:15:23 -040054 self.isolated_creds = {}
55
56 @classmethod
Matthew Treinish976e8df2014-12-19 14:21:54 -050057 def _append_role(cls, role, account_hash, hash_dict):
58 if role in hash_dict['roles']:
59 hash_dict['roles'][role].append(account_hash)
60 else:
61 hash_dict['roles'][role] = [account_hash]
62 return hash_dict
63
64 @classmethod
Matthew Treinishc791ac42014-07-16 09:15:23 -040065 def get_hash_dict(cls, accounts):
Matthew Treinishf83f35c2015-04-10 11:59:11 -040066 hash_dict = {'roles': {}, 'creds': {}, 'networks': {}}
Matthew Treinish976e8df2014-12-19 14:21:54 -050067 # Loop over the accounts read from the yaml file
Matthew Treinishc791ac42014-07-16 09:15:23 -040068 for account in accounts:
Matthew Treinish976e8df2014-12-19 14:21:54 -050069 roles = []
70 types = []
Matthew Treinishf83f35c2015-04-10 11:59:11 -040071 resources = []
Matthew Treinish976e8df2014-12-19 14:21:54 -050072 if 'roles' in account:
73 roles = account.pop('roles')
74 if 'types' in account:
75 types = account.pop('types')
Matthew Treinishf83f35c2015-04-10 11:59:11 -040076 if 'resources' in account:
77 resources = account.pop('resources')
Matthew Treinishc791ac42014-07-16 09:15:23 -040078 temp_hash = hashlib.md5()
Matthew Treinish1c517a22015-04-23 11:39:44 -040079 temp_hash.update(six.text_type(account).encode('utf-8'))
Matthew Treinish976e8df2014-12-19 14:21:54 -050080 temp_hash_key = temp_hash.hexdigest()
81 hash_dict['creds'][temp_hash_key] = account
82 for role in roles:
83 hash_dict = cls._append_role(role, temp_hash_key,
84 hash_dict)
85 # If types are set for the account append the matching role
86 # subdict with the hash
87 for type in types:
88 if type == 'admin':
89 hash_dict = cls._append_role(CONF.identity.admin_role,
90 temp_hash_key, hash_dict)
91 elif type == 'operator':
92 hash_dict = cls._append_role(
93 CONF.object_storage.operator_role, temp_hash_key,
94 hash_dict)
95 elif type == 'reseller_admin':
96 hash_dict = cls._append_role(
97 CONF.object_storage.reseller_admin_role,
98 temp_hash_key,
99 hash_dict)
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400100 # Populate the network subdict
101 for resource in resources:
102 if resource == 'network':
103 hash_dict['networks'][temp_hash_key] = resources[resource]
104 else:
105 LOG.warning('Unkown resource type %s, ignoring this field'
106 % resource)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400107 return hash_dict
108
Matthew Treinish09f17832014-08-15 15:22:50 -0400109 def is_multi_user(self):
Andrea Frittoli8283b4e2014-07-17 13:28:58 +0100110 # Default credentials is not a valid option with locking Account
111 if self.use_default_creds:
112 raise exceptions.InvalidConfiguration(
113 "Account file %s doesn't exist" % CONF.auth.test_accounts_file)
114 else:
Matthew Treinish976e8df2014-12-19 14:21:54 -0500115 return len(self.hash_dict['creds']) > 1
Matthew Treinish09f17832014-08-15 15:22:50 -0400116
Yair Fried76488d72014-10-21 10:13:19 +0300117 def is_multi_tenant(self):
118 return self.is_multi_user()
119
Matthew Treinish09f17832014-08-15 15:22:50 -0400120 def _create_hash_file(self, hash_string):
121 path = os.path.join(os.path.join(self.accounts_dir, hash_string))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400122 if not os.path.isfile(path):
Matthew Treinish4041b262015-02-27 11:18:54 -0500123 with open(path, 'w') as fd:
124 fd.write(self.name)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400125 return True
126 return False
127
128 @lockutils.synchronized('test_accounts_io', external=True)
129 def _get_free_hash(self, hashes):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500130 # Cast as a list because in some edge cases a set will be passed in
131 hashes = list(hashes)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400132 if not os.path.isdir(self.accounts_dir):
133 os.mkdir(self.accounts_dir)
134 # Create File from first hash (since none are in use)
135 self._create_hash_file(hashes[0])
136 return hashes[0]
Matthew Treinish4041b262015-02-27 11:18:54 -0500137 names = []
Matthew Treinish09f17832014-08-15 15:22:50 -0400138 for _hash in hashes:
139 res = self._create_hash_file(_hash)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400140 if res:
Matthew Treinish09f17832014-08-15 15:22:50 -0400141 return _hash
Matthew Treinish4041b262015-02-27 11:18:54 -0500142 else:
143 path = os.path.join(os.path.join(self.accounts_dir,
144 _hash))
145 with open(path, 'r') as fd:
146 names.append(fd.read())
147 msg = ('Insufficient number of users provided. %s have allocated all '
148 'the credentials for this allocation request' % ','.join(names))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400149 raise exceptions.InvalidConfiguration(msg)
150
Matthew Treinish976e8df2014-12-19 14:21:54 -0500151 def _get_match_hash_list(self, roles=None):
152 hashes = []
153 if roles:
154 # Loop over all the creds for each role in the subdict and generate
155 # a list of cred lists for each role
156 for role in roles:
157 temp_hashes = self.hash_dict['roles'].get(role, None)
158 if not temp_hashes:
159 raise exceptions.InvalidConfiguration(
160 "No credentials with role: %s specified in the "
161 "accounts ""file" % role)
162 hashes.append(temp_hashes)
163 # Take the list of lists and do a boolean and between each list to
164 # find the creds which fall under all the specified roles
165 temp_list = set(hashes[0])
166 for hash_list in hashes[1:]:
167 temp_list = temp_list & set(hash_list)
168 hashes = temp_list
169 else:
170 hashes = self.hash_dict['creds'].keys()
171 # NOTE(mtreinish): admin is a special case because of the increased
172 # privlege set which could potentially cause issues on tests where that
173 # is not expected. So unless the admin role isn't specified do not
174 # allocate admin.
175 admin_hashes = self.hash_dict['roles'].get(CONF.identity.admin_role,
176 None)
177 if ((not roles or CONF.identity.admin_role not in roles) and
178 admin_hashes):
179 useable_hashes = [x for x in hashes if x not in admin_hashes]
180 else:
181 useable_hashes = hashes
182 return useable_hashes
183
Matthew Treinishfd683e82015-04-13 20:30:06 -0400184 def _sanitize_creds(self, creds):
185 temp_creds = creds.copy()
186 temp_creds.pop('password')
187 return temp_creds
188
Matthew Treinish976e8df2014-12-19 14:21:54 -0500189 def _get_creds(self, roles=None):
Matthew Treinishb19eeb82014-09-04 09:57:46 -0400190 if self.use_default_creds:
191 raise exceptions.InvalidConfiguration(
192 "Account file %s doesn't exist" % CONF.auth.test_accounts_file)
Matthew Treinish976e8df2014-12-19 14:21:54 -0500193 useable_hashes = self._get_match_hash_list(roles)
194 free_hash = self._get_free_hash(useable_hashes)
Matthew Treinishfd683e82015-04-13 20:30:06 -0400195 clean_creds = self._sanitize_creds(
196 self.hash_dict['creds'][free_hash])
197 LOG.info('%s allocated creds:\n%s' % (self.name, clean_creds))
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400198 return self._wrap_creds_with_network(free_hash)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400199
200 @lockutils.synchronized('test_accounts_io', external=True)
Matthew Treinish09f17832014-08-15 15:22:50 -0400201 def remove_hash(self, hash_string):
202 hash_path = os.path.join(self.accounts_dir, hash_string)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400203 if not os.path.isfile(hash_path):
204 LOG.warning('Expected an account lock file %s to remove, but '
Matthew Treinish53a2b4b2015-02-24 23:32:07 -0500205 'one did not exist' % hash_path)
Matthew Treinishc791ac42014-07-16 09:15:23 -0400206 else:
207 os.remove(hash_path)
208 if not os.listdir(self.accounts_dir):
209 os.rmdir(self.accounts_dir)
210
211 def get_hash(self, creds):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500212 for _hash in self.hash_dict['creds']:
213 # Comparing on the attributes that are expected in the YAML
Andrea Frittoli (andreaf)f39f9f32015-04-13 20:55:31 +0100214 init_attributes = creds.get_init_attributes()
215 hash_attributes = self.hash_dict['creds'][_hash].copy()
216 if ('user_domain_name' in init_attributes and 'user_domain_name'
217 not in hash_attributes):
218 # Allow for the case of domain_name populated from config
219 domain_name = CONF.identity.admin_domain_name
220 hash_attributes['user_domain_name'] = domain_name
221 if all([getattr(creds, k) == hash_attributes[k] for
222 k in init_attributes]):
Matthew Treinish09f17832014-08-15 15:22:50 -0400223 return _hash
Matthew Treinishc791ac42014-07-16 09:15:23 -0400224 raise AttributeError('Invalid credentials %s' % creds)
225
226 def remove_credentials(self, creds):
Matthew Treinish09f17832014-08-15 15:22:50 -0400227 _hash = self.get_hash(creds)
Matthew Treinishfd683e82015-04-13 20:30:06 -0400228 clean_creds = self._sanitize_creds(self.hash_dict['creds'][_hash])
Matthew Treinish09f17832014-08-15 15:22:50 -0400229 self.remove_hash(_hash)
Matthew Treinishfd683e82015-04-13 20:30:06 -0400230 LOG.info("%s returned allocated creds:\n%s" % (self.name, clean_creds))
Matthew Treinishc791ac42014-07-16 09:15:23 -0400231
232 def get_primary_creds(self):
Matthew Treinish09f17832014-08-15 15:22:50 -0400233 if self.isolated_creds.get('primary'):
234 return self.isolated_creds.get('primary')
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400235 net_creds = self._get_creds()
236 self.isolated_creds['primary'] = net_creds
237 return net_creds
Matthew Treinishc791ac42014-07-16 09:15:23 -0400238
239 def get_alt_creds(self):
Matthew Treinish09f17832014-08-15 15:22:50 -0400240 if self.isolated_creds.get('alt'):
241 return self.isolated_creds.get('alt')
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400242 net_creds = self._get_creds()
243 self.isolated_creds['alt'] = net_creds
244 return net_creds
Matthew Treinishc791ac42014-07-16 09:15:23 -0400245
Matthew Treinish976e8df2014-12-19 14:21:54 -0500246 def get_creds_by_roles(self, roles, force_new=False):
247 roles = list(set(roles))
Matthew Treinish1c517a22015-04-23 11:39:44 -0400248 exist_creds = self.isolated_creds.get(six.text_type(roles).encode(
249 'utf-8'), None)
Matthew Treinish976e8df2014-12-19 14:21:54 -0500250 # The force kwarg is used to allocate an additional set of creds with
251 # the same role list. The index used for the previously allocation
252 # in the isolated_creds dict will be moved.
253 if exist_creds and not force_new:
254 return exist_creds
255 elif exist_creds and force_new:
Matthew Treinish1c517a22015-04-23 11:39:44 -0400256 new_index = six.text_type(roles).encode('utf-8') + '-' + \
257 six.text_type(len(self.isolated_creds)).encode('utf-8')
Matthew Treinish976e8df2014-12-19 14:21:54 -0500258 self.isolated_creds[new_index] = exist_creds
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400259 net_creds = self._get_creds(roles=roles)
Matthew Treinish1c517a22015-04-23 11:39:44 -0400260 self.isolated_creds[six.text_type(roles).encode('utf-8')] = net_creds
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400261 return net_creds
Matthew Treinish976e8df2014-12-19 14:21:54 -0500262
Matthew Treinishc791ac42014-07-16 09:15:23 -0400263 def clear_isolated_creds(self):
Matthew Treinish09f17832014-08-15 15:22:50 -0400264 for creds in self.isolated_creds.values():
Matthew Treinishc791ac42014-07-16 09:15:23 -0400265 self.remove_credentials(creds)
266
267 def get_admin_creds(self):
Matthew Treinish976e8df2014-12-19 14:21:54 -0500268 return self.get_creds_by_roles([CONF.identity.admin_role])
269
Matthew Treinish4a596932015-03-06 20:37:01 -0500270 def is_role_available(self, role):
271 if self.use_default_creds:
Matthew Treinish976e8df2014-12-19 14:21:54 -0500272 return False
Matthew Treinish4a596932015-03-06 20:37:01 -0500273 else:
274 if self.hash_dict['roles'].get(role):
275 return True
276 return False
277
278 def admin_available(self):
279 return self.is_role_available(CONF.identity.admin_role)
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100280
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400281 def _wrap_creds_with_network(self, hash):
282 creds_dict = self.hash_dict['creds'][hash]
283 credential = cred_provider.get_credentials(
284 identity_version=self.identity_version, **creds_dict)
285 net_creds = cred_provider.TestResources(credential)
286 net_clients = clients.Manager(credentials=credential)
287 compute_network_client = net_clients.networks_client
288 net_name = self.hash_dict['networks'].get(hash, None)
Matthew Treinishbe855fd2015-04-16 13:10:49 -0400289 try:
290 network = fixed_network.get_network_from_name(
291 net_name, compute_network_client)
292 except exceptions.InvalidConfiguration:
293 network = {}
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400294 net_creds.set_resources(network=network)
295 return net_creds
296
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100297
298class NotLockingAccounts(Accounts):
299 """Credentials provider which always returns the first and second
300 configured accounts as primary and alt users.
301 This credential provider can be used in case of serial test execution
302 to preserve the current behaviour of the serial tempest run.
303 """
304
Yair Fried76488d72014-10-21 10:13:19 +0300305 def _unique_creds(self, cred_arg=None):
306 """Verify that the configured credentials are valid and distinct """
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400307 try:
308 user = self.get_primary_creds()
309 alt_user = self.get_alt_creds()
310 return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
311 except exceptions.InvalidCredentials as ic:
312 msg = "At least one of the configured credentials is " \
313 "not valid: %s" % ic.message
314 raise exceptions.InvalidConfiguration(msg)
Andrea Frittoli8283b4e2014-07-17 13:28:58 +0100315
Yair Fried76488d72014-10-21 10:13:19 +0300316 def is_multi_user(self):
317 return self._unique_creds('username')
318
319 def is_multi_tenant(self):
320 return self._unique_creds('tenant_id')
321
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100322 def get_primary_creds(self):
323 if self.isolated_creds.get('primary'):
324 return self.isolated_creds.get('primary')
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400325 primary_credential = cred_provider.get_configured_credentials(
326 credential_type='user', identity_version=self.identity_version)
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400327 self.isolated_creds['primary'] = cred_provider.TestResources(
328 primary_credential)
329 return self.isolated_creds['primary']
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100330
331 def get_alt_creds(self):
332 if self.isolated_creds.get('alt'):
333 return self.isolated_creds.get('alt')
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400334 alt_credential = cred_provider.get_configured_credentials(
335 credential_type='alt_user',
336 identity_version=self.identity_version)
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400337 self.isolated_creds['alt'] = cred_provider.TestResources(
338 alt_credential)
339 return self.isolated_creds['alt']
Andrea Frittolib1c23fc2014-09-03 13:40:08 +0100340
341 def clear_isolated_creds(self):
342 self.isolated_creds = {}
343
344 def get_admin_creds(self):
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400345 creds = cred_provider.get_configured_credentials(
346 "identity_admin", fill_in=False)
Matthew Treinishf83f35c2015-04-10 11:59:11 -0400347 self.isolated_creds['admin'] = cred_provider.TestResources(creds)
348 return self.isolated_creds['admin']
Matthew Treinish976e8df2014-12-19 14:21:54 -0500349
350 def get_creds_by_roles(self, roles, force_new=False):
Matthew Treinishfc7cd8f2015-03-30 11:51:55 -0400351 msg = "Credentials being specified through the config file can not be"\
352 " used with tests that specify using credentials by roles. "\
353 "Either exclude/skip the tests doing this or use either an "\
354 "test_accounts_file or tenant isolation."
355 raise exceptions.InvalidConfiguration(msg)