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