blob: 95dcafc3aefe46e74b5bd350477ecea0fd7c5162 [file] [log] [blame]
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +01001# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7#
8# Unless required by applicable law or agreed to in writing, software
9# distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
13
14import os
15
Andrea Frittoli (andreaf)848e3482015-10-12 14:17:21 +010016from oslo_concurrency import lockutils
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +010017from oslo_log import log as logging
18from tempest_lib import auth
19
20from tempest import clients
21from tempest.common import cred_provider
22from tempest.common import dynamic_creds
23from tempest.common import preprov_creds
24from tempest import config
25from tempest import exceptions
26
27CONF = config.CONF
28LOG = logging.getLogger(__name__)
29
30
31"""This module provides factories of credential and credential providers
32
33Credentials providers and clients are (going to be) part of tempest-lib,
34and so they may not hold any dependency to tempest configuration.
35
36Methods in this module collect the relevant configuration details and pass
37them to credentials providers and clients, so that test can have easy
38access to these features.
39
40Client managers with hard-coded configured credentials are also moved here,
41to avoid circular dependencies."""
42
43# === Credential Providers
44
45
Andrea Frittoli (andreaf)848e3482015-10-12 14:17:21 +010046# Subset of the parameters of credential providers that depend on configuration
47def _get_common_provider_params():
48 return {
49 'credentials_domain': CONF.auth.default_credentials_domain_name,
50 'admin_role': CONF.identity.admin_role
51 }
52
53
54def _get_dynamic_provider_params():
55 return _get_common_provider_params()
56
57
58def _get_preprov_provider_params():
59 _common_params = _get_common_provider_params()
60 reseller_admin_role = CONF.object_storage.reseller_admin_role
61 return dict(_common_params, **dict([
62 ('accounts_lock_dir', lockutils.get_lock_path(CONF)),
63 ('test_accounts_file', CONF.auth.test_accounts_file),
64 ('object_storage_operator_role', CONF.object_storage.operator_role),
65 ('object_storage_reseller_admin_role', reseller_admin_role)
66 ]))
67
68
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +010069class LegacyCredentialProvider(cred_provider.CredentialProvider):
70
71 def __init__(self, identity_version):
72 """Credentials provider which returns credentials from tempest.conf
73
74 Credentials provider which always returns the first and second
75 configured accounts as primary and alt users.
76 Credentials from tempest.conf are deprecated, and this credential
77 provider is also accordingly.
78
79 This credential provider can be used in case of serial test execution
80 to preserve the current behaviour of the serial tempest run.
81
82 :param identity_version: Version of the identity API
83 :return: CredentialProvider
84 """
85 super(LegacyCredentialProvider, self).__init__(
86 identity_version=identity_version)
87 self._creds = {}
88
89 def _unique_creds(self, cred_arg=None):
90 """Verify that the configured credentials are valid and distinct """
91 try:
92 user = self.get_primary_creds()
93 alt_user = self.get_alt_creds()
94 return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
95 except exceptions.InvalidCredentials as ic:
96 msg = "At least one of the configured credentials is " \
97 "not valid: %s" % ic.message
98 raise exceptions.InvalidConfiguration(msg)
99
100 def is_multi_user(self):
101 return self._unique_creds('username')
102
103 def is_multi_tenant(self):
104 return self._unique_creds('tenant_id')
105
106 def get_primary_creds(self):
107 if self._creds.get('primary'):
108 return self._creds.get('primary')
109 primary_credential = get_configured_credentials(
110 credential_type='user', fill_in=False,
111 identity_version=self.identity_version)
112 self._creds['primary'] = cred_provider.TestResources(
113 primary_credential)
114 return self._creds['primary']
115
116 def get_alt_creds(self):
117 if self._creds.get('alt'):
118 return self._creds.get('alt')
119 alt_credential = get_configured_credentials(
120 credential_type='alt_user', fill_in=False,
121 identity_version=self.identity_version)
122 self._creds['alt'] = cred_provider.TestResources(
123 alt_credential)
124 return self._creds['alt']
125
126 def clear_creds(self):
127 self._creds = {}
128
129 def get_admin_creds(self):
130 if self._creds.get('admin'):
131 return self._creds.get('admin')
132 creds = get_configured_credentials(
133 "identity_admin", fill_in=False)
134 self._creds['admin'] = cred_provider.TestResources(creds)
135 return self._creds['admin']
136
137 def get_creds_by_roles(self, roles, force_new=False):
138 msg = "Credentials being specified through the config file can not be"\
139 " used with tests that specify using credentials by roles. "\
140 "Either exclude/skip the tests doing this or use either an "\
141 "test_accounts_file or dynamic credentials."
142 raise exceptions.InvalidConfiguration(msg)
143
144 def is_role_available(self, role):
Andrea Frittoli (andreaf)074dee82015-11-20 06:40:54 +0000145 # NOTE(andreaf) LegacyCredentialProvider does not support credentials
146 # by role, so returning always False.
147 # Test that rely on credentials by role should use this to skip
148 # when this is credential provider is used
149 return False
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +0100150
151
152# Return the right implementation of CredentialProvider based on config
153# Dropping interface and password, as they are never used anyways
154# TODO(andreaf) Drop them from the CredentialsProvider interface completely
155def get_credentials_provider(name, network_resources=None,
156 force_tenant_isolation=False,
157 identity_version=None):
158 # If a test requires a new account to work, it can have it via forcing
159 # dynamic credentials. A new account will be produced only for that test.
160 # In case admin credentials are not available for the account creation,
161 # the test should be skipped else it would fail.
162 identity_version = identity_version or CONF.identity.auth_version
163 if CONF.auth.use_dynamic_credentials or force_tenant_isolation:
164 admin_creds = get_configured_credentials(
165 'identity_admin', fill_in=True, identity_version=identity_version)
166 return dynamic_creds.DynamicCredentialProvider(
167 name=name,
168 network_resources=network_resources,
169 identity_version=identity_version,
Andrea Frittoli (andreaf)848e3482015-10-12 14:17:21 +0100170 admin_creds=admin_creds,
171 **_get_dynamic_provider_params())
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +0100172 else:
173 if (CONF.auth.test_accounts_file and
174 os.path.isfile(CONF.auth.test_accounts_file)):
175 # Most params are not relevant for pre-created accounts
176 return preprov_creds.PreProvisionedCredentialProvider(
177 name=name, identity_version=identity_version,
Andrea Frittoli (andreaf)848e3482015-10-12 14:17:21 +0100178 **_get_preprov_provider_params())
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +0100179 else:
180 # Dynamic credentials are disabled, and the account file is not
181 # defined - we fall back on credentials configured in tempest.conf
182 return LegacyCredentialProvider(identity_version=identity_version)
183
184
185# We want a helper function here to check and see if admin credentials
186# are available so we can do a single call from skip_checks if admin
187# creds area available.
188# This depends on identity_version as there may be admin credentials
189# available for v2 but not for v3.
190def is_admin_available(identity_version):
191 is_admin = True
192 # If dynamic credentials is enabled admin will be available
193 if CONF.auth.use_dynamic_credentials:
194 return is_admin
195 # Check whether test accounts file has the admin specified or not
196 elif (CONF.auth.test_accounts_file and
197 os.path.isfile(CONF.auth.test_accounts_file)):
198 check_accounts = preprov_creds.PreProvisionedCredentialProvider(
199 identity_version=identity_version, name='check_admin',
Andrea Frittoli (andreaf)848e3482015-10-12 14:17:21 +0100200 **_get_preprov_provider_params())
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +0100201 if not check_accounts.admin_available():
202 is_admin = False
203 else:
204 try:
205 get_configured_credentials('identity_admin', fill_in=False,
206 identity_version=identity_version)
207 except exceptions.InvalidConfiguration:
208 is_admin = False
209 return is_admin
210
211
212# We want a helper function here to check and see if alt credentials
213# are available so we can do a single call from skip_checks if alt
214# creds area available.
215# This depends on identity_version as there may be alt credentials
216# available for v2 but not for v3.
217def is_alt_available(identity_version):
218 # If dynamic credentials is enabled alt will be available
219 if CONF.auth.use_dynamic_credentials:
220 return True
221 # Check whether test accounts file has the admin specified or not
222 if (CONF.auth.test_accounts_file and
223 os.path.isfile(CONF.auth.test_accounts_file)):
224 check_accounts = preprov_creds.PreProvisionedCredentialProvider(
225 identity_version=identity_version, name='check_alt',
Andrea Frittoli (andreaf)848e3482015-10-12 14:17:21 +0100226 **_get_preprov_provider_params())
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +0100227 else:
228 check_accounts = LegacyCredentialProvider(identity_version)
229 try:
230 if not check_accounts.is_multi_user():
231 return False
232 else:
233 return True
234 except exceptions.InvalidConfiguration:
235 return False
236
237# === Credentials
238
239# Type of credentials available from configuration
240CREDENTIAL_TYPES = {
241 'identity_admin': ('auth', 'admin'),
242 'user': ('identity', None),
243 'alt_user': ('identity', 'alt')
244}
245
246DEFAULT_PARAMS = {
247 'disable_ssl_certificate_validation':
248 CONF.identity.disable_ssl_certificate_validation,
249 'ca_certs': CONF.identity.ca_certificates_file,
250 'trace_requests': CONF.debug.trace_requests
251}
252
253
254# Read credentials from configuration, builds a Credentials object
255# based on the specified or configured version
256def get_configured_credentials(credential_type, fill_in=True,
257 identity_version=None):
258 identity_version = identity_version or CONF.identity.auth_version
259
260 if identity_version not in ('v2', 'v3'):
261 raise exceptions.InvalidConfiguration(
262 'Unsupported auth version: %s' % identity_version)
263
264 if credential_type not in CREDENTIAL_TYPES:
265 raise exceptions.InvalidCredentials()
266 conf_attributes = ['username', 'password', 'tenant_name']
267
268 if identity_version == 'v3':
269 conf_attributes.append('domain_name')
270 # Read the parts of credentials from config
271 params = DEFAULT_PARAMS.copy()
272 section, prefix = CREDENTIAL_TYPES[credential_type]
273 for attr in conf_attributes:
274 _section = getattr(CONF, section)
275 if prefix is None:
276 params[attr] = getattr(_section, attr)
277 else:
278 params[attr] = getattr(_section, prefix + "_" + attr)
279 # Build and validate credentials. We are reading configured credentials,
280 # so validate them even if fill_in is False
281 credentials = get_credentials(fill_in=fill_in,
282 identity_version=identity_version, **params)
283 if not fill_in:
284 if not credentials.is_valid():
285 msg = ("The %s credentials are incorrectly set in the config file."
286 " Double check that all required values are assigned" %
287 credential_type)
288 raise exceptions.InvalidConfiguration(msg)
289 return credentials
290
291
292# Wrapper around auth.get_credentials to use the configured identity version
293# is none is specified
294def get_credentials(fill_in=True, identity_version=None, **kwargs):
295 params = dict(DEFAULT_PARAMS, **kwargs)
296 identity_version = identity_version or CONF.identity.auth_version
297 # In case of "v3" add the domain from config if not specified
298 if identity_version == 'v3':
299 domain_fields = set(x for x in auth.KeystoneV3Credentials.ATTRIBUTES
300 if 'domain' in x)
301 if not domain_fields.intersection(kwargs.keys()):
302 domain_name = CONF.auth.default_credentials_domain_name
303 params['user_domain_name'] = domain_name
304
305 auth_url = CONF.identity.uri_v3
306 else:
307 auth_url = CONF.identity.uri
308 return auth.get_credentials(auth_url,
309 fill_in=fill_in,
310 identity_version=identity_version,
311 **params)
312
313# === Credential / client managers
314
315
316class ConfiguredUserManager(clients.Manager):
Ken'ichi Ohmichicb67d2d2015-11-19 08:23:22 +0000317 """Manager that uses user credentials for its managed client objects"""
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +0100318
319 def __init__(self, service=None):
320 super(ConfiguredUserManager, self).__init__(
321 credentials=get_configured_credentials('user'),
322 service=service)
323
324
325class AdminManager(clients.Manager):
Ken'ichi Ohmichicb67d2d2015-11-19 08:23:22 +0000326 """Manager that uses admin credentials for its managed client objects"""
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +0100327
328 def __init__(self, service=None):
329 super(AdminManager, self).__init__(
330 credentials=get_configured_credentials('identity_admin'),
331 service=service)