blob: 83a43191c0f5299a6abd617dd1dacd208cadd33d [file] [log] [blame]
Jay Pipes051075a2012-04-28 17:39:37 -04001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
ZhiQiang Fan39f97222013-09-20 04:49:44 +08003# Copyright 2012 OpenStack Foundation
Jay Pipes051075a2012-04-28 17:39:37 -04004# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
Attila Fazekasf86fa312013-07-30 19:56:39 +020018import atexit
Masayuki Igawa80c1b9f2013-10-07 17:19:11 +090019import functools
Ian Wienand98c35f32013-07-23 20:34:23 +100020import os
Jay Pipes051075a2012-04-28 17:39:37 -040021import time
22
Matthew Treinish78561ad2013-07-26 11:41:56 -040023import fixtures
Chris Yeoh55530bb2013-02-08 16:04:27 +103024import nose.plugins.attrib
Attila Fazekasdc216422013-01-29 15:12:14 +010025import testresources
ivan-zhu1feeb382013-01-24 10:14:39 +080026import testtools
Jay Pipes051075a2012-04-28 17:39:37 -040027
Matthew Treinish3e046852013-07-23 16:00:24 -040028from tempest import clients
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -070029from tempest.common import isolated_creds
Attila Fazekasdc216422013-01-29 15:12:14 +010030from tempest import config
Matthew Treinish16c43792013-09-09 19:55:23 +000031from tempest import exceptions
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -040032from tempest.openstack.common import log as logging
Jay Pipes051075a2012-04-28 17:39:37 -040033
34LOG = logging.getLogger(__name__)
35
Sean Dague86bd8422013-12-20 09:56:44 -050036CONF = config.CONF
37
Samuel Merritt0d499bc2013-06-19 12:08:23 -070038# All the successful HTTP status codes from RFC 2616
39HTTP_SUCCESS = (200, 201, 202, 203, 204, 205, 206)
40
Jay Pipes051075a2012-04-28 17:39:37 -040041
Chris Yeoh55530bb2013-02-08 16:04:27 +103042def attr(*args, **kwargs):
43 """A decorator which applies the nose and testtools attr decorator
44
45 This decorator applies the nose attr decorator as well as the
46 the testtools.testcase.attr if it is in the list of attributes
Attila Fazekasb2902af2013-02-16 16:22:44 +010047 to testtools we want to apply.
48 """
Chris Yeoh55530bb2013-02-08 16:04:27 +103049
50 def decorator(f):
Giulio Fidente4946a052013-05-14 12:23:51 +020051 if 'type' in kwargs and isinstance(kwargs['type'], str):
52 f = testtools.testcase.attr(kwargs['type'])(f)
Chris Yeohcf3fb7c2013-05-19 15:59:00 +093053 if kwargs['type'] == 'smoke':
54 f = testtools.testcase.attr('gate')(f)
Giulio Fidente4946a052013-05-14 12:23:51 +020055 elif 'type' in kwargs and isinstance(kwargs['type'], list):
56 for attr in kwargs['type']:
57 f = testtools.testcase.attr(attr)(f)
Chris Yeohcf3fb7c2013-05-19 15:59:00 +093058 if attr == 'smoke':
59 f = testtools.testcase.attr('gate')(f)
Giulio Fidente4946a052013-05-14 12:23:51 +020060 return nose.plugins.attrib.attr(*args, **kwargs)(f)
Chris Yeoh55530bb2013-02-08 16:04:27 +103061
62 return decorator
63
64
Matthew Treinish16c43792013-09-09 19:55:23 +000065def services(*args, **kwargs):
66 """A decorator used to set an attr for each service used in a test case
67
68 This decorator applies a testtools attr for each service that gets
69 exercised by a test case.
70 """
71 valid_service_list = ['compute', 'image', 'volume', 'orchestration',
72 'network', 'identity', 'object', 'dashboard']
73
74 def decorator(f):
75 for service in args:
76 if service not in valid_service_list:
77 raise exceptions.InvalidServiceTag('%s is not a valid service'
78 % service)
79 attr(type=list(args))(f)
80 return f
81 return decorator
82
83
Marc Koderer32221b8e2013-08-23 13:57:50 +020084def stresstest(*args, **kwargs):
85 """Add stress test decorator
86
87 For all functions with this decorator a attr stress will be
88 set automatically.
89
90 @param class_setup_per: allowed values are application, process, action
91 ``application``: once in the stress job lifetime
92 ``process``: once in the worker process lifetime
93 ``action``: on each action
Marc Kodererb0604412013-09-02 09:43:40 +020094 @param allow_inheritance: allows inheritance of this attribute
Marc Koderer32221b8e2013-08-23 13:57:50 +020095 """
96 def decorator(f):
97 if 'class_setup_per' in kwargs:
98 setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
99 else:
100 setattr(f, "st_class_setup_per", 'process')
Marc Kodererb0604412013-09-02 09:43:40 +0200101 if 'allow_inheritance' in kwargs:
102 setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
103 else:
104 setattr(f, "st_allow_inheritance", False)
Marc Koderer32221b8e2013-08-23 13:57:50 +0200105 attr(type='stress')(f)
106 return f
107 return decorator
108
109
Giulio Fidente83181a92013-10-01 06:02:24 +0200110def skip_because(*args, **kwargs):
111 """A decorator useful to skip tests hitting known bugs
112
113 @param bug: bug number causing the test to skip
114 @param condition: optional condition to be True for the skip to have place
Ken'ichi Ohmichia1aa44c2013-12-06 20:48:24 +0900115 @param interface: skip the test if it is the same as self._interface
Giulio Fidente83181a92013-10-01 06:02:24 +0200116 """
117 def decorator(f):
Masayuki Igawa80c1b9f2013-10-07 17:19:11 +0900118 @functools.wraps(f)
Ken'ichi Ohmichia1aa44c2013-12-06 20:48:24 +0900119 def wrapper(self, *func_args, **func_kwargs):
120 skip = False
121 if "condition" in kwargs:
122 if kwargs["condition"] is True:
123 skip = True
124 elif "interface" in kwargs:
125 if kwargs["interface"] == self._interface:
126 skip = True
127 else:
128 skip = True
129 if "bug" in kwargs and skip is True:
130 msg = "Skipped until Bug: %s is resolved." % kwargs["bug"]
131 raise testtools.TestCase.skipException(msg)
132 return f(self, *func_args, **func_kwargs)
Masayuki Igawa80c1b9f2013-10-07 17:19:11 +0900133 return wrapper
Giulio Fidente83181a92013-10-01 06:02:24 +0200134 return decorator
135
136
Matthew Treinishe3d26142013-11-26 19:14:58 +0000137def requires_ext(*args, **kwargs):
138 """A decorator to skip tests if an extension is not enabled
139
140 @param extension
141 @param service
142 """
143 def decorator(func):
144 @functools.wraps(func)
145 def wrapper(*func_args, **func_kwargs):
146 if not is_extension_enabled(kwargs['extension'],
147 kwargs['service']):
148 msg = "Skipped because %s extension: %s is not enabled" % (
149 kwargs['service'], kwargs['extension'])
150 raise testtools.TestCase.skipException(msg)
151 return func(*func_args, **func_kwargs)
152 return wrapper
153 return decorator
154
155
156def is_extension_enabled(extension_name, service):
157 """A function that will check the list of enabled extensions from config
158
159 """
Sean Dague86bd8422013-12-20 09:56:44 -0500160 configs = CONF
Matthew Treinishe3d26142013-11-26 19:14:58 +0000161 config_dict = {
162 'compute': configs.compute_feature_enabled.api_extensions,
163 'compute_v3': configs.compute_feature_enabled.api_v3_extensions,
164 'volume': configs.volume_feature_enabled.api_extensions,
165 'network': configs.network_feature_enabled.api_extensions,
Matthew Treinish20345382013-12-13 17:04:23 +0000166 'object': configs.object_storage_feature_enabled.discoverable_apis,
Matthew Treinishe3d26142013-11-26 19:14:58 +0000167 }
168 if config_dict[service][0] == 'all':
169 return True
170 if extension_name in config_dict[service]:
171 return True
172 return False
173
Ian Wienand98c35f32013-07-23 20:34:23 +1000174# there is a mis-match between nose and testtools for older pythons.
175# testtools will set skipException to be either
176# unittest.case.SkipTest, unittest2.case.SkipTest or an internal skip
177# exception, depending on what it can find. Python <2.7 doesn't have
178# unittest.case.SkipTest; so if unittest2 is not installed it falls
179# back to the internal class.
180#
181# The current nose skip plugin will decide to raise either
182# unittest.case.SkipTest or its own internal exception; it does not
183# look for unittest2 or the internal unittest exception. Thus we must
184# monkey-patch testtools.TestCase.skipException to be the exception
185# the nose skip plugin expects.
186#
187# However, with the switch to testr nose may not be available, so we
188# require you to opt-in to this fix with an environment variable.
189#
190# This is temporary until upstream nose starts looking for unittest2
191# as testtools does; we can then remove this and ensure unittest2 is
192# available for older pythons; then nose and testtools will agree
193# unittest2.case.SkipTest is the one-true skip test exception.
194#
195# https://review.openstack.org/#/c/33056
196# https://github.com/nose-devs/nose/pull/699
197if 'TEMPEST_PY26_NOSE_COMPAT' in os.environ:
198 try:
199 import unittest.case.SkipTest
200 # convince pep8 we're using the import...
201 if unittest.case.SkipTest:
202 pass
203 raise RuntimeError("You have unittest.case.SkipTest; "
204 "no need to override")
205 except ImportError:
206 LOG.info("Overriding skipException to nose SkipTest")
207 testtools.TestCase.skipException = nose.plugins.skip.SkipTest
208
Attila Fazekasf86fa312013-07-30 19:56:39 +0200209at_exit_set = set()
210
211
212def validate_tearDownClass():
213 if at_exit_set:
Alex Gaynor94560d42013-08-23 05:41:23 -0700214 raise RuntimeError("tearDownClass does not calls the super's "
Attila Fazekasf86fa312013-07-30 19:56:39 +0200215 "tearDownClass in these classes: "
Attila Fazekasd5d43b82013-10-09 16:02:19 +0200216 + str(at_exit_set) + "\n"
217 "If you see the exception, with another "
218 "exception please do not report this one!"
219 "If you are changing tempest code, make sure you",
220 "are calling the super class's tearDownClass!")
Attila Fazekasf86fa312013-07-30 19:56:39 +0200221
222atexit.register(validate_tearDownClass)
223
Ian Wienand98c35f32013-07-23 20:34:23 +1000224
Attila Fazekasdc216422013-01-29 15:12:14 +0100225class BaseTestCase(testtools.TestCase,
226 testtools.testcase.WithAttributes,
227 testresources.ResourcedTestCase):
Attila Fazekasc43fec82013-04-09 23:17:52 +0200228
Sean Dague86bd8422013-12-20 09:56:44 -0500229 config = CONF
Attila Fazekasdc216422013-01-29 15:12:14 +0100230
Attila Fazekasf86fa312013-07-30 19:56:39 +0200231 setUpClassCalled = False
232
Matthew Treinish9f756a02014-01-15 10:26:07 -0500233 network_resources = {}
234
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200235 @classmethod
236 def setUpClass(cls):
237 if hasattr(super(BaseTestCase, cls), 'setUpClass'):
238 super(BaseTestCase, cls).setUpClass()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200239 cls.setUpClassCalled = True
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200240
Attila Fazekasf86fa312013-07-30 19:56:39 +0200241 @classmethod
242 def tearDownClass(cls):
Attila Fazekas5d275302013-08-29 12:35:12 +0200243 at_exit_set.discard(cls)
Attila Fazekasf86fa312013-07-30 19:56:39 +0200244 if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
245 super(BaseTestCase, cls).tearDownClass()
246
247 def setUp(self):
248 super(BaseTestCase, self).setUp()
249 if not self.setUpClassCalled:
250 raise RuntimeError("setUpClass does not calls the super's"
251 "setUpClass in the "
252 + self.__class__.__name__)
253 at_exit_set.add(self.__class__)
Matthew Treinish78561ad2013-07-26 11:41:56 -0400254 test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
255 try:
256 test_timeout = int(test_timeout)
257 except ValueError:
258 test_timeout = 0
259 if test_timeout > 0:
Attila Fazekasf86fa312013-07-30 19:56:39 +0200260 self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400261
262 if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
263 os.environ.get('OS_STDOUT_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200264 stdout = self.useFixture(fixtures.StringStream('stdout')).stream
265 self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400266 if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
267 os.environ.get('OS_STDERR_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200268 stderr = self.useFixture(fixtures.StringStream('stderr')).stream
269 self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
Attila Fazekas31388072013-08-15 08:58:07 +0200270 if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
271 os.environ.get('OS_LOG_CAPTURE') != '0'):
272 log_format = '%(asctime)-15s %(message)s'
273 self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
Attila Fazekas90445be2013-10-24 16:46:03 +0200274 format=log_format,
275 level=None))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400276
Matthew Treinish3e046852013-07-23 16:00:24 -0400277 @classmethod
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700278 def get_client_manager(cls):
279 """
280 Returns an Openstack client manager
281 """
Matthew Treinish9f756a02014-01-15 10:26:07 -0500282 cls.isolated_creds = isolated_creds.IsolatedCreds(
283 cls.__name__, network_resources=cls.network_resources)
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700284
285 force_tenant_isolation = getattr(cls, 'force_tenant_isolation', None)
286 if (cls.config.compute.allow_tenant_isolation or
287 force_tenant_isolation):
288 creds = cls.isolated_creds.get_primary_creds()
289 username, tenant_name, password = creds
290 os = clients.Manager(username=username,
291 password=password,
292 tenant_name=tenant_name,
293 interface=cls._interface)
294 else:
295 os = clients.Manager(interface=cls._interface)
296 return os
297
298 @classmethod
299 def clear_isolated_creds(cls):
300 """
301 Clears isolated creds if set
302 """
303 if getattr(cls, 'isolated_creds'):
304 cls.isolated_creds.clear_isolated_creds()
305
306 @classmethod
Matthew Treinish3e046852013-07-23 16:00:24 -0400307 def _get_identity_admin_client(cls):
308 """
309 Returns an instance of the Identity Admin API client
310 """
311 os = clients.AdminManager(interface=cls._interface)
312 admin_client = os.identity_client
313 return admin_client
314
315 @classmethod
316 def _get_client_args(cls):
317
318 return (
319 cls.config,
320 cls.config.identity.admin_username,
321 cls.config.identity.admin_password,
322 cls.config.identity.uri
323 )
324
Matthew Treinish9f756a02014-01-15 10:26:07 -0500325 @classmethod
326 def set_network_resources(self, network=False, router=False, subnet=False,
327 dhcp=False):
328 """Specify which network resources should be created
329
330 @param network
331 @param router
332 @param subnet
333 @param dhcp
334 """
335 self.network_resources = {
336 'network': network,
337 'router': router,
338 'subnet': subnet,
339 'dhcp': dhcp,
340 }
341
Attila Fazekasdc216422013-01-29 15:12:14 +0100342
Sean Dague35a7caf2013-05-10 10:38:22 -0400343def call_until_true(func, duration, sleep_for):
344 """
345 Call the given function until it returns True (and return True) or
346 until the specified duration (in seconds) elapses (and return
347 False).
348
349 :param func: A zero argument callable that returns True on success.
350 :param duration: The number of seconds for which to attempt a
351 successful call of the function.
352 :param sleep_for: The number of seconds to sleep after an unsuccessful
353 invocation of the function.
354 """
355 now = time.time()
356 timeout = now + duration
357 while now < timeout:
358 if func():
359 return True
360 LOG.debug("Sleeping for %d seconds", sleep_for)
361 time.sleep(sleep_for)
362 now = time.time()
363 return False