blob: aefe1a9d34f1e785c6479f1dba2eeb81e7142830 [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
22
Matthew Treinish78561ad2013-07-26 11:41:56 -040023import fixtures
Doug Hellmann583ce2c2015-03-11 14:55:46 +000024from oslo_log import log as logging
Matthew Treinish21905512015-07-13 10:33:35 -040025from oslo_serialization import jsonutils as json
Doug Hellmann583ce2c2015-03-11 14:55:46 +000026from oslo_utils import importutils
Chris Hoge296558c2015-02-19 00:29:49 -060027import six
Harshada Mangesh Kakad71f6b972015-12-22 09:46:48 -080028from six.moves import urllib
Marc Koderer674c8fc2014-03-17 09:45:04 +010029import testscenarios
ivan-zhu1feeb382013-01-24 10:14:39 +080030import testtools
Jay Pipes051075a2012-04-28 17:39:37 -040031
Matthew Treinish3e046852013-07-23 16:00:24 -040032from tempest import clients
Jamie Lennox15350172015-08-17 10:54:25 +100033from tempest.common import cred_client
Andrea Frittoli (andreaf)290b3e12015-10-08 10:25:02 +010034from tempest.common import credentials_factory as credentials
Rohan Kanade9ce97df2013-12-10 18:59:35 +053035from tempest.common import fixed_network
Marc Koderer6ee82dc2014-02-17 10:26:29 +010036import tempest.common.generator.valid_generator as valid
nithya-ganesan222efd72015-01-22 12:20:27 +000037import tempest.common.validation_resources as vresources
Attila Fazekasdc216422013-01-29 15:12:14 +010038from tempest import config
Matthew Treinish16c43792013-09-09 19:55:23 +000039from tempest import exceptions
Ken'ichi Ohmichid079c892016-04-19 11:23:36 -070040from tempest.lib.common.utils import data_utils
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,
Matthew Treinishb66c94e2015-03-11 13:00:48 -040081 'data_processing': CONF.service_available.sahara,
82 'database': CONF.service_available.trove
Matthew Treinish8afbffd2014-01-21 23:56:13 +000083 }
Matthew Treinish3d8c7322014-08-03 23:53:28 -040084 return service_list
Matthew Treinish16c43792013-09-09 19:55:23 +000085
Matthew Treinish3d8c7322014-08-03 23:53:28 -040086
Yaroslav Lobankovda999f72015-06-30 20:32:55 +030087def services(*args):
Matthew Treinish3d8c7322014-08-03 23:53:28 -040088 """A decorator used to set an attr for each service used in a test case
89
90 This decorator applies a testtools attr for each service that gets
91 exercised by a test case.
92 """
Matthew Treinish16c43792013-09-09 19:55:23 +000093 def decorator(f):
Matthew Treinish3d8c7322014-08-03 23:53:28 -040094 services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
95 'network', 'identity', 'object_storage', 'dashboard',
ghanshyame4796f82016-04-13 15:49:22 +090096 'data_processing', 'database']
Matthew Treinish16c43792013-09-09 19:55:23 +000097 for service in args:
Matthew Treinish3d8c7322014-08-03 23:53:28 -040098 if service not in services:
99 raise exceptions.InvalidServiceTag('%s is not a valid '
100 'service' % service)
Matthew Treinish16c43792013-09-09 19:55:23 +0000101 attr(type=list(args))(f)
Matthew Treinish8afbffd2014-01-21 23:56:13 +0000102
103 @functools.wraps(f)
104 def wrapper(self, *func_args, **func_kwargs):
Matthew Treinish3d8c7322014-08-03 23:53:28 -0400105 service_list = get_service_list()
106
Matthew Treinish8afbffd2014-01-21 23:56:13 +0000107 for service in args:
108 if not service_list[service]:
109 msg = 'Skipped because the %s service is not available' % (
110 service)
111 raise testtools.TestCase.skipException(msg)
112 return f(self, *func_args, **func_kwargs)
113 return wrapper
Matthew Treinish16c43792013-09-09 19:55:23 +0000114 return decorator
115
116
Yaroslav Lobankovda999f72015-06-30 20:32:55 +0300117def stresstest(**kwargs):
Marc Koderer32221b8e2013-08-23 13:57:50 +0200118 """Add stress test decorator
119
120 For all functions with this decorator a attr stress will be
121 set automatically.
122
123 @param class_setup_per: allowed values are application, process, action
124 ``application``: once in the stress job lifetime
125 ``process``: once in the worker process lifetime
126 ``action``: on each action
Marc Kodererb0604412013-09-02 09:43:40 +0200127 @param allow_inheritance: allows inheritance of this attribute
Marc Koderer32221b8e2013-08-23 13:57:50 +0200128 """
129 def decorator(f):
130 if 'class_setup_per' in kwargs:
131 setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
132 else:
133 setattr(f, "st_class_setup_per", 'process')
Marc Kodererb0604412013-09-02 09:43:40 +0200134 if 'allow_inheritance' in kwargs:
135 setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
136 else:
137 setattr(f, "st_allow_inheritance", False)
Marc Koderer32221b8e2013-08-23 13:57:50 +0200138 attr(type='stress')(f)
139 return f
140 return decorator
141
142
Yaroslav Lobankovda999f72015-06-30 20:32:55 +0300143def requires_ext(**kwargs):
Matthew Treinishe3d26142013-11-26 19:14:58 +0000144 """A decorator to skip tests if an extension is not enabled
145
146 @param extension
147 @param service
148 """
149 def decorator(func):
150 @functools.wraps(func)
151 def wrapper(*func_args, **func_kwargs):
152 if not is_extension_enabled(kwargs['extension'],
153 kwargs['service']):
154 msg = "Skipped because %s extension: %s is not enabled" % (
155 kwargs['service'], kwargs['extension'])
156 raise testtools.TestCase.skipException(msg)
157 return func(*func_args, **func_kwargs)
158 return wrapper
159 return decorator
160
161
162def is_extension_enabled(extension_name, service):
163 """A function that will check the list of enabled extensions from config
164
165 """
Matthew Treinishe3d26142013-11-26 19:14:58 +0000166 config_dict = {
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000167 'compute': CONF.compute_feature_enabled.api_extensions,
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000168 'volume': CONF.volume_feature_enabled.api_extensions,
169 'network': CONF.network_feature_enabled.api_extensions,
170 'object': CONF.object_storage_feature_enabled.discoverable_apis,
Jane Zadorozhna121576d2015-06-23 12:57:13 +0300171 'identity': CONF.identity_feature_enabled.api_extensions
Matthew Treinishe3d26142013-11-26 19:14:58 +0000172 }
Simeon Monov5d7effe2014-07-16 07:32:38 +0300173 if len(config_dict[service]) == 0:
174 return False
Matthew Treinishe3d26142013-11-26 19:14:58 +0000175 if config_dict[service][0] == 'all':
176 return True
177 if extension_name in config_dict[service]:
178 return True
179 return False
180
Ian Wienand98c35f32013-07-23 20:34:23 +1000181
Yair Fried95914122016-03-03 09:14:40 +0200182def is_scheduler_filter_enabled(filter_name):
Yair Friedca5cfb52016-01-04 15:41:55 +0200183 """Check the list of enabled compute scheduler filters from config. """
184
185 filters = CONF.compute_feature_enabled.scheduler_available_filters
186 if len(filters) == 0:
187 return False
188 if 'all' in filters:
189 return True
190 if filter_name in filters:
191 return True
192 return False
193
194
Attila Fazekasf86fa312013-07-30 19:56:39 +0200195at_exit_set = set()
196
197
198def validate_tearDownClass():
199 if at_exit_set:
Sean Dagueeb1523b2014-03-10 10:17:44 -0400200 LOG.error(
201 "tearDownClass does not call the super's "
202 "tearDownClass in these classes: \n"
203 + str(at_exit_set))
204
Attila Fazekasf86fa312013-07-30 19:56:39 +0200205
206atexit.register(validate_tearDownClass)
207
Attila Fazekas53943322014-02-10 16:07:34 +0100208
Matthew Treinish2474f412014-11-17 18:11:56 -0500209class BaseTestCase(testtools.testcase.WithAttributes,
210 testtools.TestCase):
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100211 """The test base class defines Tempest framework for class level fixtures.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000212
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100213 `setUpClass` and `tearDownClass` are defined here and cannot be overwritten
214 by subclasses (enforced via hacking rule T105).
215
216 Set-up is split in a series of steps (setup stages), which can be
217 overwritten by test classes. Set-up stages are:
218 - skip_checks
219 - setup_credentials
220 - setup_clients
221 - resource_setup
222
223 Tear-down is also split in a series of steps (teardown stages), which are
224 stacked for execution only if the corresponding setup stage had been
225 reached during the setup phase. Tear-down stages are:
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700226 - clear_credentials (defined in the base test class)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100227 - resource_cleanup
228 """
Attila Fazekasc43fec82013-04-09 23:17:52 +0200229
Attila Fazekasf86fa312013-07-30 19:56:39 +0200230 setUpClassCalled = False
Marc Koderer24eb89c2014-01-31 11:23:33 +0100231 _service = None
Attila Fazekasf86fa312013-07-30 19:56:39 +0200232
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000233 # NOTE(andreaf) credentials holds a list of the credentials to be allocated
Andrea Frittoli (andreaf)825b2d32015-04-08 20:58:01 +0100234 # at class setup time. Credential types can be 'primary', 'alt', 'admin' or
235 # a list of roles - the first element of the list being a label, and the
236 # rest the actual roles
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000237 credentials = []
nithya-ganesan222efd72015-01-22 12:20:27 +0000238 # Resources required to validate a server using ssh
239 validation_resources = {}
Matthew Treinish9f756a02014-01-15 10:26:07 -0500240 network_resources = {}
241
Sean Dague2ef32ac2014-06-09 11:32:23 -0400242 # NOTE(sdague): log_format is defined inline here instead of using the oslo
243 # default because going through the config path recouples config to the
244 # stress tests too early, and depending on testr order will fail unit tests
245 log_format = ('%(asctime)s %(process)d %(levelname)-8s '
246 '[%(name)s] %(message)s')
247
Ryota MIBU60687e52015-12-09 18:37:39 +0900248 # Client manager class to use in this test case.
249 client_manager = clients.Manager
250
Sean Dague02620fd2016-03-02 15:52:51 -0500251 # A way to adjust slow test classes
252 TIMEOUT_SCALING_FACTOR = 1
253
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200254 @classmethod
255 def setUpClass(cls):
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100256 # It should never be overridden by descendants
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200257 if hasattr(super(BaseTestCase, cls), 'setUpClass'):
258 super(BaseTestCase, cls).setUpClass()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200259 cls.setUpClassCalled = True
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100260 # Stack of (name, callable) to be invoked in reverse order at teardown
261 cls.teardowns = []
262 # All the configuration checks that may generate a skip
263 cls.skip_checks()
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100264 try:
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100265 # Allocation of all required credentials and client managers
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700266 cls.teardowns.append(('credentials', cls.clear_credentials))
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100267 cls.setup_credentials()
268 # Shortcuts to clients
269 cls.setup_clients()
270 # Additional class-wide test resources
271 cls.teardowns.append(('resources', cls.resource_cleanup))
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100272 cls.resource_setup()
273 except Exception:
274 etype, value, trace = sys.exc_info()
Matthew Treinished2ad4f2014-12-23 15:18:32 -0500275 LOG.info("%s raised in %s.setUpClass. Invoking tearDownClass." % (
276 etype, cls.__name__))
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100277 cls.tearDownClass()
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100278 try:
Matthew Treinish843227d2015-04-23 10:17:17 -0400279 six.reraise(etype, value, trace)
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100280 finally:
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100281 del trace # to avoid circular refs
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200282
Attila Fazekasf86fa312013-07-30 19:56:39 +0200283 @classmethod
284 def tearDownClass(cls):
Attila Fazekas5d275302013-08-29 12:35:12 +0200285 at_exit_set.discard(cls)
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100286 # It should never be overridden by descendants
Attila Fazekasf86fa312013-07-30 19:56:39 +0200287 if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
288 super(BaseTestCase, cls).tearDownClass()
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100289 # Save any existing exception, we always want to re-raise the original
290 # exception only
291 etype, value, trace = sys.exc_info()
292 # If there was no exception during setup we shall re-raise the first
293 # exception in teardown
294 re_raise = (etype is None)
295 while cls.teardowns:
296 name, teardown = cls.teardowns.pop()
297 # Catch any exception in tearDown so we can re-raise the original
298 # exception at the end
299 try:
300 teardown()
301 except Exception as te:
302 sys_exec_info = sys.exc_info()
303 tetype = sys_exec_info[0]
304 # TODO(andreaf): Till we have the ability to cleanup only
305 # resources that were successfully setup in resource_cleanup,
306 # log AttributeError as info instead of exception.
307 if tetype is AttributeError and name == 'resources':
308 LOG.info("tearDownClass of %s failed: %s" % (name, te))
309 else:
310 LOG.exception("teardown of %s failed: %s" % (name, te))
311 if not etype:
312 etype, value, trace = sys_exec_info
Joshua Whitebd769602016-02-02 09:30:11 -0800313 # If exceptions were raised during teardown, and not before, re-raise
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100314 # the first one
315 if re_raise and etype is not None:
316 try:
Matthew Treinish843227d2015-04-23 10:17:17 -0400317 six.reraise(etype, value, trace)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100318 finally:
319 del trace # to avoid circular refs
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100320
321 @classmethod
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100322 def skip_checks(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000323 """Class level skip checks.
324
325 Subclasses verify in here all conditions that might prevent the
326 execution of the entire test class.
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100327 Checks implemented here may not make use API calls, and should rely on
328 configuration alone.
329 In general skip checks that require an API call are discouraged.
330 If one is really needed it may be implemented either in the
331 resource_setup or at test level.
332 """
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100333 identity_version = cls.get_identity_version()
334 if 'admin' in cls.credentials and not credentials.is_admin_available(
335 identity_version=identity_version):
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000336 msg = "Missing Identity Admin API credentials in configuration."
337 raise cls.skipException(msg)
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100338 if 'alt' in cls.credentials and not credentials.is_alt_available(
339 identity_version=identity_version):
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000340 msg = "Missing a 2nd set of API credentials in configuration."
341 raise cls.skipException(msg)
Andrea Frittoli (andreaf)41601412015-05-12 16:39:03 +0100342 if hasattr(cls, 'identity_version'):
343 if cls.identity_version == 'v2':
344 if not CONF.identity_feature_enabled.api_v2:
345 raise cls.skipException("Identity api v2 is not enabled")
346 elif cls.identity_version == 'v3':
347 if not CONF.identity_feature_enabled.api_v3:
348 raise cls.skipException("Identity api v3 is not enabled")
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100349
350 @classmethod
351 def setup_credentials(cls):
edannon6cc6fbc2016-05-03 11:56:12 +0300352 """Allocate credentials and create the client managers from them.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000353
edannon6cc6fbc2016-05-03 11:56:12 +0300354 For every element of credentials param function creates tenant/user,
355 Then it creates client manager for that credential.
356
357 Network related tests must override this function with
358 set_network_resources() method, otherwise it will create
359 network resources(network resources are created in a later step).
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000360 """
361 for credentials_type in cls.credentials:
362 # This may raise an exception in case credentials are not available
363 # In that case we want to let the exception through and the test
364 # fail accordingly
Andrea Frittoli (andreaf)825b2d32015-04-08 20:58:01 +0100365 if isinstance(credentials_type, six.string_types):
366 manager = cls.get_client_manager(
367 credential_type=credentials_type)
368 setattr(cls, 'os_%s' % credentials_type, manager)
369 # Setup some common aliases
370 # TODO(andreaf) The aliases below are a temporary hack
371 # to avoid changing too much code in one patch. They should
372 # be removed eventually
373 if credentials_type == 'primary':
374 cls.os = cls.manager = cls.os_primary
375 if credentials_type == 'admin':
376 cls.os_adm = cls.admin_manager = cls.os_admin
377 if credentials_type == 'alt':
378 cls.alt_manager = cls.os_alt
379 elif isinstance(credentials_type, list):
380 manager = cls.get_client_manager(roles=credentials_type[1:],
381 force_new=True)
382 setattr(cls, 'os_roles_%s' % credentials_type[0], manager)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100383
384 @classmethod
385 def setup_clients(cls):
386 """Create links to the clients into the test object."""
387 # TODO(andreaf) There is a fair amount of code that could me moved from
388 # base / test classes in here. Ideally tests should be able to only
389 # specify which client is `client` and nothing else.
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100390 pass
Attila Fazekasf86fa312013-07-30 19:56:39 +0200391
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000392 @classmethod
393 def resource_setup(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000394 """Class level resource setup for test cases."""
nithya-ganesan222efd72015-01-22 12:20:27 +0000395 if hasattr(cls, "os"):
396 cls.validation_resources = vresources.create_validation_resources(
397 cls.os, cls.validation_resources)
398 else:
zhangguoqing6c096642016-01-04 06:17:21 +0000399 LOG.warning("Client manager not found, validation resources not"
400 " created")
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000401
402 @classmethod
403 def resource_cleanup(cls):
404 """Class level resource cleanup for test cases.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000405
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000406 Resource cleanup must be able to handle the case of partially setup
407 resources, in case a failure during `resource_setup` should happen.
408 """
nithya-ganesan222efd72015-01-22 12:20:27 +0000409 if cls.validation_resources:
410 if hasattr(cls, "os"):
411 vresources.clear_validation_resources(cls.os,
412 cls.validation_resources)
413 cls.validation_resources = {}
414 else:
zhangguoqing6c096642016-01-04 06:17:21 +0000415 LOG.warning("Client manager not found, validation resources "
416 "not deleted")
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000417
Attila Fazekasf86fa312013-07-30 19:56:39 +0200418 def setUp(self):
419 super(BaseTestCase, self).setUp()
420 if not self.setUpClassCalled:
421 raise RuntimeError("setUpClass does not calls the super's"
422 "setUpClass in the "
423 + self.__class__.__name__)
424 at_exit_set.add(self.__class__)
Matthew Treinish78561ad2013-07-26 11:41:56 -0400425 test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
426 try:
Sean Dague02620fd2016-03-02 15:52:51 -0500427 test_timeout = int(test_timeout) * self.TIMEOUT_SCALING_FACTOR
Matthew Treinish78561ad2013-07-26 11:41:56 -0400428 except ValueError:
429 test_timeout = 0
430 if test_timeout > 0:
Attila Fazekasf86fa312013-07-30 19:56:39 +0200431 self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400432
433 if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
434 os.environ.get('OS_STDOUT_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200435 stdout = self.useFixture(fixtures.StringStream('stdout')).stream
436 self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400437 if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
438 os.environ.get('OS_STDERR_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200439 stderr = self.useFixture(fixtures.StringStream('stderr')).stream
440 self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
Attila Fazekas31388072013-08-15 08:58:07 +0200441 if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
442 os.environ.get('OS_LOG_CAPTURE') != '0'):
Attila Fazekas31388072013-08-15 08:58:07 +0200443 self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
Sean Dague2ef32ac2014-06-09 11:32:23 -0400444 format=self.log_format,
Attila Fazekas90445be2013-10-24 16:46:03 +0200445 level=None))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400446
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100447 @property
448 def credentials_provider(self):
449 return self._get_credentials_provider()
450
Jamie Lennox15350172015-08-17 10:54:25 +1000451 @property
452 def identity_utils(self):
453 """A client that abstracts v2 and v3 identity operations.
454
455 This can be used for creating and tearing down projects in tests. It
456 should not be used for testing identity features.
457 """
458 if CONF.identity.auth_version == 'v2':
459 client = self.os_admin.identity_client
Daniel Mellado7aea5342016-02-09 09:10:12 +0000460 users_client = self.os_admin.users_client
Daniel Melladob04da902015-11-20 17:43:12 +0100461 project_client = self.os_admin.tenants_client
Daniel Mellado6b16b922015-12-07 12:43:08 +0000462 roles_client = self.os_admin.roles_client
Daniel Mellado91a26b62016-02-11 11:13:04 +0000463 domains_client = None
Jamie Lennox15350172015-08-17 10:54:25 +1000464 else:
465 client = self.os_admin.identity_v3_client
Daniel Mellado7aea5342016-02-09 09:10:12 +0000466 users_client = self.os_admin.users_v3_client
Arx Cruz24bcb882016-02-10 15:20:16 +0100467 project_client = self.os_admin.projects_client
468 roles_client = self.os_admin.roles_v3_client
Daniel Mellado91a26b62016-02-11 11:13:04 +0000469 domains_client = self.os_admin.domains_client
Jamie Lennox15350172015-08-17 10:54:25 +1000470
471 try:
472 domain = client.auth_provider.credentials.project_domain_name
473 except AttributeError:
474 domain = 'Default'
475
Daniel Melladob04da902015-11-20 17:43:12 +0100476 return cred_client.get_creds_client(client, project_client,
Daniel Mellado82c83a52015-12-09 15:16:49 +0000477 users_client,
Daniel Mellado7aea5342016-02-09 09:10:12 +0000478 roles_client,
Daniel Mellado91a26b62016-02-11 11:13:04 +0000479 domains_client,
Daniel Melladob04da902015-11-20 17:43:12 +0100480 project_domain_name=domain)
Jamie Lennox15350172015-08-17 10:54:25 +1000481
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100482 @classmethod
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100483 def get_identity_version(cls):
484 """Returns the identity version used by the test class"""
485 identity_version = getattr(cls, 'identity_version', None)
486 return identity_version or CONF.identity.auth_version
487
488 @classmethod
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100489 def _get_credentials_provider(cls):
490 """Returns a credentials provider
491
492 If no credential provider exists yet creates one.
493 It uses self.identity_version if defined, or the configuration value
494 """
495 if (not hasattr(cls, '_creds_provider') or not cls._creds_provider or
496 not cls._creds_provider.name == cls.__name__):
497 force_tenant_isolation = getattr(cls, 'force_tenant_isolation',
498 False)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100499
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700500 cls._creds_provider = credentials.get_credentials_provider(
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100501 name=cls.__name__, network_resources=cls.network_resources,
502 force_tenant_isolation=force_tenant_isolation,
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100503 identity_version=cls.get_identity_version())
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100504 return cls._creds_provider
505
Matthew Treinish3e046852013-07-23 16:00:24 -0400506 @classmethod
Andrea Frittoli (andreaf)41601412015-05-12 16:39:03 +0100507 def get_client_manager(cls, credential_type=None, roles=None,
508 force_new=None):
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100509 """Returns an OpenStack client manager
510
511 Returns an OpenStack client manager based on either credential_type
512 or a list of roles. If neither is specified, it defaults to
513 credential_type 'primary'
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100514 :param credential_type: string - primary, alt or admin
515 :param roles: list of roles
516
lei zhangdd552b22015-11-25 20:41:48 +0800517 :returns: the created client manager
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100518 :raises skipException: if the requested credentials are not available
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700519 """
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100520 if all([roles, credential_type]):
521 msg = "Cannot get credentials by type and roles at the same time"
522 raise ValueError(msg)
523 if not any([roles, credential_type]):
524 credential_type = 'primary'
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100525 cred_provider = cls._get_credentials_provider()
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100526 if roles:
527 for role in roles:
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100528 if not cred_provider.is_role_available(role):
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100529 skip_msg = (
530 "%s skipped because the configured credential provider"
531 " is not able to provide credentials with the %s role "
532 "assigned." % (cls.__name__, role))
533 raise cls.skipException(skip_msg)
534 params = dict(roles=roles)
535 if force_new is not None:
536 params.update(force_new=force_new)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100537 creds = cred_provider.get_creds_by_roles(**params)
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000538 else:
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100539 credentials_method = 'get_%s_creds' % credential_type
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100540 if hasattr(cred_provider, credentials_method):
541 creds = getattr(cred_provider, credentials_method)()
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100542 else:
543 raise exceptions.InvalidCredentials(
544 "Invalid credentials type %s" % credential_type)
Ghanshyam05049dd2016-02-12 17:44:48 +0900545 return cls.client_manager(credentials=creds, service=cls._service)
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700546
547 @classmethod
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700548 def clear_credentials(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000549 """Clears creds if set"""
Attila Fazekas5b0d9262015-05-20 10:17:39 +0200550 if hasattr(cls, '_creds_provider'):
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700551 cls._creds_provider.clear_creds()
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700552
553 @classmethod
nithya-ganesan222efd72015-01-22 12:20:27 +0000554 def set_validation_resources(cls, keypair=None, floating_ip=None,
555 security_group=None,
556 security_group_rules=None):
557 """Specify which ssh server validation resources should be created.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000558
nithya-ganesan222efd72015-01-22 12:20:27 +0000559 Each of the argument must be set to either None, True or False, with
560 None - use default from config (security groups and security group
561 rules get created when set to None)
562 False - Do not create the validation resource
563 True - create the validation resource
564
565 @param keypair
566 @param security_group
567 @param security_group_rules
568 @param floating_ip
569 """
Matthew Treinishe5cca002015-05-11 15:36:50 -0400570 if not CONF.validation.run_validation:
571 return
nithya-ganesan222efd72015-01-22 12:20:27 +0000572 if keypair is None:
573 if CONF.validation.auth_method.lower() == "keypair":
574 keypair = True
575 else:
576 keypair = False
577 if floating_ip is None:
578 if CONF.validation.connect_method.lower() == "floating":
579 floating_ip = True
580 else:
581 floating_ip = False
582 if security_group is None:
Brandon Palmc6cc91d2015-08-19 13:20:21 -0500583 security_group = CONF.validation.security_group
nithya-ganesan222efd72015-01-22 12:20:27 +0000584 if security_group_rules is None:
Brandon Palmc6cc91d2015-08-19 13:20:21 -0500585 security_group_rules = CONF.validation.security_group_rules
586
nithya-ganesan222efd72015-01-22 12:20:27 +0000587 if not cls.validation_resources:
588 cls.validation_resources = {
589 'keypair': keypair,
590 'security_group': security_group,
591 'security_group_rules': security_group_rules,
592 'floating_ip': floating_ip}
593
594 @classmethod
Andrea Frittoli7d5ed592015-02-10 01:10:23 +0000595 def set_network_resources(cls, network=False, router=False, subnet=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -0500596 dhcp=False):
597 """Specify which network resources should be created
598
599 @param network
600 @param router
601 @param subnet
602 @param dhcp
603 """
Salvatore Orlando5a337242014-01-15 22:49:22 +0000604 # network resources should be set only once from callers
605 # in order to ensure that even if it's called multiple times in
606 # a chain of overloaded methods, the attribute is set only
607 # in the leaf class
Andrea Frittoli7d5ed592015-02-10 01:10:23 +0000608 if not cls.network_resources:
609 cls.network_resources = {
Salvatore Orlando5a337242014-01-15 22:49:22 +0000610 'network': network,
611 'router': router,
612 'subnet': subnet,
613 'dhcp': dhcp}
Matthew Treinish9f756a02014-01-15 10:26:07 -0500614
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530615 @classmethod
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000616 def get_tenant_network(cls, credentials_type='primary'):
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530617 """Get the network to be used in testing
618
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000619 :param credentials_type: The type of credentials for which to get the
620 tenant network
621
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530622 :return: network dict including 'id' and 'name'
623 """
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000624 # Get a manager for the given credentials_type, but at least
625 # always fall back on getting the manager for primary credentials
626 if isinstance(credentials_type, six.string_types):
627 manager = cls.get_client_manager(credential_type=credentials_type)
628 elif isinstance(credentials_type, list):
629 manager = cls.get_client_manager(roles=credentials_type[1:])
630 else:
631 manager = cls.get_client_manager()
632
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700633 # Make sure cred_provider exists and get a network client
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000634 networks_client = manager.compute_networks_client
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100635 cred_provider = cls._get_credentials_provider()
Andrea Frittoli700711e2015-04-02 11:39:38 +0100636 # In case of nova network, isolated tenants are not able to list the
Joshua Whitebd769602016-02-02 09:30:11 -0800637 # network configured in fixed_network_name, even if they can use it
Andrea Frittoli700711e2015-04-02 11:39:38 +0100638 # for their servers, so using an admin network client to validate
639 # the network name
640 if (not CONF.service_available.neutron and
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100641 credentials.is_admin_available(
642 identity_version=cls.get_identity_version())):
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100643 admin_creds = cred_provider.get_admin_creds()
Ghanshyam05049dd2016-02-12 17:44:48 +0900644 admin_manager = clients.Manager(admin_creds)
John Warren9487a182015-09-14 18:12:56 -0400645 networks_client = admin_manager.compute_networks_client
Andrea Frittoli (andreaf)940f8c62015-10-30 16:39:24 +0900646 return fixed_network.get_tenant_network(
647 cred_provider, networks_client, CONF.compute.fixed_network_name)
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530648
Mark Maglana5885eb32014-02-28 10:57:34 -0800649 def assertEmpty(self, list, msg=None):
650 self.assertTrue(len(list) == 0, msg)
651
652 def assertNotEmpty(self, list, msg=None):
653 self.assertTrue(len(list) > 0, msg)
654
Attila Fazekasdc216422013-01-29 15:12:14 +0100655
Marc Koderer24eb89c2014-01-31 11:23:33 +0100656class NegativeAutoTest(BaseTestCase):
657
658 _resources = {}
659
660 @classmethod
661 def setUpClass(cls):
662 super(NegativeAutoTest, cls).setUpClass()
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000663 os = cls.get_client_manager(credential_type='primary')
Marc Koderer24eb89c2014-01-31 11:23:33 +0100664 cls.client = os.negative_client
665
666 @staticmethod
Marc Koderer674c8fc2014-03-17 09:45:04 +0100667 def load_tests(*args):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000668 """Wrapper for testscenarios
669
670 To set the mandatory scenarios variable only in case a real test
671 loader is in place. Will be automatically called in case the variable
672 "load_tests" is set.
Marc Koderer674c8fc2014-03-17 09:45:04 +0100673 """
674 if getattr(args[0], 'suiteClass', None) is not None:
675 loader, standard_tests, pattern = args
676 else:
677 standard_tests, module, loader = args
678 for test in testtools.iterate_tests(standard_tests):
Marc Koderer4f44d722014-08-07 14:04:58 +0200679 schema = getattr(test, '_schema', None)
Marc Koderer3dd31052014-11-27 09:31:00 +0100680 if schema is not None:
Marc Koderer4f44d722014-08-07 14:04:58 +0200681 setattr(test, 'scenarios',
682 NegativeAutoTest.generate_scenario(schema))
Marc Koderer674c8fc2014-03-17 09:45:04 +0100683 return testscenarios.load_tests_apply_scenarios(*args)
684
685 @staticmethod
Marc Koderer4f44d722014-08-07 14:04:58 +0200686 def generate_scenario(description):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000687 """Generates the test scenario list for a given description.
Marc Koderer24eb89c2014-01-31 11:23:33 +0100688
Marc Koderer4f44d722014-08-07 14:04:58 +0200689 :param description: A file or dictionary with the following entries:
Marc Koderer24eb89c2014-01-31 11:23:33 +0100690 name (required) name for the api
691 http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
692 url (required) the url to be appended to the catalog url with '%s'
693 for each resource mentioned
694 resources: (optional) A list of resource names such as "server",
695 "flavor", etc. with an element for each '%s' in the url. This
696 method will call self.get_resource for each element when
697 constructing the positive test case template so negative
698 subclasses are expected to return valid resource ids when
699 appropriate.
700 json-schema (optional) A valid json schema that will be used to
701 create invalid data for the api calls. For "GET" and "HEAD",
702 the data is used to generate query strings appended to the url,
703 otherwise for the body of the http call.
704 """
Marc Koderer24eb89c2014-01-31 11:23:33 +0100705 LOG.debug(description)
Marc Koderer674c8fc2014-03-17 09:45:04 +0100706 generator = importutils.import_class(
707 CONF.negative.test_generator)()
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100708 generator.validate_schema(description)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100709 schema = description.get("json-schema", None)
710 resources = description.get("resources", [])
711 scenario_list = []
Marc Koderer424c84f2014-02-06 17:02:19 +0100712 expected_result = None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100713 for resource in resources:
Marc Koderer424c84f2014-02-06 17:02:19 +0100714 if isinstance(resource, dict):
715 expected_result = resource['expected_result']
716 resource = resource['name']
Marc Koderer24eb89c2014-01-31 11:23:33 +0100717 LOG.debug("Add resource to test %s" % resource)
718 scn_name = "inv_res_%s" % (resource)
Ken'ichi Ohmichid079c892016-04-19 11:23:36 -0700719 scenario_list.append((scn_name, {
720 "resource": (resource, data_utils.rand_uuid()),
721 "expected_result": expected_result
722 }))
Marc Koderer24eb89c2014-01-31 11:23:33 +0100723 if schema is not None:
Marc Kodererf07f5d12014-09-01 09:47:23 +0200724 for scenario in generator.generate_scenarios(schema):
725 scenario_list.append((scenario['_negtest_name'],
726 scenario))
Marc Koderer24eb89c2014-01-31 11:23:33 +0100727 LOG.debug(scenario_list)
728 return scenario_list
729
Marc Koderer4f44d722014-08-07 14:04:58 +0200730 def execute(self, description):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000731 """Execute a http call
732
Marc Koderer24eb89c2014-01-31 11:23:33 +0100733 Execute a http call on an api that are expected to
734 result in client errors. First it uses invalid resources that are part
735 of the url, and then invalid data for queries and http request bodies.
736
Marc Koderer4f44d722014-08-07 14:04:58 +0200737 :param description: A json file or dictionary with the following
738 entries:
Marc Koderer24eb89c2014-01-31 11:23:33 +0100739 name (required) name for the api
740 http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
741 url (required) the url to be appended to the catalog url with '%s'
742 for each resource mentioned
743 resources: (optional) A list of resource names such as "server",
744 "flavor", etc. with an element for each '%s' in the url. This
745 method will call self.get_resource for each element when
746 constructing the positive test case template so negative
747 subclasses are expected to return valid resource ids when
748 appropriate.
749 json-schema (optional) A valid json schema that will be used to
750 create invalid data for the api calls. For "GET" and "HEAD",
751 the data is used to generate query strings appended to the url,
752 otherwise for the body of the http call.
753
754 """
Marc Koderer24eb89c2014-01-31 11:23:33 +0100755 LOG.info("Executing %s" % description["name"])
756 LOG.debug(description)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200757 generator = importutils.import_class(
758 CONF.negative.test_generator)()
759 schema = description.get("json-schema", None)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100760 method = description["http-method"]
761 url = description["url"]
Marc Kodererf07f5d12014-09-01 09:47:23 +0200762 expected_result = None
763 if "default_result_code" in description:
764 expected_result = description["default_result_code"]
Marc Koderer24eb89c2014-01-31 11:23:33 +0100765
766 resources = [self.get_resource(r) for
767 r in description.get("resources", [])]
768
769 if hasattr(self, "resource"):
770 # Note(mkoderer): The resources list already contains an invalid
771 # entry (see get_resource).
772 # We just send a valid json-schema with it
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100773 valid_schema = None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100774 if schema:
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100775 valid_schema = \
776 valid.ValidTestGenerator().generate_valid(schema)
777 new_url, body = self._http_arguments(valid_schema, url, method)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200778 elif hasattr(self, "_negtest_name"):
779 schema_under_test = \
780 valid.ValidTestGenerator().generate_valid(schema)
781 local_expected_result = \
782 generator.generate_payload(self, schema_under_test)
783 if local_expected_result is not None:
784 expected_result = local_expected_result
785 new_url, body = \
786 self._http_arguments(schema_under_test, url, method)
Marc Koderer1c247c82014-03-20 08:24:38 +0100787 else:
788 raise Exception("testscenarios are not active. Please make sure "
789 "that your test runner supports the load_tests "
790 "mechanism")
Marc Koderer424c84f2014-02-06 17:02:19 +0100791
Marc Kodererf857fda2014-03-05 15:58:00 +0100792 if "admin_client" in description and description["admin_client"]:
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100793 if not credentials.is_admin_available(
794 identity_version=self.get_identity_version()):
David Kranzafecec02015-03-23 14:27:15 -0400795 msg = ("Missing Identity Admin API credentials in"
796 "configuration.")
797 raise self.skipException(msg)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100798 creds = self.credentials_provider.get_admin_creds()
David Kranzafecec02015-03-23 14:27:15 -0400799 os_adm = clients.Manager(credentials=creds)
800 client = os_adm.negative_client
Marc Kodererf857fda2014-03-05 15:58:00 +0100801 else:
802 client = self.client
803 resp, resp_body = client.send_request(method, new_url,
804 resources, body=body)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200805 self._check_negative_response(expected_result, resp.status, resp_body)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100806
807 def _http_arguments(self, json_dict, url, method):
808 LOG.debug("dict: %s url: %s method: %s" % (json_dict, url, method))
809 if not json_dict:
810 return url, None
811 elif method in ["GET", "HEAD", "PUT", "DELETE"]:
Harshada Mangesh Kakad71f6b972015-12-22 09:46:48 -0800812 return "%s?%s" % (url, urllib.parse.urlencode(json_dict)), None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100813 else:
814 return url, json.dumps(json_dict)
815
Marc Kodererf07f5d12014-09-01 09:47:23 +0200816 def _check_negative_response(self, expected_result, result, body):
Marc Koderer24eb89c2014-01-31 11:23:33 +0100817 self.assertTrue(result >= 400 and result < 500 and result != 413,
818 "Expected client error, got %s:%s" %
819 (result, body))
820 self.assertTrue(expected_result is None or expected_result == result,
821 "Expected %s, got %s:%s" %
822 (expected_result, result, body))
823
824 @classmethod
825 def set_resource(cls, name, resource):
Joshua Whitebd769602016-02-02 09:30:11 -0800826 """Register a resource for a test
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000827
Joshua Whitebd769602016-02-02 09:30:11 -0800828 This function can be used in setUpClass context to register a resource
Marc Koderer24eb89c2014-01-31 11:23:33 +0100829 for a test.
830
831 :param name: The name of the kind of resource such as "flavor", "role",
832 etc.
833 :resource: The id of the resource
834 """
835 cls._resources[name] = resource
836
837 def get_resource(self, name):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000838 """Return a valid uuid for a type of resource.
839
840 If a real resource is needed as part of a url then this method should
841 return one. Otherwise it can return None.
Marc Koderer24eb89c2014-01-31 11:23:33 +0100842
843 :param name: The name of the kind of resource such as "flavor", "role",
844 etc.
845 """
Marc Koderer424c84f2014-02-06 17:02:19 +0100846 if isinstance(name, dict):
847 name = name['name']
Marc Koderer24eb89c2014-01-31 11:23:33 +0100848 if hasattr(self, "resource") and self.resource[0] == name:
849 LOG.debug("Return invalid resource (%s) value: %s" %
850 (self.resource[0], self.resource[1]))
851 return self.resource[1]
852 if name in self._resources:
853 return self._resources[name]
854 return None
855
856
Marc Kodererb2978da2014-03-26 13:45:43 +0100857def SimpleNegativeAutoTest(klass):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000858 """This decorator registers a test function on basis of the class name."""
Sean Dague5e1bcd92015-04-27 09:08:36 -0400859 @attr(type=['negative'])
Marc Kodererb2978da2014-03-26 13:45:43 +0100860 def generic_test(self):
Marc Koderer4f44d722014-08-07 14:04:58 +0200861 if hasattr(self, '_schema'):
862 self.execute(self._schema)
Marc Kodererb2978da2014-03-26 13:45:43 +0100863
864 cn = klass.__name__
865 cn = cn.replace('JSON', '')
866 cn = cn.replace('Test', '')
867 # NOTE(mkoderer): replaces uppercase chars inside the class name with '_'
868 lower_cn = re.sub('(?<!^)(?=[A-Z])', '_', cn).lower()
869 func_name = 'test_%s' % lower_cn
870 setattr(klass, func_name, generic_test)
871 return klass
872
873
Sean Dague35a7caf2013-05-10 10:38:22 -0400874def call_until_true(func, duration, sleep_for):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000875 """Call the given function until it returns True (and return True)
876
877 or until the specified duration (in seconds) elapses (and return False).
Sean Dague35a7caf2013-05-10 10:38:22 -0400878
879 :param func: A zero argument callable that returns True on success.
880 :param duration: The number of seconds for which to attempt a
881 successful call of the function.
882 :param sleep_for: The number of seconds to sleep after an unsuccessful
883 invocation of the function.
884 """
885 now = time.time()
886 timeout = now + duration
887 while now < timeout:
888 if func():
889 return True
Sean Dague35a7caf2013-05-10 10:38:22 -0400890 time.sleep(sleep_for)
891 now = time.time()
892 return False