blob: 45b818f54a3e2c1c2b620d1158eb5fb6c80cd19e [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Jay Pipes051075a2012-04-28 17:39:37 -04002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Attila Fazekasf86fa312013-07-30 19:56:39 +020016import atexit
Masayuki Igawa80c1b9f2013-10-07 17:19:11 +090017import functools
Ian Wienand98c35f32013-07-23 20:34:23 +100018import os
Marc Kodererb2978da2014-03-26 13:45:43 +010019import re
Attila Fazekas53943322014-02-10 16:07:34 +010020import sys
Jay Pipes051075a2012-04-28 17:39:37 -040021import time
Marc Koderer24eb89c2014-01-31 11:23:33 +010022import uuid
Jay Pipes051075a2012-04-28 17:39:37 -040023
Matthew Treinish78561ad2013-07-26 11:41:56 -040024import fixtures
Doug Hellmann583ce2c2015-03-11 14:55:46 +000025from oslo_log import log as logging
Matthew Treinish21905512015-07-13 10:33:35 -040026from oslo_serialization import jsonutils as json
Doug Hellmann583ce2c2015-03-11 14:55:46 +000027from oslo_utils import importutils
Chris Hoge296558c2015-02-19 00:29:49 -060028import six
Harshada Mangesh Kakad71f6b972015-12-22 09:46:48 -080029from six.moves import urllib
Marc Koderer674c8fc2014-03-17 09:45:04 +010030import testscenarios
ivan-zhu1feeb382013-01-24 10:14:39 +080031import testtools
Jay Pipes051075a2012-04-28 17:39:37 -040032
Matthew Treinish3e046852013-07-23 16:00:24 -040033from tempest import clients
Jamie Lennox15350172015-08-17 10:54:25 +100034from tempest.common import cred_client
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +010035from tempest.common import credentials_factory as credentials
Rohan Kanade9ce97df2013-12-10 18:59:35 +053036from tempest.common import fixed_network
Marc Koderer6ee82dc2014-02-17 10:26:29 +010037import tempest.common.generator.valid_generator as valid
nithya-ganesan222efd72015-01-22 12:20:27 +000038import tempest.common.validation_resources as vresources
Attila Fazekasdc216422013-01-29 15:12:14 +010039from tempest import config
Matthew Treinish16c43792013-09-09 19:55:23 +000040from tempest import exceptions
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050041from tempest.lib import decorators
Jay Pipes051075a2012-04-28 17:39:37 -040042
43LOG = logging.getLogger(__name__)
44
Sean Dague86bd8422013-12-20 09:56:44 -050045CONF = config.CONF
46
Matthew Treinishc1802bc2015-12-03 18:48:11 -050047idempotent_id = decorators.idempotent_id
48
Jay Pipes051075a2012-04-28 17:39:37 -040049
Yaroslav Lobankovda999f72015-06-30 20:32:55 +030050def attr(**kwargs):
liuchenhong00caec52015-07-19 22:40:28 +080051 """A decorator which applies the testtools attr decorator
Chris Yeoh55530bb2013-02-08 16:04:27 +103052
Matthew Treinisha74f5d42014-02-07 20:25:44 -050053 This decorator applies the testtools.testcase.attr if it is in the list of
54 attributes to testtools we want to apply.
Attila Fazekasb2902af2013-02-16 16:22:44 +010055 """
Chris Yeoh55530bb2013-02-08 16:04:27 +103056
57 def decorator(f):
Giulio Fidente4946a052013-05-14 12:23:51 +020058 if 'type' in kwargs and isinstance(kwargs['type'], str):
59 f = testtools.testcase.attr(kwargs['type'])(f)
60 elif 'type' in kwargs and isinstance(kwargs['type'], list):
61 for attr in kwargs['type']:
62 f = testtools.testcase.attr(attr)(f)
Matthew Treinisha74f5d42014-02-07 20:25:44 -050063 return f
Chris Yeoh55530bb2013-02-08 16:04:27 +103064
65 return decorator
66
67
Matthew Treinish3d8c7322014-08-03 23:53:28 -040068def get_service_list():
Matthew Treinish8afbffd2014-01-21 23:56:13 +000069 service_list = {
70 'compute': CONF.service_available.nova,
71 'image': CONF.service_available.glance,
Adam Gandelman4a48a602014-03-20 18:23:18 -070072 'baremetal': CONF.service_available.ironic,
Matthew Treinish8afbffd2014-01-21 23:56:13 +000073 'volume': CONF.service_available.cinder,
74 'orchestration': CONF.service_available.heat,
75 # NOTE(mtreinish) nova-network will provide networking functionality
76 # if neutron isn't available, so always set to True.
77 'network': True,
78 'identity': True,
79 'object_storage': CONF.service_available.swift,
80 'dashboard': CONF.service_available.horizon,
ekhugen7aff0992014-08-04 19:01:57 +000081 'telemetry': CONF.service_available.ceilometer,
Matthew Treinishb66c94e2015-03-11 13:00:48 -040082 'data_processing': CONF.service_available.sahara,
83 'database': CONF.service_available.trove
Matthew Treinish8afbffd2014-01-21 23:56:13 +000084 }
Matthew Treinish3d8c7322014-08-03 23:53:28 -040085 return service_list
Matthew Treinish16c43792013-09-09 19:55:23 +000086
Matthew Treinish3d8c7322014-08-03 23:53:28 -040087
Yaroslav Lobankovda999f72015-06-30 20:32:55 +030088def services(*args):
Matthew Treinish3d8c7322014-08-03 23:53:28 -040089 """A decorator used to set an attr for each service used in a test case
90
91 This decorator applies a testtools attr for each service that gets
92 exercised by a test case.
93 """
Matthew Treinish16c43792013-09-09 19:55:23 +000094 def decorator(f):
Matthew Treinish3d8c7322014-08-03 23:53:28 -040095 services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
96 'network', 'identity', 'object_storage', 'dashboard',
Matthew Treinishb66c94e2015-03-11 13:00:48 -040097 'telemetry', 'data_processing', 'database']
Matthew Treinish16c43792013-09-09 19:55:23 +000098 for service in args:
Matthew Treinish3d8c7322014-08-03 23:53:28 -040099 if service not in services:
100 raise exceptions.InvalidServiceTag('%s is not a valid '
101 'service' % service)
Matthew Treinish16c43792013-09-09 19:55:23 +0000102 attr(type=list(args))(f)
Matthew Treinish8afbffd2014-01-21 23:56:13 +0000103
104 @functools.wraps(f)
105 def wrapper(self, *func_args, **func_kwargs):
Matthew Treinish3d8c7322014-08-03 23:53:28 -0400106 service_list = get_service_list()
107
Matthew Treinish8afbffd2014-01-21 23:56:13 +0000108 for service in args:
109 if not service_list[service]:
110 msg = 'Skipped because the %s service is not available' % (
111 service)
112 raise testtools.TestCase.skipException(msg)
113 return f(self, *func_args, **func_kwargs)
114 return wrapper
Matthew Treinish16c43792013-09-09 19:55:23 +0000115 return decorator
116
117
Yaroslav Lobankovda999f72015-06-30 20:32:55 +0300118def stresstest(**kwargs):
Marc Koderer32221b8e2013-08-23 13:57:50 +0200119 """Add stress test decorator
120
121 For all functions with this decorator a attr stress will be
122 set automatically.
123
124 @param class_setup_per: allowed values are application, process, action
125 ``application``: once in the stress job lifetime
126 ``process``: once in the worker process lifetime
127 ``action``: on each action
Marc Kodererb0604412013-09-02 09:43:40 +0200128 @param allow_inheritance: allows inheritance of this attribute
Marc Koderer32221b8e2013-08-23 13:57:50 +0200129 """
130 def decorator(f):
131 if 'class_setup_per' in kwargs:
132 setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
133 else:
134 setattr(f, "st_class_setup_per", 'process')
Marc Kodererb0604412013-09-02 09:43:40 +0200135 if 'allow_inheritance' in kwargs:
136 setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
137 else:
138 setattr(f, "st_allow_inheritance", False)
Marc Koderer32221b8e2013-08-23 13:57:50 +0200139 attr(type='stress')(f)
140 return f
141 return decorator
142
143
Yaroslav Lobankovda999f72015-06-30 20:32:55 +0300144def requires_ext(**kwargs):
Matthew Treinishe3d26142013-11-26 19:14:58 +0000145 """A decorator to skip tests if an extension is not enabled
146
147 @param extension
148 @param service
149 """
150 def decorator(func):
151 @functools.wraps(func)
152 def wrapper(*func_args, **func_kwargs):
153 if not is_extension_enabled(kwargs['extension'],
154 kwargs['service']):
155 msg = "Skipped because %s extension: %s is not enabled" % (
156 kwargs['service'], kwargs['extension'])
157 raise testtools.TestCase.skipException(msg)
158 return func(*func_args, **func_kwargs)
159 return wrapper
160 return decorator
161
162
163def is_extension_enabled(extension_name, service):
164 """A function that will check the list of enabled extensions from config
165
166 """
Matthew Treinishe3d26142013-11-26 19:14:58 +0000167 config_dict = {
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000168 'compute': CONF.compute_feature_enabled.api_extensions,
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000169 'volume': CONF.volume_feature_enabled.api_extensions,
170 'network': CONF.network_feature_enabled.api_extensions,
171 'object': CONF.object_storage_feature_enabled.discoverable_apis,
Jane Zadorozhna121576d2015-06-23 12:57:13 +0300172 'identity': CONF.identity_feature_enabled.api_extensions
Matthew Treinishe3d26142013-11-26 19:14:58 +0000173 }
Simeon Monov5d7effe2014-07-16 07:32:38 +0300174 if len(config_dict[service]) == 0:
175 return False
Matthew Treinishe3d26142013-11-26 19:14:58 +0000176 if config_dict[service][0] == 'all':
177 return True
178 if extension_name in config_dict[service]:
179 return True
180 return False
181
Ian Wienand98c35f32013-07-23 20:34:23 +1000182
Attila Fazekasf86fa312013-07-30 19:56:39 +0200183at_exit_set = set()
184
185
186def validate_tearDownClass():
187 if at_exit_set:
Sean Dagueeb1523b2014-03-10 10:17:44 -0400188 LOG.error(
189 "tearDownClass does not call the super's "
190 "tearDownClass in these classes: \n"
191 + str(at_exit_set))
192
Attila Fazekasf86fa312013-07-30 19:56:39 +0200193
194atexit.register(validate_tearDownClass)
195
Attila Fazekas53943322014-02-10 16:07:34 +0100196
Matthew Treinish2474f412014-11-17 18:11:56 -0500197class BaseTestCase(testtools.testcase.WithAttributes,
198 testtools.TestCase):
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100199 """The test base class defines Tempest framework for class level fixtures.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000200
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100201 `setUpClass` and `tearDownClass` are defined here and cannot be overwritten
202 by subclasses (enforced via hacking rule T105).
203
204 Set-up is split in a series of steps (setup stages), which can be
205 overwritten by test classes. Set-up stages are:
206 - skip_checks
207 - setup_credentials
208 - setup_clients
209 - resource_setup
210
211 Tear-down is also split in a series of steps (teardown stages), which are
212 stacked for execution only if the corresponding setup stage had been
213 reached during the setup phase. Tear-down stages are:
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700214 - clear_credentials (defined in the base test class)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100215 - resource_cleanup
216 """
Attila Fazekasc43fec82013-04-09 23:17:52 +0200217
Attila Fazekasf86fa312013-07-30 19:56:39 +0200218 setUpClassCalled = False
Marc Koderer24eb89c2014-01-31 11:23:33 +0100219 _service = None
Attila Fazekasf86fa312013-07-30 19:56:39 +0200220
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000221 # NOTE(andreaf) credentials holds a list of the credentials to be allocated
Andrea Frittoli (andreaf)825b2d32015-04-08 20:58:01 +0100222 # at class setup time. Credential types can be 'primary', 'alt', 'admin' or
223 # a list of roles - the first element of the list being a label, and the
224 # rest the actual roles
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000225 credentials = []
nithya-ganesan222efd72015-01-22 12:20:27 +0000226 # Resources required to validate a server using ssh
227 validation_resources = {}
Matthew Treinish9f756a02014-01-15 10:26:07 -0500228 network_resources = {}
229
Sean Dague2ef32ac2014-06-09 11:32:23 -0400230 # NOTE(sdague): log_format is defined inline here instead of using the oslo
231 # default because going through the config path recouples config to the
232 # stress tests too early, and depending on testr order will fail unit tests
233 log_format = ('%(asctime)s %(process)d %(levelname)-8s '
234 '[%(name)s] %(message)s')
235
Ryota MIBU60687e52015-12-09 18:37:39 +0900236 # Client manager class to use in this test case.
237 client_manager = clients.Manager
238
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200239 @classmethod
240 def setUpClass(cls):
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100241 # It should never be overridden by descendants
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200242 if hasattr(super(BaseTestCase, cls), 'setUpClass'):
243 super(BaseTestCase, cls).setUpClass()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200244 cls.setUpClassCalled = True
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100245 # Stack of (name, callable) to be invoked in reverse order at teardown
246 cls.teardowns = []
247 # All the configuration checks that may generate a skip
248 cls.skip_checks()
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100249 try:
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100250 # Allocation of all required credentials and client managers
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700251 cls.teardowns.append(('credentials', cls.clear_credentials))
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100252 cls.setup_credentials()
253 # Shortcuts to clients
254 cls.setup_clients()
255 # Additional class-wide test resources
256 cls.teardowns.append(('resources', cls.resource_cleanup))
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100257 cls.resource_setup()
258 except Exception:
259 etype, value, trace = sys.exc_info()
Matthew Treinished2ad4f2014-12-23 15:18:32 -0500260 LOG.info("%s raised in %s.setUpClass. Invoking tearDownClass." % (
261 etype, cls.__name__))
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100262 cls.tearDownClass()
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100263 try:
Matthew Treinish843227d2015-04-23 10:17:17 -0400264 six.reraise(etype, value, trace)
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100265 finally:
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100266 del trace # to avoid circular refs
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200267
Attila Fazekasf86fa312013-07-30 19:56:39 +0200268 @classmethod
269 def tearDownClass(cls):
Attila Fazekas5d275302013-08-29 12:35:12 +0200270 at_exit_set.discard(cls)
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100271 # It should never be overridden by descendants
Attila Fazekasf86fa312013-07-30 19:56:39 +0200272 if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
273 super(BaseTestCase, cls).tearDownClass()
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100274 # Save any existing exception, we always want to re-raise the original
275 # exception only
276 etype, value, trace = sys.exc_info()
277 # If there was no exception during setup we shall re-raise the first
278 # exception in teardown
279 re_raise = (etype is None)
280 while cls.teardowns:
281 name, teardown = cls.teardowns.pop()
282 # Catch any exception in tearDown so we can re-raise the original
283 # exception at the end
284 try:
285 teardown()
286 except Exception as te:
287 sys_exec_info = sys.exc_info()
288 tetype = sys_exec_info[0]
289 # TODO(andreaf): Till we have the ability to cleanup only
290 # resources that were successfully setup in resource_cleanup,
291 # log AttributeError as info instead of exception.
292 if tetype is AttributeError and name == 'resources':
293 LOG.info("tearDownClass of %s failed: %s" % (name, te))
294 else:
295 LOG.exception("teardown of %s failed: %s" % (name, te))
296 if not etype:
297 etype, value, trace = sys_exec_info
Joshua Whitebd769602016-02-02 09:30:11 -0800298 # If exceptions were raised during teardown, and not before, re-raise
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100299 # the first one
300 if re_raise and etype is not None:
301 try:
Matthew Treinish843227d2015-04-23 10:17:17 -0400302 six.reraise(etype, value, trace)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100303 finally:
304 del trace # to avoid circular refs
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100305
306 @classmethod
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100307 def skip_checks(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000308 """Class level skip checks.
309
310 Subclasses verify in here all conditions that might prevent the
311 execution of the entire test class.
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100312 Checks implemented here may not make use API calls, and should rely on
313 configuration alone.
314 In general skip checks that require an API call are discouraged.
315 If one is really needed it may be implemented either in the
316 resource_setup or at test level.
317 """
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100318 identity_version = cls.get_identity_version()
319 if 'admin' in cls.credentials and not credentials.is_admin_available(
320 identity_version=identity_version):
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000321 msg = "Missing Identity Admin API credentials in configuration."
322 raise cls.skipException(msg)
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100323 if 'alt' in cls.credentials and not credentials.is_alt_available(
324 identity_version=identity_version):
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000325 msg = "Missing a 2nd set of API credentials in configuration."
326 raise cls.skipException(msg)
Andrea Frittoli (andreaf)41601412015-05-12 16:39:03 +0100327 if hasattr(cls, 'identity_version'):
328 if cls.identity_version == 'v2':
329 if not CONF.identity_feature_enabled.api_v2:
330 raise cls.skipException("Identity api v2 is not enabled")
331 elif cls.identity_version == 'v3':
332 if not CONF.identity_feature_enabled.api_v3:
333 raise cls.skipException("Identity api v3 is not enabled")
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100334
335 @classmethod
336 def setup_credentials(cls):
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000337 """Allocate credentials and the client managers from them.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000338
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000339 A test class that requires network resources must override
340 setup_credentials and defined the required resources before super
341 is invoked.
342 """
343 for credentials_type in cls.credentials:
344 # This may raise an exception in case credentials are not available
345 # In that case we want to let the exception through and the test
346 # fail accordingly
Andrea Frittoli (andreaf)825b2d32015-04-08 20:58:01 +0100347 if isinstance(credentials_type, six.string_types):
348 manager = cls.get_client_manager(
349 credential_type=credentials_type)
350 setattr(cls, 'os_%s' % credentials_type, manager)
351 # Setup some common aliases
352 # TODO(andreaf) The aliases below are a temporary hack
353 # to avoid changing too much code in one patch. They should
354 # be removed eventually
355 if credentials_type == 'primary':
356 cls.os = cls.manager = cls.os_primary
357 if credentials_type == 'admin':
358 cls.os_adm = cls.admin_manager = cls.os_admin
359 if credentials_type == 'alt':
360 cls.alt_manager = cls.os_alt
361 elif isinstance(credentials_type, list):
362 manager = cls.get_client_manager(roles=credentials_type[1:],
363 force_new=True)
364 setattr(cls, 'os_roles_%s' % credentials_type[0], manager)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100365
366 @classmethod
367 def setup_clients(cls):
368 """Create links to the clients into the test object."""
369 # TODO(andreaf) There is a fair amount of code that could me moved from
370 # base / test classes in here. Ideally tests should be able to only
371 # specify which client is `client` and nothing else.
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100372 pass
Attila Fazekasf86fa312013-07-30 19:56:39 +0200373
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000374 @classmethod
375 def resource_setup(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000376 """Class level resource setup for test cases."""
nithya-ganesan222efd72015-01-22 12:20:27 +0000377 if hasattr(cls, "os"):
378 cls.validation_resources = vresources.create_validation_resources(
379 cls.os, cls.validation_resources)
380 else:
zhangguoqing6c096642016-01-04 06:17:21 +0000381 LOG.warning("Client manager not found, validation resources not"
382 " created")
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000383
384 @classmethod
385 def resource_cleanup(cls):
386 """Class level resource cleanup for test cases.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000387
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000388 Resource cleanup must be able to handle the case of partially setup
389 resources, in case a failure during `resource_setup` should happen.
390 """
nithya-ganesan222efd72015-01-22 12:20:27 +0000391 if cls.validation_resources:
392 if hasattr(cls, "os"):
393 vresources.clear_validation_resources(cls.os,
394 cls.validation_resources)
395 cls.validation_resources = {}
396 else:
zhangguoqing6c096642016-01-04 06:17:21 +0000397 LOG.warning("Client manager not found, validation resources "
398 "not deleted")
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000399
Attila Fazekasf86fa312013-07-30 19:56:39 +0200400 def setUp(self):
401 super(BaseTestCase, self).setUp()
402 if not self.setUpClassCalled:
403 raise RuntimeError("setUpClass does not calls the super's"
404 "setUpClass in the "
405 + self.__class__.__name__)
406 at_exit_set.add(self.__class__)
Matthew Treinish78561ad2013-07-26 11:41:56 -0400407 test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
408 try:
409 test_timeout = int(test_timeout)
410 except ValueError:
411 test_timeout = 0
412 if test_timeout > 0:
Attila Fazekasf86fa312013-07-30 19:56:39 +0200413 self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400414
415 if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
416 os.environ.get('OS_STDOUT_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200417 stdout = self.useFixture(fixtures.StringStream('stdout')).stream
418 self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400419 if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
420 os.environ.get('OS_STDERR_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200421 stderr = self.useFixture(fixtures.StringStream('stderr')).stream
422 self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
Attila Fazekas31388072013-08-15 08:58:07 +0200423 if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
424 os.environ.get('OS_LOG_CAPTURE') != '0'):
Attila Fazekas31388072013-08-15 08:58:07 +0200425 self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
Sean Dague2ef32ac2014-06-09 11:32:23 -0400426 format=self.log_format,
Attila Fazekas90445be2013-10-24 16:46:03 +0200427 level=None))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400428
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100429 @property
430 def credentials_provider(self):
431 return self._get_credentials_provider()
432
Jamie Lennox15350172015-08-17 10:54:25 +1000433 @property
434 def identity_utils(self):
435 """A client that abstracts v2 and v3 identity operations.
436
437 This can be used for creating and tearing down projects in tests. It
438 should not be used for testing identity features.
439 """
440 if CONF.identity.auth_version == 'v2':
441 client = self.os_admin.identity_client
Daniel Mellado7aea5342016-02-09 09:10:12 +0000442 users_client = self.os_admin.users_client
Daniel Melladob04da902015-11-20 17:43:12 +0100443 project_client = self.os_admin.tenants_client
Daniel Mellado6b16b922015-12-07 12:43:08 +0000444 roles_client = self.os_admin.roles_client
Daniel Mellado91a26b62016-02-11 11:13:04 +0000445 domains_client = None
Jamie Lennox15350172015-08-17 10:54:25 +1000446 else:
447 client = self.os_admin.identity_v3_client
Yaroslav Lobankov47a93ab2016-02-07 16:32:49 -0600448 project_client = self.os_adm.projects_client
Daniel Mellado7aea5342016-02-09 09:10:12 +0000449 users_client = self.os_admin.users_v3_client
Daniel Mellado6b16b922015-12-07 12:43:08 +0000450 roles_client = None
Daniel Mellado91a26b62016-02-11 11:13:04 +0000451 domains_client = self.os_admin.domains_client
Jamie Lennox15350172015-08-17 10:54:25 +1000452
453 try:
454 domain = client.auth_provider.credentials.project_domain_name
455 except AttributeError:
456 domain = 'Default'
457
Daniel Melladob04da902015-11-20 17:43:12 +0100458 return cred_client.get_creds_client(client, project_client,
Daniel Mellado82c83a52015-12-09 15:16:49 +0000459 users_client,
Daniel Mellado7aea5342016-02-09 09:10:12 +0000460 roles_client,
Daniel Mellado91a26b62016-02-11 11:13:04 +0000461 domains_client,
Daniel Melladob04da902015-11-20 17:43:12 +0100462 project_domain_name=domain)
Jamie Lennox15350172015-08-17 10:54:25 +1000463
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100464 @classmethod
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100465 def get_identity_version(cls):
466 """Returns the identity version used by the test class"""
467 identity_version = getattr(cls, 'identity_version', None)
468 return identity_version or CONF.identity.auth_version
469
470 @classmethod
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100471 def _get_credentials_provider(cls):
472 """Returns a credentials provider
473
474 If no credential provider exists yet creates one.
475 It uses self.identity_version if defined, or the configuration value
476 """
477 if (not hasattr(cls, '_creds_provider') or not cls._creds_provider or
478 not cls._creds_provider.name == cls.__name__):
479 force_tenant_isolation = getattr(cls, 'force_tenant_isolation',
480 False)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100481
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700482 cls._creds_provider = credentials.get_credentials_provider(
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100483 name=cls.__name__, network_resources=cls.network_resources,
484 force_tenant_isolation=force_tenant_isolation,
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100485 identity_version=cls.get_identity_version())
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100486 return cls._creds_provider
487
Matthew Treinish3e046852013-07-23 16:00:24 -0400488 @classmethod
Andrea Frittoli (andreaf)41601412015-05-12 16:39:03 +0100489 def get_client_manager(cls, credential_type=None, roles=None,
490 force_new=None):
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100491 """Returns an OpenStack client manager
492
493 Returns an OpenStack client manager based on either credential_type
494 or a list of roles. If neither is specified, it defaults to
495 credential_type 'primary'
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100496 :param credential_type: string - primary, alt or admin
497 :param roles: list of roles
498
lei zhangdd552b22015-11-25 20:41:48 +0800499 :returns: the created client manager
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100500 :raises skipException: if the requested credentials are not available
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700501 """
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100502 if all([roles, credential_type]):
503 msg = "Cannot get credentials by type and roles at the same time"
504 raise ValueError(msg)
505 if not any([roles, credential_type]):
506 credential_type = 'primary'
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100507 cred_provider = cls._get_credentials_provider()
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100508 if roles:
509 for role in roles:
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100510 if not cred_provider.is_role_available(role):
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100511 skip_msg = (
512 "%s skipped because the configured credential provider"
513 " is not able to provide credentials with the %s role "
514 "assigned." % (cls.__name__, role))
515 raise cls.skipException(skip_msg)
516 params = dict(roles=roles)
517 if force_new is not None:
518 params.update(force_new=force_new)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100519 creds = cred_provider.get_creds_by_roles(**params)
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000520 else:
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100521 credentials_method = 'get_%s_creds' % credential_type
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100522 if hasattr(cred_provider, credentials_method):
523 creds = getattr(cred_provider, credentials_method)()
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100524 else:
525 raise exceptions.InvalidCredentials(
526 "Invalid credentials type %s" % credential_type)
Ghanshyam05049dd2016-02-12 17:44:48 +0900527 return cls.client_manager(credentials=creds, service=cls._service)
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700528
529 @classmethod
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700530 def clear_credentials(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000531 """Clears creds if set"""
Attila Fazekas5b0d9262015-05-20 10:17:39 +0200532 if hasattr(cls, '_creds_provider'):
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700533 cls._creds_provider.clear_creds()
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700534
535 @classmethod
nithya-ganesan222efd72015-01-22 12:20:27 +0000536 def set_validation_resources(cls, keypair=None, floating_ip=None,
537 security_group=None,
538 security_group_rules=None):
539 """Specify which ssh server validation resources should be created.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000540
nithya-ganesan222efd72015-01-22 12:20:27 +0000541 Each of the argument must be set to either None, True or False, with
542 None - use default from config (security groups and security group
543 rules get created when set to None)
544 False - Do not create the validation resource
545 True - create the validation resource
546
547 @param keypair
548 @param security_group
549 @param security_group_rules
550 @param floating_ip
551 """
Matthew Treinishe5cca002015-05-11 15:36:50 -0400552 if not CONF.validation.run_validation:
553 return
nithya-ganesan222efd72015-01-22 12:20:27 +0000554 if keypair is None:
555 if CONF.validation.auth_method.lower() == "keypair":
556 keypair = True
557 else:
558 keypair = False
559 if floating_ip is None:
560 if CONF.validation.connect_method.lower() == "floating":
561 floating_ip = True
562 else:
563 floating_ip = False
564 if security_group is None:
Brandon Palmc6cc91d2015-08-19 13:20:21 -0500565 security_group = CONF.validation.security_group
nithya-ganesan222efd72015-01-22 12:20:27 +0000566 if security_group_rules is None:
Brandon Palmc6cc91d2015-08-19 13:20:21 -0500567 security_group_rules = CONF.validation.security_group_rules
568
nithya-ganesan222efd72015-01-22 12:20:27 +0000569 if not cls.validation_resources:
570 cls.validation_resources = {
571 'keypair': keypair,
572 'security_group': security_group,
573 'security_group_rules': security_group_rules,
574 'floating_ip': floating_ip}
575
576 @classmethod
Andrea Frittoli7d5ed592015-02-10 01:10:23 +0000577 def set_network_resources(cls, network=False, router=False, subnet=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -0500578 dhcp=False):
579 """Specify which network resources should be created
580
581 @param network
582 @param router
583 @param subnet
584 @param dhcp
585 """
Salvatore Orlando5a337242014-01-15 22:49:22 +0000586 # network resources should be set only once from callers
587 # in order to ensure that even if it's called multiple times in
588 # a chain of overloaded methods, the attribute is set only
589 # in the leaf class
Andrea Frittoli7d5ed592015-02-10 01:10:23 +0000590 if not cls.network_resources:
591 cls.network_resources = {
Salvatore Orlando5a337242014-01-15 22:49:22 +0000592 'network': network,
593 'router': router,
594 'subnet': subnet,
595 'dhcp': dhcp}
Matthew Treinish9f756a02014-01-15 10:26:07 -0500596
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530597 @classmethod
598 def get_tenant_network(cls):
599 """Get the network to be used in testing
600
601 :return: network dict including 'id' and 'name'
602 """
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700603 # Make sure cred_provider exists and get a network client
John Warren9487a182015-09-14 18:12:56 -0400604 networks_client = cls.get_client_manager().compute_networks_client
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100605 cred_provider = cls._get_credentials_provider()
Andrea Frittoli700711e2015-04-02 11:39:38 +0100606 # In case of nova network, isolated tenants are not able to list the
Joshua Whitebd769602016-02-02 09:30:11 -0800607 # network configured in fixed_network_name, even if they can use it
Andrea Frittoli700711e2015-04-02 11:39:38 +0100608 # for their servers, so using an admin network client to validate
609 # the network name
610 if (not CONF.service_available.neutron and
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100611 credentials.is_admin_available(
612 identity_version=cls.get_identity_version())):
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100613 admin_creds = cred_provider.get_admin_creds()
Ghanshyam05049dd2016-02-12 17:44:48 +0900614 admin_manager = clients.Manager(admin_creds)
John Warren9487a182015-09-14 18:12:56 -0400615 networks_client = admin_manager.compute_networks_client
Andrea Frittoli (andreaf)940f8c62015-10-30 16:39:24 +0900616 return fixed_network.get_tenant_network(
617 cred_provider, networks_client, CONF.compute.fixed_network_name)
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530618
Mark Maglana5885eb32014-02-28 10:57:34 -0800619 def assertEmpty(self, list, msg=None):
620 self.assertTrue(len(list) == 0, msg)
621
622 def assertNotEmpty(self, list, msg=None):
623 self.assertTrue(len(list) > 0, msg)
624
Attila Fazekasdc216422013-01-29 15:12:14 +0100625
Marc Koderer24eb89c2014-01-31 11:23:33 +0100626class NegativeAutoTest(BaseTestCase):
627
628 _resources = {}
629
630 @classmethod
631 def setUpClass(cls):
632 super(NegativeAutoTest, cls).setUpClass()
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000633 os = cls.get_client_manager(credential_type='primary')
Marc Koderer24eb89c2014-01-31 11:23:33 +0100634 cls.client = os.negative_client
635
636 @staticmethod
Marc Koderer674c8fc2014-03-17 09:45:04 +0100637 def load_tests(*args):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000638 """Wrapper for testscenarios
639
640 To set the mandatory scenarios variable only in case a real test
641 loader is in place. Will be automatically called in case the variable
642 "load_tests" is set.
Marc Koderer674c8fc2014-03-17 09:45:04 +0100643 """
644 if getattr(args[0], 'suiteClass', None) is not None:
645 loader, standard_tests, pattern = args
646 else:
647 standard_tests, module, loader = args
648 for test in testtools.iterate_tests(standard_tests):
Marc Koderer4f44d722014-08-07 14:04:58 +0200649 schema = getattr(test, '_schema', None)
Marc Koderer3dd31052014-11-27 09:31:00 +0100650 if schema is not None:
Marc Koderer4f44d722014-08-07 14:04:58 +0200651 setattr(test, 'scenarios',
652 NegativeAutoTest.generate_scenario(schema))
Marc Koderer674c8fc2014-03-17 09:45:04 +0100653 return testscenarios.load_tests_apply_scenarios(*args)
654
655 @staticmethod
Marc Koderer4f44d722014-08-07 14:04:58 +0200656 def generate_scenario(description):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000657 """Generates the test scenario list for a given description.
Marc Koderer24eb89c2014-01-31 11:23:33 +0100658
Marc Koderer4f44d722014-08-07 14:04:58 +0200659 :param description: A file or dictionary with the following entries:
Marc Koderer24eb89c2014-01-31 11:23:33 +0100660 name (required) name for the api
661 http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
662 url (required) the url to be appended to the catalog url with '%s'
663 for each resource mentioned
664 resources: (optional) A list of resource names such as "server",
665 "flavor", etc. with an element for each '%s' in the url. This
666 method will call self.get_resource for each element when
667 constructing the positive test case template so negative
668 subclasses are expected to return valid resource ids when
669 appropriate.
670 json-schema (optional) A valid json schema that will be used to
671 create invalid data for the api calls. For "GET" and "HEAD",
672 the data is used to generate query strings appended to the url,
673 otherwise for the body of the http call.
674 """
Marc Koderer24eb89c2014-01-31 11:23:33 +0100675 LOG.debug(description)
Marc Koderer674c8fc2014-03-17 09:45:04 +0100676 generator = importutils.import_class(
677 CONF.negative.test_generator)()
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100678 generator.validate_schema(description)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100679 schema = description.get("json-schema", None)
680 resources = description.get("resources", [])
681 scenario_list = []
Marc Koderer424c84f2014-02-06 17:02:19 +0100682 expected_result = None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100683 for resource in resources:
Marc Koderer424c84f2014-02-06 17:02:19 +0100684 if isinstance(resource, dict):
685 expected_result = resource['expected_result']
686 resource = resource['name']
Marc Koderer24eb89c2014-01-31 11:23:33 +0100687 LOG.debug("Add resource to test %s" % resource)
688 scn_name = "inv_res_%s" % (resource)
689 scenario_list.append((scn_name, {"resource": (resource,
Marc Koderer424c84f2014-02-06 17:02:19 +0100690 str(uuid.uuid4())),
691 "expected_result": expected_result
Marc Koderer24eb89c2014-01-31 11:23:33 +0100692 }))
693 if schema is not None:
Marc Kodererf07f5d12014-09-01 09:47:23 +0200694 for scenario in generator.generate_scenarios(schema):
695 scenario_list.append((scenario['_negtest_name'],
696 scenario))
Marc Koderer24eb89c2014-01-31 11:23:33 +0100697 LOG.debug(scenario_list)
698 return scenario_list
699
Marc Koderer4f44d722014-08-07 14:04:58 +0200700 def execute(self, description):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000701 """Execute a http call
702
Marc Koderer24eb89c2014-01-31 11:23:33 +0100703 Execute a http call on an api that are expected to
704 result in client errors. First it uses invalid resources that are part
705 of the url, and then invalid data for queries and http request bodies.
706
Marc Koderer4f44d722014-08-07 14:04:58 +0200707 :param description: A json file or dictionary with the following
708 entries:
Marc Koderer24eb89c2014-01-31 11:23:33 +0100709 name (required) name for the api
710 http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
711 url (required) the url to be appended to the catalog url with '%s'
712 for each resource mentioned
713 resources: (optional) A list of resource names such as "server",
714 "flavor", etc. with an element for each '%s' in the url. This
715 method will call self.get_resource for each element when
716 constructing the positive test case template so negative
717 subclasses are expected to return valid resource ids when
718 appropriate.
719 json-schema (optional) A valid json schema that will be used to
720 create invalid data for the api calls. For "GET" and "HEAD",
721 the data is used to generate query strings appended to the url,
722 otherwise for the body of the http call.
723
724 """
Marc Koderer24eb89c2014-01-31 11:23:33 +0100725 LOG.info("Executing %s" % description["name"])
726 LOG.debug(description)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200727 generator = importutils.import_class(
728 CONF.negative.test_generator)()
729 schema = description.get("json-schema", None)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100730 method = description["http-method"]
731 url = description["url"]
Marc Kodererf07f5d12014-09-01 09:47:23 +0200732 expected_result = None
733 if "default_result_code" in description:
734 expected_result = description["default_result_code"]
Marc Koderer24eb89c2014-01-31 11:23:33 +0100735
736 resources = [self.get_resource(r) for
737 r in description.get("resources", [])]
738
739 if hasattr(self, "resource"):
740 # Note(mkoderer): The resources list already contains an invalid
741 # entry (see get_resource).
742 # We just send a valid json-schema with it
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100743 valid_schema = None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100744 if schema:
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100745 valid_schema = \
746 valid.ValidTestGenerator().generate_valid(schema)
747 new_url, body = self._http_arguments(valid_schema, url, method)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200748 elif hasattr(self, "_negtest_name"):
749 schema_under_test = \
750 valid.ValidTestGenerator().generate_valid(schema)
751 local_expected_result = \
752 generator.generate_payload(self, schema_under_test)
753 if local_expected_result is not None:
754 expected_result = local_expected_result
755 new_url, body = \
756 self._http_arguments(schema_under_test, url, method)
Marc Koderer1c247c82014-03-20 08:24:38 +0100757 else:
758 raise Exception("testscenarios are not active. Please make sure "
759 "that your test runner supports the load_tests "
760 "mechanism")
Marc Koderer424c84f2014-02-06 17:02:19 +0100761
Marc Kodererf857fda2014-03-05 15:58:00 +0100762 if "admin_client" in description and description["admin_client"]:
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100763 if not credentials.is_admin_available(
764 identity_version=self.get_identity_version()):
David Kranzafecec02015-03-23 14:27:15 -0400765 msg = ("Missing Identity Admin API credentials in"
766 "configuration.")
767 raise self.skipException(msg)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100768 creds = self.credentials_provider.get_admin_creds()
David Kranzafecec02015-03-23 14:27:15 -0400769 os_adm = clients.Manager(credentials=creds)
770 client = os_adm.negative_client
Marc Kodererf857fda2014-03-05 15:58:00 +0100771 else:
772 client = self.client
773 resp, resp_body = client.send_request(method, new_url,
774 resources, body=body)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200775 self._check_negative_response(expected_result, resp.status, resp_body)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100776
777 def _http_arguments(self, json_dict, url, method):
778 LOG.debug("dict: %s url: %s method: %s" % (json_dict, url, method))
779 if not json_dict:
780 return url, None
781 elif method in ["GET", "HEAD", "PUT", "DELETE"]:
Harshada Mangesh Kakad71f6b972015-12-22 09:46:48 -0800782 return "%s?%s" % (url, urllib.parse.urlencode(json_dict)), None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100783 else:
784 return url, json.dumps(json_dict)
785
Marc Kodererf07f5d12014-09-01 09:47:23 +0200786 def _check_negative_response(self, expected_result, result, body):
Marc Koderer24eb89c2014-01-31 11:23:33 +0100787 self.assertTrue(result >= 400 and result < 500 and result != 413,
788 "Expected client error, got %s:%s" %
789 (result, body))
790 self.assertTrue(expected_result is None or expected_result == result,
791 "Expected %s, got %s:%s" %
792 (expected_result, result, body))
793
794 @classmethod
795 def set_resource(cls, name, resource):
Joshua Whitebd769602016-02-02 09:30:11 -0800796 """Register a resource for a test
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000797
Joshua Whitebd769602016-02-02 09:30:11 -0800798 This function can be used in setUpClass context to register a resource
Marc Koderer24eb89c2014-01-31 11:23:33 +0100799 for a test.
800
801 :param name: The name of the kind of resource such as "flavor", "role",
802 etc.
803 :resource: The id of the resource
804 """
805 cls._resources[name] = resource
806
807 def get_resource(self, name):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000808 """Return a valid uuid for a type of resource.
809
810 If a real resource is needed as part of a url then this method should
811 return one. Otherwise it can return None.
Marc Koderer24eb89c2014-01-31 11:23:33 +0100812
813 :param name: The name of the kind of resource such as "flavor", "role",
814 etc.
815 """
Marc Koderer424c84f2014-02-06 17:02:19 +0100816 if isinstance(name, dict):
817 name = name['name']
Marc Koderer24eb89c2014-01-31 11:23:33 +0100818 if hasattr(self, "resource") and self.resource[0] == name:
819 LOG.debug("Return invalid resource (%s) value: %s" %
820 (self.resource[0], self.resource[1]))
821 return self.resource[1]
822 if name in self._resources:
823 return self._resources[name]
824 return None
825
826
Marc Kodererb2978da2014-03-26 13:45:43 +0100827def SimpleNegativeAutoTest(klass):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000828 """This decorator registers a test function on basis of the class name."""
Sean Dague5e1bcd92015-04-27 09:08:36 -0400829 @attr(type=['negative'])
Marc Kodererb2978da2014-03-26 13:45:43 +0100830 def generic_test(self):
Marc Koderer4f44d722014-08-07 14:04:58 +0200831 if hasattr(self, '_schema'):
832 self.execute(self._schema)
Marc Kodererb2978da2014-03-26 13:45:43 +0100833
834 cn = klass.__name__
835 cn = cn.replace('JSON', '')
836 cn = cn.replace('Test', '')
837 # NOTE(mkoderer): replaces uppercase chars inside the class name with '_'
838 lower_cn = re.sub('(?<!^)(?=[A-Z])', '_', cn).lower()
839 func_name = 'test_%s' % lower_cn
840 setattr(klass, func_name, generic_test)
841 return klass
842
843
Sean Dague35a7caf2013-05-10 10:38:22 -0400844def call_until_true(func, duration, sleep_for):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000845 """Call the given function until it returns True (and return True)
846
847 or until the specified duration (in seconds) elapses (and return False).
Sean Dague35a7caf2013-05-10 10:38:22 -0400848
849 :param func: A zero argument callable that returns True on success.
850 :param duration: The number of seconds for which to attempt a
851 successful call of the function.
852 :param sleep_for: The number of seconds to sleep after an unsuccessful
853 invocation of the function.
854 """
855 now = time.time()
856 timeout = now + duration
857 while now < timeout:
858 if func():
859 return True
Sean Dague35a7caf2013-05-10 10:38:22 -0400860 time.sleep(sleep_for)
861 now = time.time()
862 return False