blob: 1a3ca0dc1771753351af4af04658c36442989bb0 [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
Jay Pipes051075a2012-04-28 17:39:37 -040019import time
20
Matthew Treinish78561ad2013-07-26 11:41:56 -040021import fixtures
Chris Yeoh55530bb2013-02-08 16:04:27 +103022import nose.plugins.attrib
Attila Fazekasdc216422013-01-29 15:12:14 +010023import testresources
ivan-zhu1feeb382013-01-24 10:14:39 +080024import testtools
Jay Pipes051075a2012-04-28 17:39:37 -040025
Matthew Treinish3e046852013-07-23 16:00:24 -040026from tempest import clients
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -070027from tempest.common import isolated_creds
Attila Fazekasdc216422013-01-29 15:12:14 +010028from tempest import config
Matthew Treinish16c43792013-09-09 19:55:23 +000029from tempest import exceptions
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -040030from tempest.openstack.common import log as logging
Jay Pipes051075a2012-04-28 17:39:37 -040031
32LOG = logging.getLogger(__name__)
33
Sean Dague86bd8422013-12-20 09:56:44 -050034CONF = config.CONF
35
Samuel Merritt0d499bc2013-06-19 12:08:23 -070036# All the successful HTTP status codes from RFC 2616
37HTTP_SUCCESS = (200, 201, 202, 203, 204, 205, 206)
38
Jay Pipes051075a2012-04-28 17:39:37 -040039
Chris Yeoh55530bb2013-02-08 16:04:27 +103040def attr(*args, **kwargs):
41 """A decorator which applies the nose and testtools attr decorator
42
43 This decorator applies the nose attr decorator as well as the
44 the testtools.testcase.attr if it is in the list of attributes
Attila Fazekasb2902af2013-02-16 16:22:44 +010045 to testtools we want to apply.
46 """
Chris Yeoh55530bb2013-02-08 16:04:27 +103047
48 def decorator(f):
Giulio Fidente4946a052013-05-14 12:23:51 +020049 if 'type' in kwargs and isinstance(kwargs['type'], str):
50 f = testtools.testcase.attr(kwargs['type'])(f)
Chris Yeohcf3fb7c2013-05-19 15:59:00 +093051 if kwargs['type'] == 'smoke':
52 f = testtools.testcase.attr('gate')(f)
Giulio Fidente4946a052013-05-14 12:23:51 +020053 elif 'type' in kwargs and isinstance(kwargs['type'], list):
54 for attr in kwargs['type']:
55 f = testtools.testcase.attr(attr)(f)
Chris Yeohcf3fb7c2013-05-19 15:59:00 +093056 if attr == 'smoke':
57 f = testtools.testcase.attr('gate')(f)
Giulio Fidente4946a052013-05-14 12:23:51 +020058 return nose.plugins.attrib.attr(*args, **kwargs)(f)
Chris Yeoh55530bb2013-02-08 16:04:27 +103059
60 return decorator
61
62
Matthew Treinish16c43792013-09-09 19:55:23 +000063def services(*args, **kwargs):
64 """A decorator used to set an attr for each service used in a test case
65
66 This decorator applies a testtools attr for each service that gets
67 exercised by a test case.
68 """
69 valid_service_list = ['compute', 'image', 'volume', 'orchestration',
Matthew Treinish03c4f772014-02-02 13:37:44 -050070 'network', 'identity', 'object_storage', 'dashboard']
Matthew Treinish16c43792013-09-09 19:55:23 +000071
72 def decorator(f):
73 for service in args:
74 if service not in valid_service_list:
75 raise exceptions.InvalidServiceTag('%s is not a valid service'
76 % service)
77 attr(type=list(args))(f)
78 return f
79 return decorator
80
81
Marc Koderer32221b8e2013-08-23 13:57:50 +020082def stresstest(*args, **kwargs):
83 """Add stress test decorator
84
85 For all functions with this decorator a attr stress will be
86 set automatically.
87
88 @param class_setup_per: allowed values are application, process, action
89 ``application``: once in the stress job lifetime
90 ``process``: once in the worker process lifetime
91 ``action``: on each action
Marc Kodererb0604412013-09-02 09:43:40 +020092 @param allow_inheritance: allows inheritance of this attribute
Marc Koderer32221b8e2013-08-23 13:57:50 +020093 """
94 def decorator(f):
95 if 'class_setup_per' in kwargs:
96 setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
97 else:
98 setattr(f, "st_class_setup_per", 'process')
Marc Kodererb0604412013-09-02 09:43:40 +020099 if 'allow_inheritance' in kwargs:
100 setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
101 else:
102 setattr(f, "st_allow_inheritance", False)
Marc Koderer32221b8e2013-08-23 13:57:50 +0200103 attr(type='stress')(f)
104 return f
105 return decorator
106
107
Giulio Fidente83181a92013-10-01 06:02:24 +0200108def skip_because(*args, **kwargs):
109 """A decorator useful to skip tests hitting known bugs
110
111 @param bug: bug number causing the test to skip
112 @param condition: optional condition to be True for the skip to have place
Ken'ichi Ohmichia1aa44c2013-12-06 20:48:24 +0900113 @param interface: skip the test if it is the same as self._interface
Giulio Fidente83181a92013-10-01 06:02:24 +0200114 """
115 def decorator(f):
Masayuki Igawa80c1b9f2013-10-07 17:19:11 +0900116 @functools.wraps(f)
Ken'ichi Ohmichia1aa44c2013-12-06 20:48:24 +0900117 def wrapper(self, *func_args, **func_kwargs):
118 skip = False
119 if "condition" in kwargs:
120 if kwargs["condition"] is True:
121 skip = True
122 elif "interface" in kwargs:
123 if kwargs["interface"] == self._interface:
124 skip = True
125 else:
126 skip = True
127 if "bug" in kwargs and skip is True:
128 msg = "Skipped until Bug: %s is resolved." % kwargs["bug"]
129 raise testtools.TestCase.skipException(msg)
130 return f(self, *func_args, **func_kwargs)
Masayuki Igawa80c1b9f2013-10-07 17:19:11 +0900131 return wrapper
Giulio Fidente83181a92013-10-01 06:02:24 +0200132 return decorator
133
134
Matthew Treinishe3d26142013-11-26 19:14:58 +0000135def requires_ext(*args, **kwargs):
136 """A decorator to skip tests if an extension is not enabled
137
138 @param extension
139 @param service
140 """
141 def decorator(func):
142 @functools.wraps(func)
143 def wrapper(*func_args, **func_kwargs):
144 if not is_extension_enabled(kwargs['extension'],
145 kwargs['service']):
146 msg = "Skipped because %s extension: %s is not enabled" % (
147 kwargs['service'], kwargs['extension'])
148 raise testtools.TestCase.skipException(msg)
149 return func(*func_args, **func_kwargs)
150 return wrapper
151 return decorator
152
153
154def is_extension_enabled(extension_name, service):
155 """A function that will check the list of enabled extensions from config
156
157 """
Matthew Treinishe3d26142013-11-26 19:14:58 +0000158 config_dict = {
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000159 'compute': CONF.compute_feature_enabled.api_extensions,
160 'compute_v3': CONF.compute_feature_enabled.api_v3_extensions,
161 'volume': CONF.volume_feature_enabled.api_extensions,
162 'network': CONF.network_feature_enabled.api_extensions,
163 'object': CONF.object_storage_feature_enabled.discoverable_apis,
Matthew Treinishe3d26142013-11-26 19:14:58 +0000164 }
165 if config_dict[service][0] == 'all':
166 return True
167 if extension_name in config_dict[service]:
168 return True
169 return False
170
Ian Wienand98c35f32013-07-23 20:34:23 +1000171# there is a mis-match between nose and testtools for older pythons.
172# testtools will set skipException to be either
173# unittest.case.SkipTest, unittest2.case.SkipTest or an internal skip
174# exception, depending on what it can find. Python <2.7 doesn't have
175# unittest.case.SkipTest; so if unittest2 is not installed it falls
176# back to the internal class.
177#
178# The current nose skip plugin will decide to raise either
179# unittest.case.SkipTest or its own internal exception; it does not
180# look for unittest2 or the internal unittest exception. Thus we must
181# monkey-patch testtools.TestCase.skipException to be the exception
182# the nose skip plugin expects.
183#
184# However, with the switch to testr nose may not be available, so we
185# require you to opt-in to this fix with an environment variable.
186#
187# This is temporary until upstream nose starts looking for unittest2
188# as testtools does; we can then remove this and ensure unittest2 is
189# available for older pythons; then nose and testtools will agree
190# unittest2.case.SkipTest is the one-true skip test exception.
191#
192# https://review.openstack.org/#/c/33056
193# https://github.com/nose-devs/nose/pull/699
194if 'TEMPEST_PY26_NOSE_COMPAT' in os.environ:
195 try:
196 import unittest.case.SkipTest
197 # convince pep8 we're using the import...
198 if unittest.case.SkipTest:
199 pass
200 raise RuntimeError("You have unittest.case.SkipTest; "
201 "no need to override")
202 except ImportError:
203 LOG.info("Overriding skipException to nose SkipTest")
204 testtools.TestCase.skipException = nose.plugins.skip.SkipTest
205
Attila Fazekasf86fa312013-07-30 19:56:39 +0200206at_exit_set = set()
207
208
209def validate_tearDownClass():
210 if at_exit_set:
Vladislav Kuzmin0df88bb2014-01-28 14:58:48 +0400211 raise RuntimeError("tearDownClass does not call the super's "
Attila Fazekasf86fa312013-07-30 19:56:39 +0200212 "tearDownClass in these classes: "
Attila Fazekasd5d43b82013-10-09 16:02:19 +0200213 + str(at_exit_set) + "\n"
214 "If you see the exception, with another "
Vladislav Kuzmin0df88bb2014-01-28 14:58:48 +0400215 "exception please do not report this one! "
216 "If you are changing tempest code, make sure you "
Attila Fazekasd5d43b82013-10-09 16:02:19 +0200217 "are calling the super class's tearDownClass!")
Attila Fazekasf86fa312013-07-30 19:56:39 +0200218
219atexit.register(validate_tearDownClass)
220
Ian Wienand98c35f32013-07-23 20:34:23 +1000221
Attila Fazekasdc216422013-01-29 15:12:14 +0100222class BaseTestCase(testtools.TestCase,
223 testtools.testcase.WithAttributes,
224 testresources.ResourcedTestCase):
Attila Fazekasc43fec82013-04-09 23:17:52 +0200225
Attila Fazekasf86fa312013-07-30 19:56:39 +0200226 setUpClassCalled = False
227
Matthew Treinish9f756a02014-01-15 10:26:07 -0500228 network_resources = {}
229
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200230 @classmethod
231 def setUpClass(cls):
232 if hasattr(super(BaseTestCase, cls), 'setUpClass'):
233 super(BaseTestCase, cls).setUpClass()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200234 cls.setUpClassCalled = True
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200235
Attila Fazekasf86fa312013-07-30 19:56:39 +0200236 @classmethod
237 def tearDownClass(cls):
Attila Fazekas5d275302013-08-29 12:35:12 +0200238 at_exit_set.discard(cls)
Attila Fazekasf86fa312013-07-30 19:56:39 +0200239 if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
240 super(BaseTestCase, cls).tearDownClass()
241
242 def setUp(self):
243 super(BaseTestCase, self).setUp()
244 if not self.setUpClassCalled:
245 raise RuntimeError("setUpClass does not calls the super's"
246 "setUpClass in the "
247 + self.__class__.__name__)
248 at_exit_set.add(self.__class__)
Matthew Treinish78561ad2013-07-26 11:41:56 -0400249 test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
250 try:
251 test_timeout = int(test_timeout)
252 except ValueError:
253 test_timeout = 0
254 if test_timeout > 0:
Attila Fazekasf86fa312013-07-30 19:56:39 +0200255 self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400256
257 if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
258 os.environ.get('OS_STDOUT_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200259 stdout = self.useFixture(fixtures.StringStream('stdout')).stream
260 self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400261 if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
262 os.environ.get('OS_STDERR_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200263 stderr = self.useFixture(fixtures.StringStream('stderr')).stream
264 self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
Attila Fazekas31388072013-08-15 08:58:07 +0200265 if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
266 os.environ.get('OS_LOG_CAPTURE') != '0'):
267 log_format = '%(asctime)-15s %(message)s'
268 self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
Attila Fazekas90445be2013-10-24 16:46:03 +0200269 format=log_format,
270 level=None))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400271
Matthew Treinish3e046852013-07-23 16:00:24 -0400272 @classmethod
Matthew Treinish8004e8c2014-01-27 23:03:14 +0000273 def get_client_manager(cls, interface=None):
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700274 """
275 Returns an Openstack client manager
276 """
Matthew Treinish9f756a02014-01-15 10:26:07 -0500277 cls.isolated_creds = isolated_creds.IsolatedCreds(
278 cls.__name__, network_resources=cls.network_resources)
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700279
280 force_tenant_isolation = getattr(cls, 'force_tenant_isolation', None)
Matthew Treinishbc0e03e2014-01-30 16:51:06 +0000281 if (CONF.compute.allow_tenant_isolation or
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700282 force_tenant_isolation):
283 creds = cls.isolated_creds.get_primary_creds()
284 username, tenant_name, password = creds
Matthew Treinish8004e8c2014-01-27 23:03:14 +0000285 if getattr(cls, '_interface', None):
286 os = clients.Manager(username=username,
287 password=password,
288 tenant_name=tenant_name,
289 interface=cls._interface)
290 elif interface:
291 os = clients.Manager(username=username,
292 password=password,
293 tenant_name=tenant_name,
294 interface=interface)
295 else:
296 os = clients.Manager(username=username,
297 password=password,
298 tenant_name=tenant_name)
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700299 else:
Matthew Treinish8004e8c2014-01-27 23:03:14 +0000300 if getattr(cls, '_interface', None):
301 os = clients.Manager(interface=cls._interface)
302 elif interface:
303 os = clients.Manager(interface=interface)
304 else:
305 os = clients.Manager()
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700306 return os
307
308 @classmethod
309 def clear_isolated_creds(cls):
310 """
311 Clears isolated creds if set
312 """
313 if getattr(cls, 'isolated_creds'):
314 cls.isolated_creds.clear_isolated_creds()
315
316 @classmethod
Matthew Treinish3e046852013-07-23 16:00:24 -0400317 def _get_identity_admin_client(cls):
318 """
319 Returns an instance of the Identity Admin API client
320 """
321 os = clients.AdminManager(interface=cls._interface)
322 admin_client = os.identity_client
323 return admin_client
324
325 @classmethod
Matthew Treinish9f756a02014-01-15 10:26:07 -0500326 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 """
Salvatore Orlando5a337242014-01-15 22:49:22 +0000335 # network resources should be set only once from callers
336 # in order to ensure that even if it's called multiple times in
337 # a chain of overloaded methods, the attribute is set only
338 # in the leaf class
339 if not self.network_resources:
340 self.network_resources = {
341 'network': network,
342 'router': router,
343 'subnet': subnet,
344 'dhcp': dhcp}
Matthew Treinish9f756a02014-01-15 10:26:07 -0500345
Attila Fazekasdc216422013-01-29 15:12:14 +0100346
Sean Dague35a7caf2013-05-10 10:38:22 -0400347def call_until_true(func, duration, sleep_for):
348 """
349 Call the given function until it returns True (and return True) or
350 until the specified duration (in seconds) elapses (and return
351 False).
352
353 :param func: A zero argument callable that returns True on success.
354 :param duration: The number of seconds for which to attempt a
355 successful call of the function.
356 :param sleep_for: The number of seconds to sleep after an unsuccessful
357 invocation of the function.
358 """
359 now = time.time()
360 timeout = now + duration
361 while now < timeout:
362 if func():
363 return True
364 LOG.debug("Sleeping for %d seconds", sleep_for)
365 time.sleep(sleep_for)
366 now = time.time()
367 return False