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