blob: d31c509c224a04b836fd048262f5719980a783c7 [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,
Matthew Treinishb66c94e2015-03-11 13:00:48 -040080 'data_processing': CONF.service_available.sahara,
81 'database': CONF.service_available.trove
Matthew Treinish8afbffd2014-01-21 23:56:13 +000082 }
Matthew Treinish3d8c7322014-08-03 23:53:28 -040083 return service_list
Matthew Treinish16c43792013-09-09 19:55:23 +000084
Matthew Treinish3d8c7322014-08-03 23:53:28 -040085
Yaroslav Lobankovda999f72015-06-30 20:32:55 +030086def services(*args):
Matthew Treinish3d8c7322014-08-03 23:53:28 -040087 """A decorator used to set an attr for each service used in a test case
88
89 This decorator applies a testtools attr for each service that gets
90 exercised by a test case.
91 """
Matthew Treinish16c43792013-09-09 19:55:23 +000092 def decorator(f):
Matthew Treinish3d8c7322014-08-03 23:53:28 -040093 services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
David Lyle672ebd32016-05-06 15:28:20 -060094 'network', 'identity', 'object_storage', 'data_processing',
95 'database']
Matthew Treinish16c43792013-09-09 19:55:23 +000096 for service in args:
Matthew Treinish3d8c7322014-08-03 23:53:28 -040097 if service not in services:
98 raise exceptions.InvalidServiceTag('%s is not a valid '
99 'service' % service)
Matthew Treinish16c43792013-09-09 19:55:23 +0000100 attr(type=list(args))(f)
Matthew Treinish8afbffd2014-01-21 23:56:13 +0000101
102 @functools.wraps(f)
103 def wrapper(self, *func_args, **func_kwargs):
Matthew Treinish3d8c7322014-08-03 23:53:28 -0400104 service_list = get_service_list()
105
Matthew Treinish8afbffd2014-01-21 23:56:13 +0000106 for service in args:
107 if not service_list[service]:
108 msg = 'Skipped because the %s service is not available' % (
109 service)
110 raise testtools.TestCase.skipException(msg)
111 return f(self, *func_args, **func_kwargs)
112 return wrapper
Matthew Treinish16c43792013-09-09 19:55:23 +0000113 return decorator
114
115
Yaroslav Lobankovda999f72015-06-30 20:32:55 +0300116def stresstest(**kwargs):
Marc Koderer32221b8e2013-08-23 13:57:50 +0200117 """Add stress test decorator
118
119 For all functions with this decorator a attr stress will be
120 set automatically.
121
122 @param class_setup_per: allowed values are application, process, action
123 ``application``: once in the stress job lifetime
124 ``process``: once in the worker process lifetime
125 ``action``: on each action
Marc Kodererb0604412013-09-02 09:43:40 +0200126 @param allow_inheritance: allows inheritance of this attribute
Marc Koderer32221b8e2013-08-23 13:57:50 +0200127 """
128 def decorator(f):
129 if 'class_setup_per' in kwargs:
130 setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
131 else:
132 setattr(f, "st_class_setup_per", 'process')
Marc Kodererb0604412013-09-02 09:43:40 +0200133 if 'allow_inheritance' in kwargs:
134 setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
135 else:
136 setattr(f, "st_allow_inheritance", False)
Marc Koderer32221b8e2013-08-23 13:57:50 +0200137 attr(type='stress')(f)
138 return f
139 return decorator
140
141
Yaroslav Lobankovda999f72015-06-30 20:32:55 +0300142def requires_ext(**kwargs):
Matthew Treinishe3d26142013-11-26 19:14:58 +0000143 """A decorator to skip tests if an extension is not enabled
144
145 @param extension
146 @param service
147 """
148 def decorator(func):
149 @functools.wraps(func)
150 def wrapper(*func_args, **func_kwargs):
151 if not is_extension_enabled(kwargs['extension'],
152 kwargs['service']):
153 msg = "Skipped because %s extension: %s is not enabled" % (
154 kwargs['service'], kwargs['extension'])
155 raise testtools.TestCase.skipException(msg)
156 return func(*func_args, **func_kwargs)
157 return wrapper
158 return decorator
159
160
161def is_extension_enabled(extension_name, service):
162 """A function that will check the list of enabled extensions from config
163
164 """
Matthew Treinishe3d26142013-11-26 19:14:58 +0000165 config_dict = {
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000166 'compute': CONF.compute_feature_enabled.api_extensions,
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000167 'volume': CONF.volume_feature_enabled.api_extensions,
168 'network': CONF.network_feature_enabled.api_extensions,
169 'object': CONF.object_storage_feature_enabled.discoverable_apis,
Jane Zadorozhna121576d2015-06-23 12:57:13 +0300170 'identity': CONF.identity_feature_enabled.api_extensions
Matthew Treinishe3d26142013-11-26 19:14:58 +0000171 }
Simeon Monov5d7effe2014-07-16 07:32:38 +0300172 if len(config_dict[service]) == 0:
173 return False
Matthew Treinishe3d26142013-11-26 19:14:58 +0000174 if config_dict[service][0] == 'all':
175 return True
176 if extension_name in config_dict[service]:
177 return True
178 return False
179
Ian Wienand98c35f32013-07-23 20:34:23 +1000180
Yair Fried95914122016-03-03 09:14:40 +0200181def is_scheduler_filter_enabled(filter_name):
Yair Friedca5cfb52016-01-04 15:41:55 +0200182 """Check the list of enabled compute scheduler filters from config. """
183
184 filters = CONF.compute_feature_enabled.scheduler_available_filters
185 if len(filters) == 0:
186 return False
187 if 'all' in filters:
188 return True
189 if filter_name in filters:
190 return True
191 return False
192
193
Attila Fazekasf86fa312013-07-30 19:56:39 +0200194at_exit_set = set()
195
196
197def validate_tearDownClass():
198 if at_exit_set:
Sean Dagueeb1523b2014-03-10 10:17:44 -0400199 LOG.error(
200 "tearDownClass does not call the super's "
201 "tearDownClass in these classes: \n"
202 + str(at_exit_set))
203
Attila Fazekasf86fa312013-07-30 19:56:39 +0200204
205atexit.register(validate_tearDownClass)
206
Attila Fazekas53943322014-02-10 16:07:34 +0100207
Matthew Treinish2474f412014-11-17 18:11:56 -0500208class BaseTestCase(testtools.testcase.WithAttributes,
209 testtools.TestCase):
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100210 """The test base class defines Tempest framework for class level fixtures.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000211
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100212 `setUpClass` and `tearDownClass` are defined here and cannot be overwritten
213 by subclasses (enforced via hacking rule T105).
214
215 Set-up is split in a series of steps (setup stages), which can be
216 overwritten by test classes. Set-up stages are:
217 - skip_checks
218 - setup_credentials
219 - setup_clients
220 - resource_setup
221
222 Tear-down is also split in a series of steps (teardown stages), which are
223 stacked for execution only if the corresponding setup stage had been
224 reached during the setup phase. Tear-down stages are:
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700225 - clear_credentials (defined in the base test class)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100226 - resource_cleanup
227 """
Attila Fazekasc43fec82013-04-09 23:17:52 +0200228
Attila Fazekasf86fa312013-07-30 19:56:39 +0200229 setUpClassCalled = False
Marc Koderer24eb89c2014-01-31 11:23:33 +0100230 _service = None
Attila Fazekasf86fa312013-07-30 19:56:39 +0200231
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000232 # NOTE(andreaf) credentials holds a list of the credentials to be allocated
Andrea Frittoli (andreaf)825b2d32015-04-08 20:58:01 +0100233 # at class setup time. Credential types can be 'primary', 'alt', 'admin' or
234 # a list of roles - the first element of the list being a label, and the
235 # rest the actual roles
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000236 credentials = []
nithya-ganesan222efd72015-01-22 12:20:27 +0000237 # Resources required to validate a server using ssh
238 validation_resources = {}
Matthew Treinish9f756a02014-01-15 10:26:07 -0500239 network_resources = {}
240
Sean Dague2ef32ac2014-06-09 11:32:23 -0400241 # NOTE(sdague): log_format is defined inline here instead of using the oslo
242 # default because going through the config path recouples config to the
243 # stress tests too early, and depending on testr order will fail unit tests
244 log_format = ('%(asctime)s %(process)d %(levelname)-8s '
245 '[%(name)s] %(message)s')
246
Ryota MIBU60687e52015-12-09 18:37:39 +0900247 # Client manager class to use in this test case.
248 client_manager = clients.Manager
249
Sean Dague02620fd2016-03-02 15:52:51 -0500250 # A way to adjust slow test classes
251 TIMEOUT_SCALING_FACTOR = 1
252
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200253 @classmethod
254 def setUpClass(cls):
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100255 # It should never be overridden by descendants
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200256 if hasattr(super(BaseTestCase, cls), 'setUpClass'):
257 super(BaseTestCase, cls).setUpClass()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200258 cls.setUpClassCalled = True
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100259 # Stack of (name, callable) to be invoked in reverse order at teardown
260 cls.teardowns = []
261 # All the configuration checks that may generate a skip
262 cls.skip_checks()
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100263 try:
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100264 # Allocation of all required credentials and client managers
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700265 cls.teardowns.append(('credentials', cls.clear_credentials))
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100266 cls.setup_credentials()
267 # Shortcuts to clients
268 cls.setup_clients()
269 # Additional class-wide test resources
270 cls.teardowns.append(('resources', cls.resource_cleanup))
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100271 cls.resource_setup()
272 except Exception:
273 etype, value, trace = sys.exc_info()
Matthew Treinished2ad4f2014-12-23 15:18:32 -0500274 LOG.info("%s raised in %s.setUpClass. Invoking tearDownClass." % (
275 etype, cls.__name__))
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100276 cls.tearDownClass()
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100277 try:
Matthew Treinish843227d2015-04-23 10:17:17 -0400278 six.reraise(etype, value, trace)
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100279 finally:
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100280 del trace # to avoid circular refs
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200281
Attila Fazekasf86fa312013-07-30 19:56:39 +0200282 @classmethod
283 def tearDownClass(cls):
Attila Fazekas5d275302013-08-29 12:35:12 +0200284 at_exit_set.discard(cls)
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100285 # It should never be overridden by descendants
Attila Fazekasf86fa312013-07-30 19:56:39 +0200286 if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
287 super(BaseTestCase, cls).tearDownClass()
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100288 # Save any existing exception, we always want to re-raise the original
289 # exception only
290 etype, value, trace = sys.exc_info()
291 # If there was no exception during setup we shall re-raise the first
292 # exception in teardown
293 re_raise = (etype is None)
294 while cls.teardowns:
295 name, teardown = cls.teardowns.pop()
296 # Catch any exception in tearDown so we can re-raise the original
297 # exception at the end
298 try:
299 teardown()
300 except Exception as te:
301 sys_exec_info = sys.exc_info()
302 tetype = sys_exec_info[0]
303 # TODO(andreaf): Till we have the ability to cleanup only
304 # resources that were successfully setup in resource_cleanup,
305 # log AttributeError as info instead of exception.
306 if tetype is AttributeError and name == 'resources':
307 LOG.info("tearDownClass of %s failed: %s" % (name, te))
308 else:
309 LOG.exception("teardown of %s failed: %s" % (name, te))
310 if not etype:
311 etype, value, trace = sys_exec_info
Joshua Whitebd769602016-02-02 09:30:11 -0800312 # If exceptions were raised during teardown, and not before, re-raise
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100313 # the first one
314 if re_raise and etype is not None:
315 try:
Matthew Treinish843227d2015-04-23 10:17:17 -0400316 six.reraise(etype, value, trace)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100317 finally:
318 del trace # to avoid circular refs
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100319
320 @classmethod
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100321 def skip_checks(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000322 """Class level skip checks.
323
324 Subclasses verify in here all conditions that might prevent the
325 execution of the entire test class.
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100326 Checks implemented here may not make use API calls, and should rely on
327 configuration alone.
328 In general skip checks that require an API call are discouraged.
329 If one is really needed it may be implemented either in the
330 resource_setup or at test level.
331 """
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100332 identity_version = cls.get_identity_version()
333 if 'admin' in cls.credentials and not credentials.is_admin_available(
334 identity_version=identity_version):
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000335 msg = "Missing Identity Admin API credentials in configuration."
336 raise cls.skipException(msg)
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100337 if 'alt' in cls.credentials and not credentials.is_alt_available(
338 identity_version=identity_version):
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000339 msg = "Missing a 2nd set of API credentials in configuration."
340 raise cls.skipException(msg)
Andrea Frittoli (andreaf)41601412015-05-12 16:39:03 +0100341 if hasattr(cls, 'identity_version'):
342 if cls.identity_version == 'v2':
343 if not CONF.identity_feature_enabled.api_v2:
344 raise cls.skipException("Identity api v2 is not enabled")
345 elif cls.identity_version == 'v3':
346 if not CONF.identity_feature_enabled.api_v3:
347 raise cls.skipException("Identity api v3 is not enabled")
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100348
349 @classmethod
350 def setup_credentials(cls):
edannon6cc6fbc2016-05-03 11:56:12 +0300351 """Allocate credentials and create the client managers from them.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000352
edannon6cc6fbc2016-05-03 11:56:12 +0300353 For every element of credentials param function creates tenant/user,
354 Then it creates client manager for that credential.
355
356 Network related tests must override this function with
357 set_network_resources() method, otherwise it will create
358 network resources(network resources are created in a later step).
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000359 """
360 for credentials_type in cls.credentials:
361 # This may raise an exception in case credentials are not available
362 # In that case we want to let the exception through and the test
363 # fail accordingly
Andrea Frittoli (andreaf)825b2d32015-04-08 20:58:01 +0100364 if isinstance(credentials_type, six.string_types):
365 manager = cls.get_client_manager(
366 credential_type=credentials_type)
367 setattr(cls, 'os_%s' % credentials_type, manager)
368 # Setup some common aliases
369 # TODO(andreaf) The aliases below are a temporary hack
370 # to avoid changing too much code in one patch. They should
371 # be removed eventually
372 if credentials_type == 'primary':
373 cls.os = cls.manager = cls.os_primary
374 if credentials_type == 'admin':
375 cls.os_adm = cls.admin_manager = cls.os_admin
376 if credentials_type == 'alt':
377 cls.alt_manager = cls.os_alt
378 elif isinstance(credentials_type, list):
379 manager = cls.get_client_manager(roles=credentials_type[1:],
380 force_new=True)
381 setattr(cls, 'os_roles_%s' % credentials_type[0], manager)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100382
383 @classmethod
384 def setup_clients(cls):
385 """Create links to the clients into the test object."""
386 # TODO(andreaf) There is a fair amount of code that could me moved from
387 # base / test classes in here. Ideally tests should be able to only
388 # specify which client is `client` and nothing else.
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100389 pass
Attila Fazekasf86fa312013-07-30 19:56:39 +0200390
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000391 @classmethod
392 def resource_setup(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000393 """Class level resource setup for test cases."""
nithya-ganesan222efd72015-01-22 12:20:27 +0000394 if hasattr(cls, "os"):
395 cls.validation_resources = vresources.create_validation_resources(
396 cls.os, cls.validation_resources)
397 else:
zhangguoqing6c096642016-01-04 06:17:21 +0000398 LOG.warning("Client manager not found, validation resources not"
399 " created")
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000400
401 @classmethod
402 def resource_cleanup(cls):
403 """Class level resource cleanup for test cases.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000404
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000405 Resource cleanup must be able to handle the case of partially setup
406 resources, in case a failure during `resource_setup` should happen.
407 """
nithya-ganesan222efd72015-01-22 12:20:27 +0000408 if cls.validation_resources:
409 if hasattr(cls, "os"):
410 vresources.clear_validation_resources(cls.os,
411 cls.validation_resources)
412 cls.validation_resources = {}
413 else:
zhangguoqing6c096642016-01-04 06:17:21 +0000414 LOG.warning("Client manager not found, validation resources "
415 "not deleted")
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000416
Attila Fazekasf86fa312013-07-30 19:56:39 +0200417 def setUp(self):
418 super(BaseTestCase, self).setUp()
419 if not self.setUpClassCalled:
420 raise RuntimeError("setUpClass does not calls the super's"
421 "setUpClass in the "
422 + self.__class__.__name__)
423 at_exit_set.add(self.__class__)
Matthew Treinish78561ad2013-07-26 11:41:56 -0400424 test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
425 try:
Sean Dague02620fd2016-03-02 15:52:51 -0500426 test_timeout = int(test_timeout) * self.TIMEOUT_SCALING_FACTOR
Matthew Treinish78561ad2013-07-26 11:41:56 -0400427 except ValueError:
428 test_timeout = 0
429 if test_timeout > 0:
Attila Fazekasf86fa312013-07-30 19:56:39 +0200430 self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400431
432 if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
433 os.environ.get('OS_STDOUT_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200434 stdout = self.useFixture(fixtures.StringStream('stdout')).stream
435 self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400436 if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
437 os.environ.get('OS_STDERR_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200438 stderr = self.useFixture(fixtures.StringStream('stderr')).stream
439 self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
Attila Fazekas31388072013-08-15 08:58:07 +0200440 if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
441 os.environ.get('OS_LOG_CAPTURE') != '0'):
Attila Fazekas31388072013-08-15 08:58:07 +0200442 self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
Sean Dague2ef32ac2014-06-09 11:32:23 -0400443 format=self.log_format,
Attila Fazekas90445be2013-10-24 16:46:03 +0200444 level=None))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400445
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100446 @property
447 def credentials_provider(self):
448 return self._get_credentials_provider()
449
Jamie Lennox15350172015-08-17 10:54:25 +1000450 @property
451 def identity_utils(self):
452 """A client that abstracts v2 and v3 identity operations.
453
454 This can be used for creating and tearing down projects in tests. It
455 should not be used for testing identity features.
456 """
457 if CONF.identity.auth_version == 'v2':
458 client = self.os_admin.identity_client
Daniel Mellado7aea5342016-02-09 09:10:12 +0000459 users_client = self.os_admin.users_client
Daniel Melladob04da902015-11-20 17:43:12 +0100460 project_client = self.os_admin.tenants_client
Daniel Mellado6b16b922015-12-07 12:43:08 +0000461 roles_client = self.os_admin.roles_client
Daniel Mellado91a26b62016-02-11 11:13:04 +0000462 domains_client = None
Jamie Lennox15350172015-08-17 10:54:25 +1000463 else:
464 client = self.os_admin.identity_v3_client
Daniel Mellado7aea5342016-02-09 09:10:12 +0000465 users_client = self.os_admin.users_v3_client
Arx Cruz24bcb882016-02-10 15:20:16 +0100466 project_client = self.os_admin.projects_client
467 roles_client = self.os_admin.roles_v3_client
Daniel Mellado91a26b62016-02-11 11:13:04 +0000468 domains_client = self.os_admin.domains_client
Jamie Lennox15350172015-08-17 10:54:25 +1000469
470 try:
471 domain = client.auth_provider.credentials.project_domain_name
472 except AttributeError:
473 domain = 'Default'
474
Daniel Melladob04da902015-11-20 17:43:12 +0100475 return cred_client.get_creds_client(client, project_client,
Daniel Mellado82c83a52015-12-09 15:16:49 +0000476 users_client,
Daniel Mellado7aea5342016-02-09 09:10:12 +0000477 roles_client,
Daniel Mellado91a26b62016-02-11 11:13:04 +0000478 domains_client,
Daniel Melladob04da902015-11-20 17:43:12 +0100479 project_domain_name=domain)
Jamie Lennox15350172015-08-17 10:54:25 +1000480
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100481 @classmethod
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100482 def get_identity_version(cls):
483 """Returns the identity version used by the test class"""
484 identity_version = getattr(cls, 'identity_version', None)
485 return identity_version or CONF.identity.auth_version
486
487 @classmethod
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100488 def _get_credentials_provider(cls):
489 """Returns a credentials provider
490
491 If no credential provider exists yet creates one.
492 It uses self.identity_version if defined, or the configuration value
493 """
494 if (not hasattr(cls, '_creds_provider') or not cls._creds_provider or
495 not cls._creds_provider.name == cls.__name__):
496 force_tenant_isolation = getattr(cls, 'force_tenant_isolation',
497 False)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100498
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700499 cls._creds_provider = credentials.get_credentials_provider(
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100500 name=cls.__name__, network_resources=cls.network_resources,
501 force_tenant_isolation=force_tenant_isolation,
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100502 identity_version=cls.get_identity_version())
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100503 return cls._creds_provider
504
Matthew Treinish3e046852013-07-23 16:00:24 -0400505 @classmethod
Andrea Frittoli (andreaf)41601412015-05-12 16:39:03 +0100506 def get_client_manager(cls, credential_type=None, roles=None,
507 force_new=None):
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100508 """Returns an OpenStack client manager
509
510 Returns an OpenStack client manager based on either credential_type
511 or a list of roles. If neither is specified, it defaults to
512 credential_type 'primary'
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100513 :param credential_type: string - primary, alt or admin
514 :param roles: list of roles
515
lei zhangdd552b22015-11-25 20:41:48 +0800516 :returns: the created client manager
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100517 :raises skipException: if the requested credentials are not available
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700518 """
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100519 if all([roles, credential_type]):
520 msg = "Cannot get credentials by type and roles at the same time"
521 raise ValueError(msg)
522 if not any([roles, credential_type]):
523 credential_type = 'primary'
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100524 cred_provider = cls._get_credentials_provider()
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100525 if roles:
526 for role in roles:
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100527 if not cred_provider.is_role_available(role):
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100528 skip_msg = (
529 "%s skipped because the configured credential provider"
530 " is not able to provide credentials with the %s role "
531 "assigned." % (cls.__name__, role))
532 raise cls.skipException(skip_msg)
533 params = dict(roles=roles)
534 if force_new is not None:
535 params.update(force_new=force_new)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100536 creds = cred_provider.get_creds_by_roles(**params)
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000537 else:
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100538 credentials_method = 'get_%s_creds' % credential_type
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100539 if hasattr(cred_provider, credentials_method):
540 creds = getattr(cred_provider, credentials_method)()
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100541 else:
542 raise exceptions.InvalidCredentials(
543 "Invalid credentials type %s" % credential_type)
Ghanshyam05049dd2016-02-12 17:44:48 +0900544 return cls.client_manager(credentials=creds, service=cls._service)
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700545
546 @classmethod
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700547 def clear_credentials(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000548 """Clears creds if set"""
Attila Fazekas5b0d9262015-05-20 10:17:39 +0200549 if hasattr(cls, '_creds_provider'):
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700550 cls._creds_provider.clear_creds()
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700551
552 @classmethod
nithya-ganesan222efd72015-01-22 12:20:27 +0000553 def set_validation_resources(cls, keypair=None, floating_ip=None,
554 security_group=None,
555 security_group_rules=None):
556 """Specify which ssh server validation resources should be created.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000557
nithya-ganesan222efd72015-01-22 12:20:27 +0000558 Each of the argument must be set to either None, True or False, with
559 None - use default from config (security groups and security group
560 rules get created when set to None)
561 False - Do not create the validation resource
562 True - create the validation resource
563
564 @param keypair
565 @param security_group
566 @param security_group_rules
567 @param floating_ip
568 """
Matthew Treinishe5cca002015-05-11 15:36:50 -0400569 if not CONF.validation.run_validation:
570 return
nithya-ganesan222efd72015-01-22 12:20:27 +0000571 if keypair is None:
572 if CONF.validation.auth_method.lower() == "keypair":
573 keypair = True
574 else:
575 keypair = False
576 if floating_ip is None:
577 if CONF.validation.connect_method.lower() == "floating":
578 floating_ip = True
579 else:
580 floating_ip = False
581 if security_group is None:
Brandon Palmc6cc91d2015-08-19 13:20:21 -0500582 security_group = CONF.validation.security_group
nithya-ganesan222efd72015-01-22 12:20:27 +0000583 if security_group_rules is None:
Brandon Palmc6cc91d2015-08-19 13:20:21 -0500584 security_group_rules = CONF.validation.security_group_rules
585
nithya-ganesan222efd72015-01-22 12:20:27 +0000586 if not cls.validation_resources:
587 cls.validation_resources = {
588 'keypair': keypair,
589 'security_group': security_group,
590 'security_group_rules': security_group_rules,
591 'floating_ip': floating_ip}
592
593 @classmethod
Andrea Frittoli7d5ed592015-02-10 01:10:23 +0000594 def set_network_resources(cls, network=False, router=False, subnet=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -0500595 dhcp=False):
596 """Specify which network resources should be created
597
598 @param network
599 @param router
600 @param subnet
601 @param dhcp
602 """
Salvatore Orlando5a337242014-01-15 22:49:22 +0000603 # network resources should be set only once from callers
604 # in order to ensure that even if it's called multiple times in
605 # a chain of overloaded methods, the attribute is set only
606 # in the leaf class
Andrea Frittoli7d5ed592015-02-10 01:10:23 +0000607 if not cls.network_resources:
608 cls.network_resources = {
Salvatore Orlando5a337242014-01-15 22:49:22 +0000609 'network': network,
610 'router': router,
611 'subnet': subnet,
612 'dhcp': dhcp}
Matthew Treinish9f756a02014-01-15 10:26:07 -0500613
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530614 @classmethod
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000615 def get_tenant_network(cls, credentials_type='primary'):
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530616 """Get the network to be used in testing
617
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000618 :param credentials_type: The type of credentials for which to get the
619 tenant network
620
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530621 :return: network dict including 'id' and 'name'
622 """
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000623 # Get a manager for the given credentials_type, but at least
624 # always fall back on getting the manager for primary credentials
625 if isinstance(credentials_type, six.string_types):
626 manager = cls.get_client_manager(credential_type=credentials_type)
627 elif isinstance(credentials_type, list):
628 manager = cls.get_client_manager(roles=credentials_type[1:])
629 else:
630 manager = cls.get_client_manager()
631
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700632 # Make sure cred_provider exists and get a network client
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000633 networks_client = manager.compute_networks_client
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100634 cred_provider = cls._get_credentials_provider()
Andrea Frittoli700711e2015-04-02 11:39:38 +0100635 # In case of nova network, isolated tenants are not able to list the
Joshua Whitebd769602016-02-02 09:30:11 -0800636 # network configured in fixed_network_name, even if they can use it
Andrea Frittoli700711e2015-04-02 11:39:38 +0100637 # for their servers, so using an admin network client to validate
638 # the network name
639 if (not CONF.service_available.neutron and
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100640 credentials.is_admin_available(
641 identity_version=cls.get_identity_version())):
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100642 admin_creds = cred_provider.get_admin_creds()
Ghanshyam05049dd2016-02-12 17:44:48 +0900643 admin_manager = clients.Manager(admin_creds)
John Warren9487a182015-09-14 18:12:56 -0400644 networks_client = admin_manager.compute_networks_client
Andrea Frittoli (andreaf)940f8c62015-10-30 16:39:24 +0900645 return fixed_network.get_tenant_network(
646 cred_provider, networks_client, CONF.compute.fixed_network_name)
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530647
Mark Maglana5885eb32014-02-28 10:57:34 -0800648 def assertEmpty(self, list, msg=None):
649 self.assertTrue(len(list) == 0, msg)
650
651 def assertNotEmpty(self, list, msg=None):
652 self.assertTrue(len(list) > 0, msg)
653
Attila Fazekasdc216422013-01-29 15:12:14 +0100654
Marc Koderer24eb89c2014-01-31 11:23:33 +0100655class NegativeAutoTest(BaseTestCase):
656
657 _resources = {}
658
659 @classmethod
660 def setUpClass(cls):
661 super(NegativeAutoTest, cls).setUpClass()
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000662 os = cls.get_client_manager(credential_type='primary')
Marc Koderer24eb89c2014-01-31 11:23:33 +0100663 cls.client = os.negative_client
664
665 @staticmethod
Marc Koderer674c8fc2014-03-17 09:45:04 +0100666 def load_tests(*args):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000667 """Wrapper for testscenarios
668
669 To set the mandatory scenarios variable only in case a real test
670 loader is in place. Will be automatically called in case the variable
671 "load_tests" is set.
Marc Koderer674c8fc2014-03-17 09:45:04 +0100672 """
673 if getattr(args[0], 'suiteClass', None) is not None:
674 loader, standard_tests, pattern = args
675 else:
676 standard_tests, module, loader = args
677 for test in testtools.iterate_tests(standard_tests):
Marc Koderer4f44d722014-08-07 14:04:58 +0200678 schema = getattr(test, '_schema', None)
Marc Koderer3dd31052014-11-27 09:31:00 +0100679 if schema is not None:
Marc Koderer4f44d722014-08-07 14:04:58 +0200680 setattr(test, 'scenarios',
681 NegativeAutoTest.generate_scenario(schema))
Marc Koderer674c8fc2014-03-17 09:45:04 +0100682 return testscenarios.load_tests_apply_scenarios(*args)
683
684 @staticmethod
Marc Koderer4f44d722014-08-07 14:04:58 +0200685 def generate_scenario(description):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000686 """Generates the test scenario list for a given description.
Marc Koderer24eb89c2014-01-31 11:23:33 +0100687
Marc Koderer4f44d722014-08-07 14:04:58 +0200688 :param description: A file or dictionary with the following entries:
Marc Koderer24eb89c2014-01-31 11:23:33 +0100689 name (required) name for the api
690 http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
691 url (required) the url to be appended to the catalog url with '%s'
692 for each resource mentioned
693 resources: (optional) A list of resource names such as "server",
694 "flavor", etc. with an element for each '%s' in the url. This
695 method will call self.get_resource for each element when
696 constructing the positive test case template so negative
697 subclasses are expected to return valid resource ids when
698 appropriate.
699 json-schema (optional) A valid json schema that will be used to
700 create invalid data for the api calls. For "GET" and "HEAD",
701 the data is used to generate query strings appended to the url,
702 otherwise for the body of the http call.
703 """
Marc Koderer24eb89c2014-01-31 11:23:33 +0100704 LOG.debug(description)
Marc Koderer674c8fc2014-03-17 09:45:04 +0100705 generator = importutils.import_class(
706 CONF.negative.test_generator)()
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100707 generator.validate_schema(description)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100708 schema = description.get("json-schema", None)
709 resources = description.get("resources", [])
710 scenario_list = []
Marc Koderer424c84f2014-02-06 17:02:19 +0100711 expected_result = None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100712 for resource in resources:
Marc Koderer424c84f2014-02-06 17:02:19 +0100713 if isinstance(resource, dict):
714 expected_result = resource['expected_result']
715 resource = resource['name']
Marc Koderer24eb89c2014-01-31 11:23:33 +0100716 LOG.debug("Add resource to test %s" % resource)
717 scn_name = "inv_res_%s" % (resource)
Ken'ichi Ohmichid079c892016-04-19 11:23:36 -0700718 scenario_list.append((scn_name, {
719 "resource": (resource, data_utils.rand_uuid()),
720 "expected_result": expected_result
721 }))
Marc Koderer24eb89c2014-01-31 11:23:33 +0100722 if schema is not None:
Marc Kodererf07f5d12014-09-01 09:47:23 +0200723 for scenario in generator.generate_scenarios(schema):
724 scenario_list.append((scenario['_negtest_name'],
725 scenario))
Marc Koderer24eb89c2014-01-31 11:23:33 +0100726 LOG.debug(scenario_list)
727 return scenario_list
728
Marc Koderer4f44d722014-08-07 14:04:58 +0200729 def execute(self, description):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000730 """Execute a http call
731
Marc Koderer24eb89c2014-01-31 11:23:33 +0100732 Execute a http call on an api that are expected to
733 result in client errors. First it uses invalid resources that are part
734 of the url, and then invalid data for queries and http request bodies.
735
Marc Koderer4f44d722014-08-07 14:04:58 +0200736 :param description: A json file or dictionary with the following
737 entries:
Marc Koderer24eb89c2014-01-31 11:23:33 +0100738 name (required) name for the api
739 http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
740 url (required) the url to be appended to the catalog url with '%s'
741 for each resource mentioned
742 resources: (optional) A list of resource names such as "server",
743 "flavor", etc. with an element for each '%s' in the url. This
744 method will call self.get_resource for each element when
745 constructing the positive test case template so negative
746 subclasses are expected to return valid resource ids when
747 appropriate.
748 json-schema (optional) A valid json schema that will be used to
749 create invalid data for the api calls. For "GET" and "HEAD",
750 the data is used to generate query strings appended to the url,
751 otherwise for the body of the http call.
752
753 """
Marc Koderer24eb89c2014-01-31 11:23:33 +0100754 LOG.info("Executing %s" % description["name"])
755 LOG.debug(description)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200756 generator = importutils.import_class(
757 CONF.negative.test_generator)()
758 schema = description.get("json-schema", None)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100759 method = description["http-method"]
760 url = description["url"]
Marc Kodererf07f5d12014-09-01 09:47:23 +0200761 expected_result = None
762 if "default_result_code" in description:
763 expected_result = description["default_result_code"]
Marc Koderer24eb89c2014-01-31 11:23:33 +0100764
765 resources = [self.get_resource(r) for
766 r in description.get("resources", [])]
767
768 if hasattr(self, "resource"):
769 # Note(mkoderer): The resources list already contains an invalid
770 # entry (see get_resource).
771 # We just send a valid json-schema with it
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100772 valid_schema = None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100773 if schema:
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100774 valid_schema = \
775 valid.ValidTestGenerator().generate_valid(schema)
776 new_url, body = self._http_arguments(valid_schema, url, method)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200777 elif hasattr(self, "_negtest_name"):
778 schema_under_test = \
779 valid.ValidTestGenerator().generate_valid(schema)
780 local_expected_result = \
781 generator.generate_payload(self, schema_under_test)
782 if local_expected_result is not None:
783 expected_result = local_expected_result
784 new_url, body = \
785 self._http_arguments(schema_under_test, url, method)
Marc Koderer1c247c82014-03-20 08:24:38 +0100786 else:
787 raise Exception("testscenarios are not active. Please make sure "
788 "that your test runner supports the load_tests "
789 "mechanism")
Marc Koderer424c84f2014-02-06 17:02:19 +0100790
Marc Kodererf857fda2014-03-05 15:58:00 +0100791 if "admin_client" in description and description["admin_client"]:
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100792 if not credentials.is_admin_available(
793 identity_version=self.get_identity_version()):
David Kranzafecec02015-03-23 14:27:15 -0400794 msg = ("Missing Identity Admin API credentials in"
795 "configuration.")
796 raise self.skipException(msg)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100797 creds = self.credentials_provider.get_admin_creds()
David Kranzafecec02015-03-23 14:27:15 -0400798 os_adm = clients.Manager(credentials=creds)
799 client = os_adm.negative_client
Marc Kodererf857fda2014-03-05 15:58:00 +0100800 else:
801 client = self.client
802 resp, resp_body = client.send_request(method, new_url,
803 resources, body=body)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200804 self._check_negative_response(expected_result, resp.status, resp_body)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100805
806 def _http_arguments(self, json_dict, url, method):
807 LOG.debug("dict: %s url: %s method: %s" % (json_dict, url, method))
808 if not json_dict:
809 return url, None
810 elif method in ["GET", "HEAD", "PUT", "DELETE"]:
Harshada Mangesh Kakad71f6b972015-12-22 09:46:48 -0800811 return "%s?%s" % (url, urllib.parse.urlencode(json_dict)), None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100812 else:
813 return url, json.dumps(json_dict)
814
Marc Kodererf07f5d12014-09-01 09:47:23 +0200815 def _check_negative_response(self, expected_result, result, body):
Marc Koderer24eb89c2014-01-31 11:23:33 +0100816 self.assertTrue(result >= 400 and result < 500 and result != 413,
817 "Expected client error, got %s:%s" %
818 (result, body))
819 self.assertTrue(expected_result is None or expected_result == result,
820 "Expected %s, got %s:%s" %
821 (expected_result, result, body))
822
823 @classmethod
824 def set_resource(cls, name, resource):
Joshua Whitebd769602016-02-02 09:30:11 -0800825 """Register a resource for a test
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000826
Joshua Whitebd769602016-02-02 09:30:11 -0800827 This function can be used in setUpClass context to register a resource
Marc Koderer24eb89c2014-01-31 11:23:33 +0100828 for a test.
829
830 :param name: The name of the kind of resource such as "flavor", "role",
831 etc.
832 :resource: The id of the resource
833 """
834 cls._resources[name] = resource
835
836 def get_resource(self, name):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000837 """Return a valid uuid for a type of resource.
838
839 If a real resource is needed as part of a url then this method should
840 return one. Otherwise it can return None.
Marc Koderer24eb89c2014-01-31 11:23:33 +0100841
842 :param name: The name of the kind of resource such as "flavor", "role",
843 etc.
844 """
Marc Koderer424c84f2014-02-06 17:02:19 +0100845 if isinstance(name, dict):
846 name = name['name']
Marc Koderer24eb89c2014-01-31 11:23:33 +0100847 if hasattr(self, "resource") and self.resource[0] == name:
848 LOG.debug("Return invalid resource (%s) value: %s" %
849 (self.resource[0], self.resource[1]))
850 return self.resource[1]
851 if name in self._resources:
852 return self._resources[name]
853 return None
854
855
Marc Kodererb2978da2014-03-26 13:45:43 +0100856def SimpleNegativeAutoTest(klass):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000857 """This decorator registers a test function on basis of the class name."""
Sean Dague5e1bcd92015-04-27 09:08:36 -0400858 @attr(type=['negative'])
Marc Kodererb2978da2014-03-26 13:45:43 +0100859 def generic_test(self):
Marc Koderer4f44d722014-08-07 14:04:58 +0200860 if hasattr(self, '_schema'):
861 self.execute(self._schema)
Marc Kodererb2978da2014-03-26 13:45:43 +0100862
863 cn = klass.__name__
864 cn = cn.replace('JSON', '')
865 cn = cn.replace('Test', '')
866 # NOTE(mkoderer): replaces uppercase chars inside the class name with '_'
867 lower_cn = re.sub('(?<!^)(?=[A-Z])', '_', cn).lower()
868 func_name = 'test_%s' % lower_cn
869 setattr(klass, func_name, generic_test)
870 return klass
871
872
Sean Dague35a7caf2013-05-10 10:38:22 -0400873def call_until_true(func, duration, sleep_for):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000874 """Call the given function until it returns True (and return True)
875
876 or until the specified duration (in seconds) elapses (and return False).
Sean Dague35a7caf2013-05-10 10:38:22 -0400877
878 :param func: A zero argument callable that returns True on success.
879 :param duration: The number of seconds for which to attempt a
880 successful call of the function.
881 :param sleep_for: The number of seconds to sleep after an unsuccessful
882 invocation of the function.
883 """
884 now = time.time()
885 timeout = now + duration
886 while now < timeout:
887 if func():
888 return True
Sean Dague35a7caf2013-05-10 10:38:22 -0400889 time.sleep(sleep_for)
890 now = time.time()
891 return False