blob: 609f1f69e78e0fec7b7758e364d46ca2dd089d0a [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 -040021
Jordan Pittier35a63752016-08-30 13:09:12 +020022import debtcollector.moves
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
Jordan Pittier35a63752016-08-30 13:09:12 +020041from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050042from tempest.lib import decorators
Andrea Frittoli (andreaf)af4f7cf2016-06-13 15:12:26 +010043from tempest.lib import exceptions as lib_exc
Jay Pipes051075a2012-04-28 17:39:37 -040044
45LOG = logging.getLogger(__name__)
46
Sean Dague86bd8422013-12-20 09:56:44 -050047CONF = config.CONF
48
Matthew Treinishc1802bc2015-12-03 18:48:11 -050049idempotent_id = decorators.idempotent_id
50
Jay Pipes051075a2012-04-28 17:39:37 -040051
Yaroslav Lobankovda999f72015-06-30 20:32:55 +030052def attr(**kwargs):
liuchenhong00caec52015-07-19 22:40:28 +080053 """A decorator which applies the testtools attr decorator
Chris Yeoh55530bb2013-02-08 16:04:27 +103054
Matthew Treinisha74f5d42014-02-07 20:25:44 -050055 This decorator applies the testtools.testcase.attr if it is in the list of
56 attributes to testtools we want to apply.
Attila Fazekasb2902af2013-02-16 16:22:44 +010057 """
Chris Yeoh55530bb2013-02-08 16:04:27 +103058
59 def decorator(f):
Giulio Fidente4946a052013-05-14 12:23:51 +020060 if 'type' in kwargs and isinstance(kwargs['type'], str):
61 f = testtools.testcase.attr(kwargs['type'])(f)
62 elif 'type' in kwargs and isinstance(kwargs['type'], list):
63 for attr in kwargs['type']:
64 f = testtools.testcase.attr(attr)(f)
Matthew Treinisha74f5d42014-02-07 20:25:44 -050065 return f
Chris Yeoh55530bb2013-02-08 16:04:27 +103066
67 return decorator
68
69
Matthew Treinish3d8c7322014-08-03 23:53:28 -040070def get_service_list():
Matthew Treinish8afbffd2014-01-21 23:56:13 +000071 service_list = {
72 'compute': CONF.service_available.nova,
73 'image': CONF.service_available.glance,
Adam Gandelman4a48a602014-03-20 18:23:18 -070074 'baremetal': CONF.service_available.ironic,
Matthew Treinish8afbffd2014-01-21 23:56:13 +000075 'volume': CONF.service_available.cinder,
Matthew Treinish8afbffd2014-01-21 23:56:13 +000076 'network': True,
77 'identity': True,
78 'object_storage': CONF.service_available.swift,
Matthew Treinish8afbffd2014-01-21 23:56:13 +000079 }
Matthew Treinish3d8c7322014-08-03 23:53:28 -040080 return service_list
Matthew Treinish16c43792013-09-09 19:55:23 +000081
Matthew Treinish3d8c7322014-08-03 23:53:28 -040082
Yaroslav Lobankovda999f72015-06-30 20:32:55 +030083def services(*args):
Matthew Treinish3d8c7322014-08-03 23:53:28 -040084 """A decorator used to set an attr for each service used in a test case
85
86 This decorator applies a testtools attr for each service that gets
87 exercised by a test case.
88 """
Matthew Treinish16c43792013-09-09 19:55:23 +000089 def decorator(f):
Masayuki Igawa22b30082016-06-27 16:18:59 +090090 services = ['compute', 'image', 'baremetal', 'volume',
91 'network', 'identity', 'object_storage']
Matthew Treinish16c43792013-09-09 19:55:23 +000092 for service in args:
Matthew Treinish3d8c7322014-08-03 23:53:28 -040093 if service not in services:
94 raise exceptions.InvalidServiceTag('%s is not a valid '
95 'service' % service)
Matthew Treinish16c43792013-09-09 19:55:23 +000096 attr(type=list(args))(f)
Matthew Treinish8afbffd2014-01-21 23:56:13 +000097
98 @functools.wraps(f)
99 def wrapper(self, *func_args, **func_kwargs):
Matthew Treinish3d8c7322014-08-03 23:53:28 -0400100 service_list = get_service_list()
101
Matthew Treinish8afbffd2014-01-21 23:56:13 +0000102 for service in args:
103 if not service_list[service]:
104 msg = 'Skipped because the %s service is not available' % (
105 service)
106 raise testtools.TestCase.skipException(msg)
107 return f(self, *func_args, **func_kwargs)
108 return wrapper
Matthew Treinish16c43792013-09-09 19:55:23 +0000109 return decorator
110
111
Yaroslav Lobankovda999f72015-06-30 20:32:55 +0300112def stresstest(**kwargs):
Marc Koderer32221b8e2013-08-23 13:57:50 +0200113 """Add stress test decorator
114
115 For all functions with this decorator a attr stress will be
116 set automatically.
117
118 @param class_setup_per: allowed values are application, process, action
119 ``application``: once in the stress job lifetime
120 ``process``: once in the worker process lifetime
121 ``action``: on each action
Marc Kodererb0604412013-09-02 09:43:40 +0200122 @param allow_inheritance: allows inheritance of this attribute
Marc Koderer32221b8e2013-08-23 13:57:50 +0200123 """
124 def decorator(f):
125 if 'class_setup_per' in kwargs:
126 setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
127 else:
128 setattr(f, "st_class_setup_per", 'process')
Marc Kodererb0604412013-09-02 09:43:40 +0200129 if 'allow_inheritance' in kwargs:
130 setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
131 else:
132 setattr(f, "st_allow_inheritance", False)
Marc Koderer32221b8e2013-08-23 13:57:50 +0200133 attr(type='stress')(f)
134 return f
135 return decorator
136
137
Yaroslav Lobankovda999f72015-06-30 20:32:55 +0300138def requires_ext(**kwargs):
Matthew Treinishe3d26142013-11-26 19:14:58 +0000139 """A decorator to skip tests if an extension is not enabled
140
141 @param extension
142 @param service
143 """
144 def decorator(func):
145 @functools.wraps(func)
146 def wrapper(*func_args, **func_kwargs):
147 if not is_extension_enabled(kwargs['extension'],
148 kwargs['service']):
149 msg = "Skipped because %s extension: %s is not enabled" % (
150 kwargs['service'], kwargs['extension'])
151 raise testtools.TestCase.skipException(msg)
152 return func(*func_args, **func_kwargs)
153 return wrapper
154 return decorator
155
156
157def is_extension_enabled(extension_name, service):
158 """A function that will check the list of enabled extensions from config
159
160 """
Matthew Treinishe3d26142013-11-26 19:14:58 +0000161 config_dict = {
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000162 'compute': CONF.compute_feature_enabled.api_extensions,
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000163 'volume': CONF.volume_feature_enabled.api_extensions,
164 'network': CONF.network_feature_enabled.api_extensions,
165 'object': CONF.object_storage_feature_enabled.discoverable_apis,
Jane Zadorozhna121576d2015-06-23 12:57:13 +0300166 'identity': CONF.identity_feature_enabled.api_extensions
Matthew Treinishe3d26142013-11-26 19:14:58 +0000167 }
Simeon Monov5d7effe2014-07-16 07:32:38 +0300168 if len(config_dict[service]) == 0:
169 return False
Matthew Treinishe3d26142013-11-26 19:14:58 +0000170 if config_dict[service][0] == 'all':
171 return True
172 if extension_name in config_dict[service]:
173 return True
174 return False
175
Ian Wienand98c35f32013-07-23 20:34:23 +1000176
Yair Fried95914122016-03-03 09:14:40 +0200177def is_scheduler_filter_enabled(filter_name):
Yair Friedca5cfb52016-01-04 15:41:55 +0200178 """Check the list of enabled compute scheduler filters from config. """
179
180 filters = CONF.compute_feature_enabled.scheduler_available_filters
181 if len(filters) == 0:
182 return False
183 if 'all' in filters:
184 return True
185 if filter_name in filters:
186 return True
187 return False
188
189
Attila Fazekasf86fa312013-07-30 19:56:39 +0200190at_exit_set = set()
191
192
193def validate_tearDownClass():
194 if at_exit_set:
Sean Dagueeb1523b2014-03-10 10:17:44 -0400195 LOG.error(
196 "tearDownClass does not call the super's "
197 "tearDownClass in these classes: \n"
198 + str(at_exit_set))
199
Attila Fazekasf86fa312013-07-30 19:56:39 +0200200
201atexit.register(validate_tearDownClass)
202
Attila Fazekas53943322014-02-10 16:07:34 +0100203
Matthew Treinish2474f412014-11-17 18:11:56 -0500204class BaseTestCase(testtools.testcase.WithAttributes,
205 testtools.TestCase):
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100206 """The test base class defines Tempest framework for class level fixtures.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000207
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100208 `setUpClass` and `tearDownClass` are defined here and cannot be overwritten
209 by subclasses (enforced via hacking rule T105).
210
211 Set-up is split in a series of steps (setup stages), which can be
212 overwritten by test classes. Set-up stages are:
213 - skip_checks
214 - setup_credentials
215 - setup_clients
216 - resource_setup
217
218 Tear-down is also split in a series of steps (teardown stages), which are
219 stacked for execution only if the corresponding setup stage had been
220 reached during the setup phase. Tear-down stages are:
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700221 - clear_credentials (defined in the base test class)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100222 - resource_cleanup
223 """
Attila Fazekasc43fec82013-04-09 23:17:52 +0200224
Attila Fazekasf86fa312013-07-30 19:56:39 +0200225 setUpClassCalled = False
Marc Koderer24eb89c2014-01-31 11:23:33 +0100226 _service = None
Attila Fazekasf86fa312013-07-30 19:56:39 +0200227
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000228 # NOTE(andreaf) credentials holds a list of the credentials to be allocated
Andrea Frittoli (andreaf)825b2d32015-04-08 20:58:01 +0100229 # at class setup time. Credential types can be 'primary', 'alt', 'admin' or
230 # a list of roles - the first element of the list being a label, and the
231 # rest the actual roles
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000232 credentials = []
nithya-ganesan222efd72015-01-22 12:20:27 +0000233 # Resources required to validate a server using ssh
234 validation_resources = {}
Matthew Treinish9f756a02014-01-15 10:26:07 -0500235 network_resources = {}
236
Sean Dague2ef32ac2014-06-09 11:32:23 -0400237 # NOTE(sdague): log_format is defined inline here instead of using the oslo
238 # default because going through the config path recouples config to the
239 # stress tests too early, and depending on testr order will fail unit tests
240 log_format = ('%(asctime)s %(process)d %(levelname)-8s '
241 '[%(name)s] %(message)s')
242
Ryota MIBU60687e52015-12-09 18:37:39 +0900243 # Client manager class to use in this test case.
244 client_manager = clients.Manager
245
Sean Dague02620fd2016-03-02 15:52:51 -0500246 # A way to adjust slow test classes
247 TIMEOUT_SCALING_FACTOR = 1
248
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200249 @classmethod
250 def setUpClass(cls):
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100251 # It should never be overridden by descendants
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200252 if hasattr(super(BaseTestCase, cls), 'setUpClass'):
253 super(BaseTestCase, cls).setUpClass()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200254 cls.setUpClassCalled = True
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100255 # Stack of (name, callable) to be invoked in reverse order at teardown
256 cls.teardowns = []
257 # All the configuration checks that may generate a skip
258 cls.skip_checks()
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100259 try:
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100260 # Allocation of all required credentials and client managers
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700261 cls.teardowns.append(('credentials', cls.clear_credentials))
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100262 cls.setup_credentials()
263 # Shortcuts to clients
264 cls.setup_clients()
265 # Additional class-wide test resources
266 cls.teardowns.append(('resources', cls.resource_cleanup))
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100267 cls.resource_setup()
268 except Exception:
269 etype, value, trace = sys.exc_info()
Matthew Treinished2ad4f2014-12-23 15:18:32 -0500270 LOG.info("%s raised in %s.setUpClass. Invoking tearDownClass." % (
271 etype, cls.__name__))
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100272 cls.tearDownClass()
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100273 try:
Matthew Treinish843227d2015-04-23 10:17:17 -0400274 six.reraise(etype, value, trace)
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100275 finally:
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100276 del trace # to avoid circular refs
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200277
Attila Fazekasf86fa312013-07-30 19:56:39 +0200278 @classmethod
279 def tearDownClass(cls):
Attila Fazekas5d275302013-08-29 12:35:12 +0200280 at_exit_set.discard(cls)
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100281 # It should never be overridden by descendants
Attila Fazekasf86fa312013-07-30 19:56:39 +0200282 if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
283 super(BaseTestCase, cls).tearDownClass()
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100284 # Save any existing exception, we always want to re-raise the original
285 # exception only
286 etype, value, trace = sys.exc_info()
287 # If there was no exception during setup we shall re-raise the first
288 # exception in teardown
289 re_raise = (etype is None)
290 while cls.teardowns:
291 name, teardown = cls.teardowns.pop()
292 # Catch any exception in tearDown so we can re-raise the original
293 # exception at the end
294 try:
295 teardown()
296 except Exception as te:
297 sys_exec_info = sys.exc_info()
298 tetype = sys_exec_info[0]
299 # TODO(andreaf): Till we have the ability to cleanup only
300 # resources that were successfully setup in resource_cleanup,
301 # log AttributeError as info instead of exception.
302 if tetype is AttributeError and name == 'resources':
303 LOG.info("tearDownClass of %s failed: %s" % (name, te))
304 else:
305 LOG.exception("teardown of %s failed: %s" % (name, te))
306 if not etype:
307 etype, value, trace = sys_exec_info
Joshua Whitebd769602016-02-02 09:30:11 -0800308 # If exceptions were raised during teardown, and not before, re-raise
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100309 # the first one
310 if re_raise and etype is not None:
311 try:
Matthew Treinish843227d2015-04-23 10:17:17 -0400312 six.reraise(etype, value, trace)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100313 finally:
314 del trace # to avoid circular refs
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100315
316 @classmethod
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100317 def skip_checks(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000318 """Class level skip checks.
319
320 Subclasses verify in here all conditions that might prevent the
321 execution of the entire test class.
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100322 Checks implemented here may not make use API calls, and should rely on
323 configuration alone.
324 In general skip checks that require an API call are discouraged.
325 If one is really needed it may be implemented either in the
326 resource_setup or at test level.
327 """
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100328 identity_version = cls.get_identity_version()
329 if 'admin' in cls.credentials and not credentials.is_admin_available(
330 identity_version=identity_version):
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000331 msg = "Missing Identity Admin API credentials in configuration."
332 raise cls.skipException(msg)
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100333 if 'alt' in cls.credentials and not credentials.is_alt_available(
334 identity_version=identity_version):
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000335 msg = "Missing a 2nd set of API credentials in configuration."
336 raise cls.skipException(msg)
Andrea Frittoli (andreaf)41601412015-05-12 16:39:03 +0100337 if hasattr(cls, 'identity_version'):
338 if cls.identity_version == 'v2':
339 if not CONF.identity_feature_enabled.api_v2:
340 raise cls.skipException("Identity api v2 is not enabled")
341 elif cls.identity_version == 'v3':
342 if not CONF.identity_feature_enabled.api_v3:
343 raise cls.skipException("Identity api v3 is not enabled")
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100344
345 @classmethod
346 def setup_credentials(cls):
edannon6cc6fbc2016-05-03 11:56:12 +0300347 """Allocate credentials and create the client managers from them.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000348
edannon6cc6fbc2016-05-03 11:56:12 +0300349 For every element of credentials param function creates tenant/user,
350 Then it creates client manager for that credential.
351
352 Network related tests must override this function with
353 set_network_resources() method, otherwise it will create
354 network resources(network resources are created in a later step).
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000355 """
356 for credentials_type in cls.credentials:
357 # This may raise an exception in case credentials are not available
358 # In that case we want to let the exception through and the test
359 # fail accordingly
Andrea Frittoli (andreaf)825b2d32015-04-08 20:58:01 +0100360 if isinstance(credentials_type, six.string_types):
361 manager = cls.get_client_manager(
362 credential_type=credentials_type)
363 setattr(cls, 'os_%s' % credentials_type, manager)
364 # Setup some common aliases
365 # TODO(andreaf) The aliases below are a temporary hack
366 # to avoid changing too much code in one patch. They should
367 # be removed eventually
368 if credentials_type == 'primary':
369 cls.os = cls.manager = cls.os_primary
370 if credentials_type == 'admin':
371 cls.os_adm = cls.admin_manager = cls.os_admin
372 if credentials_type == 'alt':
373 cls.alt_manager = cls.os_alt
374 elif isinstance(credentials_type, list):
375 manager = cls.get_client_manager(roles=credentials_type[1:],
376 force_new=True)
377 setattr(cls, 'os_roles_%s' % credentials_type[0], manager)
Andrea Frittolia5ddd552014-08-19 18:30:00 +0100378
379 @classmethod
380 def setup_clients(cls):
381 """Create links to the clients into the test object."""
382 # TODO(andreaf) There is a fair amount of code that could me moved from
383 # base / test classes in here. Ideally tests should be able to only
384 # specify which client is `client` and nothing else.
Andrea Frittoli73ee2472014-09-15 12:31:53 +0100385 pass
Attila Fazekasf86fa312013-07-30 19:56:39 +0200386
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000387 @classmethod
388 def resource_setup(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000389 """Class level resource setup for test cases."""
nithya-ganesan222efd72015-01-22 12:20:27 +0000390 if hasattr(cls, "os"):
391 cls.validation_resources = vresources.create_validation_resources(
392 cls.os, cls.validation_resources)
393 else:
zhangguoqing6c096642016-01-04 06:17:21 +0000394 LOG.warning("Client manager not found, validation resources not"
395 " created")
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000396
397 @classmethod
398 def resource_cleanup(cls):
399 """Class level resource cleanup for test cases.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000400
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000401 Resource cleanup must be able to handle the case of partially setup
402 resources, in case a failure during `resource_setup` should happen.
403 """
nithya-ganesan222efd72015-01-22 12:20:27 +0000404 if cls.validation_resources:
405 if hasattr(cls, "os"):
406 vresources.clear_validation_resources(cls.os,
407 cls.validation_resources)
408 cls.validation_resources = {}
409 else:
zhangguoqing6c096642016-01-04 06:17:21 +0000410 LOG.warning("Client manager not found, validation resources "
411 "not deleted")
Emily Hugenbruch5bd4cbf2014-12-17 21:38:38 +0000412
Attila Fazekasf86fa312013-07-30 19:56:39 +0200413 def setUp(self):
414 super(BaseTestCase, self).setUp()
415 if not self.setUpClassCalled:
416 raise RuntimeError("setUpClass does not calls the super's"
417 "setUpClass in the "
418 + self.__class__.__name__)
419 at_exit_set.add(self.__class__)
Matthew Treinish78561ad2013-07-26 11:41:56 -0400420 test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
421 try:
Sean Dague02620fd2016-03-02 15:52:51 -0500422 test_timeout = int(test_timeout) * self.TIMEOUT_SCALING_FACTOR
Matthew Treinish78561ad2013-07-26 11:41:56 -0400423 except ValueError:
424 test_timeout = 0
425 if test_timeout > 0:
Attila Fazekasf86fa312013-07-30 19:56:39 +0200426 self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400427
428 if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
429 os.environ.get('OS_STDOUT_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200430 stdout = self.useFixture(fixtures.StringStream('stdout')).stream
431 self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400432 if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
433 os.environ.get('OS_STDERR_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200434 stderr = self.useFixture(fixtures.StringStream('stderr')).stream
435 self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
Attila Fazekas31388072013-08-15 08:58:07 +0200436 if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
437 os.environ.get('OS_LOG_CAPTURE') != '0'):
Attila Fazekas31388072013-08-15 08:58:07 +0200438 self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
Sean Dague2ef32ac2014-06-09 11:32:23 -0400439 format=self.log_format,
Attila Fazekas90445be2013-10-24 16:46:03 +0200440 level=None))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400441
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100442 @property
443 def credentials_provider(self):
444 return self._get_credentials_provider()
445
Jamie Lennox15350172015-08-17 10:54:25 +1000446 @property
447 def identity_utils(self):
448 """A client that abstracts v2 and v3 identity operations.
449
450 This can be used for creating and tearing down projects in tests. It
451 should not be used for testing identity features.
452 """
453 if CONF.identity.auth_version == 'v2':
454 client = self.os_admin.identity_client
Daniel Mellado7aea5342016-02-09 09:10:12 +0000455 users_client = self.os_admin.users_client
Daniel Melladob04da902015-11-20 17:43:12 +0100456 project_client = self.os_admin.tenants_client
Daniel Mellado6b16b922015-12-07 12:43:08 +0000457 roles_client = self.os_admin.roles_client
Daniel Mellado91a26b62016-02-11 11:13:04 +0000458 domains_client = None
Jamie Lennox15350172015-08-17 10:54:25 +1000459 else:
460 client = self.os_admin.identity_v3_client
Daniel Mellado7aea5342016-02-09 09:10:12 +0000461 users_client = self.os_admin.users_v3_client
Arx Cruz24bcb882016-02-10 15:20:16 +0100462 project_client = self.os_admin.projects_client
463 roles_client = self.os_admin.roles_v3_client
Daniel Mellado91a26b62016-02-11 11:13:04 +0000464 domains_client = self.os_admin.domains_client
Jamie Lennox15350172015-08-17 10:54:25 +1000465
466 try:
467 domain = client.auth_provider.credentials.project_domain_name
468 except AttributeError:
469 domain = 'Default'
470
Daniel Melladob04da902015-11-20 17:43:12 +0100471 return cred_client.get_creds_client(client, project_client,
Daniel Mellado82c83a52015-12-09 15:16:49 +0000472 users_client,
Daniel Mellado7aea5342016-02-09 09:10:12 +0000473 roles_client,
Daniel Mellado91a26b62016-02-11 11:13:04 +0000474 domains_client,
Daniel Melladob04da902015-11-20 17:43:12 +0100475 project_domain_name=domain)
Jamie Lennox15350172015-08-17 10:54:25 +1000476
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100477 @classmethod
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100478 def get_identity_version(cls):
479 """Returns the identity version used by the test class"""
480 identity_version = getattr(cls, 'identity_version', None)
481 return identity_version or CONF.identity.auth_version
482
483 @classmethod
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100484 def _get_credentials_provider(cls):
485 """Returns a credentials provider
486
487 If no credential provider exists yet creates one.
488 It uses self.identity_version if defined, or the configuration value
489 """
490 if (not hasattr(cls, '_creds_provider') or not cls._creds_provider or
491 not cls._creds_provider.name == cls.__name__):
492 force_tenant_isolation = getattr(cls, 'force_tenant_isolation',
493 False)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100494
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700495 cls._creds_provider = credentials.get_credentials_provider(
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100496 name=cls.__name__, network_resources=cls.network_resources,
497 force_tenant_isolation=force_tenant_isolation,
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100498 identity_version=cls.get_identity_version())
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100499 return cls._creds_provider
500
Matthew Treinish3e046852013-07-23 16:00:24 -0400501 @classmethod
Andrea Frittoli (andreaf)41601412015-05-12 16:39:03 +0100502 def get_client_manager(cls, credential_type=None, roles=None,
503 force_new=None):
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100504 """Returns an OpenStack client manager
505
506 Returns an OpenStack client manager based on either credential_type
507 or a list of roles. If neither is specified, it defaults to
508 credential_type 'primary'
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100509 :param credential_type: string - primary, alt or admin
510 :param roles: list of roles
511
lei zhangdd552b22015-11-25 20:41:48 +0800512 :returns: the created client manager
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100513 :raises skipException: if the requested credentials are not available
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700514 """
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100515 if all([roles, credential_type]):
516 msg = "Cannot get credentials by type and roles at the same time"
517 raise ValueError(msg)
518 if not any([roles, credential_type]):
519 credential_type = 'primary'
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100520 cred_provider = cls._get_credentials_provider()
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100521 if roles:
522 for role in roles:
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100523 if not cred_provider.is_role_available(role):
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100524 skip_msg = (
525 "%s skipped because the configured credential provider"
526 " is not able to provide credentials with the %s role "
527 "assigned." % (cls.__name__, role))
528 raise cls.skipException(skip_msg)
529 params = dict(roles=roles)
530 if force_new is not None:
531 params.update(force_new=force_new)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100532 creds = cred_provider.get_creds_by_roles(**params)
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000533 else:
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100534 credentials_method = 'get_%s_creds' % credential_type
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100535 if hasattr(cred_provider, credentials_method):
536 creds = getattr(cred_provider, credentials_method)()
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100537 else:
Andrea Frittoli (andreaf)af4f7cf2016-06-13 15:12:26 +0100538 raise lib_exc.InvalidCredentials(
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +0100539 "Invalid credentials type %s" % credential_type)
Andrea Frittoli (andreaf)848c4a12016-06-09 11:09:02 +0100540 return cls.client_manager(credentials=creds.credentials,
541 service=cls._service)
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700542
543 @classmethod
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700544 def clear_credentials(cls):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000545 """Clears creds if set"""
Attila Fazekas5b0d9262015-05-20 10:17:39 +0200546 if hasattr(cls, '_creds_provider'):
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700547 cls._creds_provider.clear_creds()
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700548
549 @classmethod
nithya-ganesan222efd72015-01-22 12:20:27 +0000550 def set_validation_resources(cls, keypair=None, floating_ip=None,
551 security_group=None,
552 security_group_rules=None):
553 """Specify which ssh server validation resources should be created.
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000554
nithya-ganesan222efd72015-01-22 12:20:27 +0000555 Each of the argument must be set to either None, True or False, with
556 None - use default from config (security groups and security group
557 rules get created when set to None)
558 False - Do not create the validation resource
559 True - create the validation resource
560
561 @param keypair
562 @param security_group
563 @param security_group_rules
564 @param floating_ip
565 """
Matthew Treinishe5cca002015-05-11 15:36:50 -0400566 if not CONF.validation.run_validation:
567 return
nithya-ganesan222efd72015-01-22 12:20:27 +0000568 if keypair is None:
569 if CONF.validation.auth_method.lower() == "keypair":
570 keypair = True
571 else:
572 keypair = False
573 if floating_ip is None:
574 if CONF.validation.connect_method.lower() == "floating":
575 floating_ip = True
576 else:
577 floating_ip = False
578 if security_group is None:
Brandon Palmc6cc91d2015-08-19 13:20:21 -0500579 security_group = CONF.validation.security_group
nithya-ganesan222efd72015-01-22 12:20:27 +0000580 if security_group_rules is None:
Brandon Palmc6cc91d2015-08-19 13:20:21 -0500581 security_group_rules = CONF.validation.security_group_rules
582
nithya-ganesan222efd72015-01-22 12:20:27 +0000583 if not cls.validation_resources:
584 cls.validation_resources = {
585 'keypair': keypair,
586 'security_group': security_group,
587 'security_group_rules': security_group_rules,
588 'floating_ip': floating_ip}
589
590 @classmethod
Andrea Frittoli7d5ed592015-02-10 01:10:23 +0000591 def set_network_resources(cls, network=False, router=False, subnet=False,
Matthew Treinish9f756a02014-01-15 10:26:07 -0500592 dhcp=False):
593 """Specify which network resources should be created
594
595 @param network
596 @param router
597 @param subnet
598 @param dhcp
599 """
Salvatore Orlando5a337242014-01-15 22:49:22 +0000600 # network resources should be set only once from callers
601 # in order to ensure that even if it's called multiple times in
602 # a chain of overloaded methods, the attribute is set only
603 # in the leaf class
Andrea Frittoli7d5ed592015-02-10 01:10:23 +0000604 if not cls.network_resources:
605 cls.network_resources = {
Salvatore Orlando5a337242014-01-15 22:49:22 +0000606 'network': network,
607 'router': router,
608 'subnet': subnet,
609 'dhcp': dhcp}
Matthew Treinish9f756a02014-01-15 10:26:07 -0500610
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530611 @classmethod
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000612 def get_tenant_network(cls, credentials_type='primary'):
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530613 """Get the network to be used in testing
614
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000615 :param credentials_type: The type of credentials for which to get the
616 tenant network
617
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530618 :return: network dict including 'id' and 'name'
619 """
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000620 # Get a manager for the given credentials_type, but at least
621 # always fall back on getting the manager for primary credentials
622 if isinstance(credentials_type, six.string_types):
623 manager = cls.get_client_manager(credential_type=credentials_type)
624 elif isinstance(credentials_type, list):
625 manager = cls.get_client_manager(roles=credentials_type[1:])
626 else:
627 manager = cls.get_client_manager()
628
Andrea Frittoli (andreaf)17209bb2015-05-22 10:16:57 -0700629 # Make sure cred_provider exists and get a network client
Ryan Rossiter9228bf72016-02-25 03:06:12 +0000630 networks_client = manager.compute_networks_client
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100631 cred_provider = cls._get_credentials_provider()
Andrea Frittoli700711e2015-04-02 11:39:38 +0100632 # In case of nova network, isolated tenants are not able to list the
Joshua Whitebd769602016-02-02 09:30:11 -0800633 # network configured in fixed_network_name, even if they can use it
Andrea Frittoli700711e2015-04-02 11:39:38 +0100634 # for their servers, so using an admin network client to validate
635 # the network name
636 if (not CONF.service_available.neutron and
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100637 credentials.is_admin_available(
638 identity_version=cls.get_identity_version())):
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100639 admin_creds = cred_provider.get_admin_creds()
Andrea Frittoli (andreaf)848c4a12016-06-09 11:09:02 +0100640 admin_manager = clients.Manager(admin_creds.credentials)
John Warren9487a182015-09-14 18:12:56 -0400641 networks_client = admin_manager.compute_networks_client
Andrea Frittoli (andreaf)940f8c62015-10-30 16:39:24 +0900642 return fixed_network.get_tenant_network(
643 cred_provider, networks_client, CONF.compute.fixed_network_name)
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530644
Mark Maglana5885eb32014-02-28 10:57:34 -0800645 def assertEmpty(self, list, msg=None):
646 self.assertTrue(len(list) == 0, msg)
647
648 def assertNotEmpty(self, list, msg=None):
649 self.assertTrue(len(list) > 0, msg)
650
Attila Fazekasdc216422013-01-29 15:12:14 +0100651
Marc Koderer24eb89c2014-01-31 11:23:33 +0100652class NegativeAutoTest(BaseTestCase):
653
654 _resources = {}
655
656 @classmethod
657 def setUpClass(cls):
658 super(NegativeAutoTest, cls).setUpClass()
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000659 os = cls.get_client_manager(credential_type='primary')
Marc Koderer24eb89c2014-01-31 11:23:33 +0100660 cls.client = os.negative_client
661
662 @staticmethod
Marc Koderer674c8fc2014-03-17 09:45:04 +0100663 def load_tests(*args):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000664 """Wrapper for testscenarios
665
666 To set the mandatory scenarios variable only in case a real test
667 loader is in place. Will be automatically called in case the variable
668 "load_tests" is set.
Marc Koderer674c8fc2014-03-17 09:45:04 +0100669 """
670 if getattr(args[0], 'suiteClass', None) is not None:
671 loader, standard_tests, pattern = args
672 else:
673 standard_tests, module, loader = args
674 for test in testtools.iterate_tests(standard_tests):
Marc Koderer4f44d722014-08-07 14:04:58 +0200675 schema = getattr(test, '_schema', None)
Marc Koderer3dd31052014-11-27 09:31:00 +0100676 if schema is not None:
Marc Koderer4f44d722014-08-07 14:04:58 +0200677 setattr(test, 'scenarios',
678 NegativeAutoTest.generate_scenario(schema))
Marc Koderer674c8fc2014-03-17 09:45:04 +0100679 return testscenarios.load_tests_apply_scenarios(*args)
680
681 @staticmethod
Marc Koderer4f44d722014-08-07 14:04:58 +0200682 def generate_scenario(description):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000683 """Generates the test scenario list for a given description.
Marc Koderer24eb89c2014-01-31 11:23:33 +0100684
Marc Koderer4f44d722014-08-07 14:04:58 +0200685 :param description: A file or dictionary with the following entries:
Marc Koderer24eb89c2014-01-31 11:23:33 +0100686 name (required) name for the api
687 http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
688 url (required) the url to be appended to the catalog url with '%s'
689 for each resource mentioned
690 resources: (optional) A list of resource names such as "server",
691 "flavor", etc. with an element for each '%s' in the url. This
692 method will call self.get_resource for each element when
693 constructing the positive test case template so negative
694 subclasses are expected to return valid resource ids when
695 appropriate.
696 json-schema (optional) A valid json schema that will be used to
697 create invalid data for the api calls. For "GET" and "HEAD",
698 the data is used to generate query strings appended to the url,
699 otherwise for the body of the http call.
700 """
Marc Koderer24eb89c2014-01-31 11:23:33 +0100701 LOG.debug(description)
Marc Koderer674c8fc2014-03-17 09:45:04 +0100702 generator = importutils.import_class(
703 CONF.negative.test_generator)()
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100704 generator.validate_schema(description)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100705 schema = description.get("json-schema", None)
706 resources = description.get("resources", [])
707 scenario_list = []
Marc Koderer424c84f2014-02-06 17:02:19 +0100708 expected_result = None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100709 for resource in resources:
Marc Koderer424c84f2014-02-06 17:02:19 +0100710 if isinstance(resource, dict):
711 expected_result = resource['expected_result']
712 resource = resource['name']
Marc Koderer24eb89c2014-01-31 11:23:33 +0100713 LOG.debug("Add resource to test %s" % resource)
714 scn_name = "inv_res_%s" % (resource)
Ken'ichi Ohmichid079c892016-04-19 11:23:36 -0700715 scenario_list.append((scn_name, {
716 "resource": (resource, data_utils.rand_uuid()),
717 "expected_result": expected_result
718 }))
Marc Koderer24eb89c2014-01-31 11:23:33 +0100719 if schema is not None:
Marc Kodererf07f5d12014-09-01 09:47:23 +0200720 for scenario in generator.generate_scenarios(schema):
721 scenario_list.append((scenario['_negtest_name'],
722 scenario))
Marc Koderer24eb89c2014-01-31 11:23:33 +0100723 LOG.debug(scenario_list)
724 return scenario_list
725
Marc Koderer4f44d722014-08-07 14:04:58 +0200726 def execute(self, description):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000727 """Execute a http call
728
Marc Koderer24eb89c2014-01-31 11:23:33 +0100729 Execute a http call on an api that are expected to
730 result in client errors. First it uses invalid resources that are part
731 of the url, and then invalid data for queries and http request bodies.
732
Marc Koderer4f44d722014-08-07 14:04:58 +0200733 :param description: A json file or dictionary with the following
734 entries:
Marc Koderer24eb89c2014-01-31 11:23:33 +0100735 name (required) name for the api
736 http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
737 url (required) the url to be appended to the catalog url with '%s'
738 for each resource mentioned
739 resources: (optional) A list of resource names such as "server",
740 "flavor", etc. with an element for each '%s' in the url. This
741 method will call self.get_resource for each element when
742 constructing the positive test case template so negative
743 subclasses are expected to return valid resource ids when
744 appropriate.
745 json-schema (optional) A valid json schema that will be used to
746 create invalid data for the api calls. For "GET" and "HEAD",
747 the data is used to generate query strings appended to the url,
748 otherwise for the body of the http call.
749
750 """
Marc Koderer24eb89c2014-01-31 11:23:33 +0100751 LOG.info("Executing %s" % description["name"])
752 LOG.debug(description)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200753 generator = importutils.import_class(
754 CONF.negative.test_generator)()
755 schema = description.get("json-schema", None)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100756 method = description["http-method"]
757 url = description["url"]
Marc Kodererf07f5d12014-09-01 09:47:23 +0200758 expected_result = None
759 if "default_result_code" in description:
760 expected_result = description["default_result_code"]
Marc Koderer24eb89c2014-01-31 11:23:33 +0100761
762 resources = [self.get_resource(r) for
763 r in description.get("resources", [])]
764
765 if hasattr(self, "resource"):
766 # Note(mkoderer): The resources list already contains an invalid
767 # entry (see get_resource).
768 # We just send a valid json-schema with it
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100769 valid_schema = None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100770 if schema:
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100771 valid_schema = \
772 valid.ValidTestGenerator().generate_valid(schema)
773 new_url, body = self._http_arguments(valid_schema, url, method)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200774 elif hasattr(self, "_negtest_name"):
775 schema_under_test = \
776 valid.ValidTestGenerator().generate_valid(schema)
777 local_expected_result = \
778 generator.generate_payload(self, schema_under_test)
779 if local_expected_result is not None:
780 expected_result = local_expected_result
781 new_url, body = \
782 self._http_arguments(schema_under_test, url, method)
Marc Koderer1c247c82014-03-20 08:24:38 +0100783 else:
784 raise Exception("testscenarios are not active. Please make sure "
785 "that your test runner supports the load_tests "
786 "mechanism")
Marc Koderer424c84f2014-02-06 17:02:19 +0100787
Marc Kodererf857fda2014-03-05 15:58:00 +0100788 if "admin_client" in description and description["admin_client"]:
Andrea Frittoli (andreaf)32d0de12015-10-09 14:43:53 +0100789 if not credentials.is_admin_available(
790 identity_version=self.get_identity_version()):
David Kranzafecec02015-03-23 14:27:15 -0400791 msg = ("Missing Identity Admin API credentials in"
792 "configuration.")
793 raise self.skipException(msg)
Andrea Frittoli (andreaf)1f342412015-05-12 16:37:19 +0100794 creds = self.credentials_provider.get_admin_creds()
David Kranzafecec02015-03-23 14:27:15 -0400795 os_adm = clients.Manager(credentials=creds)
796 client = os_adm.negative_client
Marc Kodererf857fda2014-03-05 15:58:00 +0100797 else:
798 client = self.client
799 resp, resp_body = client.send_request(method, new_url,
800 resources, body=body)
Marc Kodererf07f5d12014-09-01 09:47:23 +0200801 self._check_negative_response(expected_result, resp.status, resp_body)
Marc Koderer24eb89c2014-01-31 11:23:33 +0100802
803 def _http_arguments(self, json_dict, url, method):
804 LOG.debug("dict: %s url: %s method: %s" % (json_dict, url, method))
805 if not json_dict:
806 return url, None
807 elif method in ["GET", "HEAD", "PUT", "DELETE"]:
Harshada Mangesh Kakad71f6b972015-12-22 09:46:48 -0800808 return "%s?%s" % (url, urllib.parse.urlencode(json_dict)), None
Marc Koderer24eb89c2014-01-31 11:23:33 +0100809 else:
810 return url, json.dumps(json_dict)
811
Marc Kodererf07f5d12014-09-01 09:47:23 +0200812 def _check_negative_response(self, expected_result, result, body):
Marc Koderer24eb89c2014-01-31 11:23:33 +0100813 self.assertTrue(result >= 400 and result < 500 and result != 413,
814 "Expected client error, got %s:%s" %
815 (result, body))
816 self.assertTrue(expected_result is None or expected_result == result,
817 "Expected %s, got %s:%s" %
818 (expected_result, result, body))
819
820 @classmethod
821 def set_resource(cls, name, resource):
Joshua Whitebd769602016-02-02 09:30:11 -0800822 """Register a resource for a test
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000823
Joshua Whitebd769602016-02-02 09:30:11 -0800824 This function can be used in setUpClass context to register a resource
Marc Koderer24eb89c2014-01-31 11:23:33 +0100825 for a test.
826
827 :param name: The name of the kind of resource such as "flavor", "role",
828 etc.
829 :resource: The id of the resource
830 """
831 cls._resources[name] = resource
832
833 def get_resource(self, name):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000834 """Return a valid uuid for a type of resource.
835
836 If a real resource is needed as part of a url then this method should
837 return one. Otherwise it can return None.
Marc Koderer24eb89c2014-01-31 11:23:33 +0100838
839 :param name: The name of the kind of resource such as "flavor", "role",
840 etc.
841 """
Marc Koderer424c84f2014-02-06 17:02:19 +0100842 if isinstance(name, dict):
843 name = name['name']
Marc Koderer24eb89c2014-01-31 11:23:33 +0100844 if hasattr(self, "resource") and self.resource[0] == name:
845 LOG.debug("Return invalid resource (%s) value: %s" %
846 (self.resource[0], self.resource[1]))
847 return self.resource[1]
848 if name in self._resources:
849 return self._resources[name]
850 return None
851
852
Marc Kodererb2978da2014-03-26 13:45:43 +0100853def SimpleNegativeAutoTest(klass):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +0000854 """This decorator registers a test function on basis of the class name."""
Sean Dague5e1bcd92015-04-27 09:08:36 -0400855 @attr(type=['negative'])
Marc Kodererb2978da2014-03-26 13:45:43 +0100856 def generic_test(self):
Marc Koderer4f44d722014-08-07 14:04:58 +0200857 if hasattr(self, '_schema'):
858 self.execute(self._schema)
Marc Kodererb2978da2014-03-26 13:45:43 +0100859
860 cn = klass.__name__
861 cn = cn.replace('JSON', '')
862 cn = cn.replace('Test', '')
863 # NOTE(mkoderer): replaces uppercase chars inside the class name with '_'
864 lower_cn = re.sub('(?<!^)(?=[A-Z])', '_', cn).lower()
865 func_name = 'test_%s' % lower_cn
866 setattr(klass, func_name, generic_test)
867 return klass
868
869
Jordan Pittier35a63752016-08-30 13:09:12 +0200870call_until_true = debtcollector.moves.moved_function(
871 test_utils.call_until_true, 'call_until_true', __name__,
872 version='Newton', removal_version='Ocata')