blob: ceb2c8076f617975d104c7cfd3df5aaa428fa2c0 [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
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',
70 'network', 'identity', 'object', 'dashboard']
71
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
113 """
114 def decorator(f):
Masayuki Igawa80c1b9f2013-10-07 17:19:11 +0900115 @functools.wraps(f)
116 def wrapper(*func_args, **func_kwargs):
117 if "bug" in kwargs:
118 if "condition" not in kwargs or kwargs["condition"] is True:
119 msg = "Skipped until Bug: %s is resolved." % kwargs["bug"]
120 raise testtools.TestCase.skipException(msg)
121 return f(*func_args, **func_kwargs)
122 return wrapper
Giulio Fidente83181a92013-10-01 06:02:24 +0200123 return decorator
124
125
Matthew Treinishe3d26142013-11-26 19:14:58 +0000126def requires_ext(*args, **kwargs):
127 """A decorator to skip tests if an extension is not enabled
128
129 @param extension
130 @param service
131 """
132 def decorator(func):
133 @functools.wraps(func)
134 def wrapper(*func_args, **func_kwargs):
135 if not is_extension_enabled(kwargs['extension'],
136 kwargs['service']):
137 msg = "Skipped because %s extension: %s is not enabled" % (
138 kwargs['service'], kwargs['extension'])
139 raise testtools.TestCase.skipException(msg)
140 return func(*func_args, **func_kwargs)
141 return wrapper
142 return decorator
143
144
145def is_extension_enabled(extension_name, service):
146 """A function that will check the list of enabled extensions from config
147
148 """
149 configs = config.TempestConfig()
150 config_dict = {
151 'compute': configs.compute_feature_enabled.api_extensions,
152 'compute_v3': configs.compute_feature_enabled.api_v3_extensions,
153 'volume': configs.volume_feature_enabled.api_extensions,
154 'network': configs.network_feature_enabled.api_extensions,
155 }
156 if config_dict[service][0] == 'all':
157 return True
158 if extension_name in config_dict[service]:
159 return True
160 return False
161
Ian Wienand98c35f32013-07-23 20:34:23 +1000162# there is a mis-match between nose and testtools for older pythons.
163# testtools will set skipException to be either
164# unittest.case.SkipTest, unittest2.case.SkipTest or an internal skip
165# exception, depending on what it can find. Python <2.7 doesn't have
166# unittest.case.SkipTest; so if unittest2 is not installed it falls
167# back to the internal class.
168#
169# The current nose skip plugin will decide to raise either
170# unittest.case.SkipTest or its own internal exception; it does not
171# look for unittest2 or the internal unittest exception. Thus we must
172# monkey-patch testtools.TestCase.skipException to be the exception
173# the nose skip plugin expects.
174#
175# However, with the switch to testr nose may not be available, so we
176# require you to opt-in to this fix with an environment variable.
177#
178# This is temporary until upstream nose starts looking for unittest2
179# as testtools does; we can then remove this and ensure unittest2 is
180# available for older pythons; then nose and testtools will agree
181# unittest2.case.SkipTest is the one-true skip test exception.
182#
183# https://review.openstack.org/#/c/33056
184# https://github.com/nose-devs/nose/pull/699
185if 'TEMPEST_PY26_NOSE_COMPAT' in os.environ:
186 try:
187 import unittest.case.SkipTest
188 # convince pep8 we're using the import...
189 if unittest.case.SkipTest:
190 pass
191 raise RuntimeError("You have unittest.case.SkipTest; "
192 "no need to override")
193 except ImportError:
194 LOG.info("Overriding skipException to nose SkipTest")
195 testtools.TestCase.skipException = nose.plugins.skip.SkipTest
196
Attila Fazekasf86fa312013-07-30 19:56:39 +0200197at_exit_set = set()
198
199
200def validate_tearDownClass():
201 if at_exit_set:
Alex Gaynor94560d42013-08-23 05:41:23 -0700202 raise RuntimeError("tearDownClass does not calls the super's "
Attila Fazekasf86fa312013-07-30 19:56:39 +0200203 "tearDownClass in these classes: "
Attila Fazekasd5d43b82013-10-09 16:02:19 +0200204 + str(at_exit_set) + "\n"
205 "If you see the exception, with another "
206 "exception please do not report this one!"
207 "If you are changing tempest code, make sure you",
208 "are calling the super class's tearDownClass!")
Attila Fazekasf86fa312013-07-30 19:56:39 +0200209
210atexit.register(validate_tearDownClass)
211
Ian Wienand98c35f32013-07-23 20:34:23 +1000212
Attila Fazekasdc216422013-01-29 15:12:14 +0100213class BaseTestCase(testtools.TestCase,
214 testtools.testcase.WithAttributes,
215 testresources.ResourcedTestCase):
Attila Fazekasc43fec82013-04-09 23:17:52 +0200216
217 config = config.TempestConfig()
Attila Fazekasdc216422013-01-29 15:12:14 +0100218
Attila Fazekasf86fa312013-07-30 19:56:39 +0200219 setUpClassCalled = False
220
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200221 @classmethod
222 def setUpClass(cls):
223 if hasattr(super(BaseTestCase, cls), 'setUpClass'):
224 super(BaseTestCase, cls).setUpClass()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200225 cls.setUpClassCalled = True
Pavel Sedlák1053bd32013-04-16 16:47:40 +0200226
Attila Fazekasf86fa312013-07-30 19:56:39 +0200227 @classmethod
228 def tearDownClass(cls):
Attila Fazekas5d275302013-08-29 12:35:12 +0200229 at_exit_set.discard(cls)
Attila Fazekasf86fa312013-07-30 19:56:39 +0200230 if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
231 super(BaseTestCase, cls).tearDownClass()
232
233 def setUp(self):
234 super(BaseTestCase, self).setUp()
235 if not self.setUpClassCalled:
236 raise RuntimeError("setUpClass does not calls the super's"
237 "setUpClass in the "
238 + self.__class__.__name__)
239 at_exit_set.add(self.__class__)
Matthew Treinish78561ad2013-07-26 11:41:56 -0400240 test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
241 try:
242 test_timeout = int(test_timeout)
243 except ValueError:
244 test_timeout = 0
245 if test_timeout > 0:
Attila Fazekasf86fa312013-07-30 19:56:39 +0200246 self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400247
248 if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
249 os.environ.get('OS_STDOUT_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200250 stdout = self.useFixture(fixtures.StringStream('stdout')).stream
251 self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400252 if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
253 os.environ.get('OS_STDERR_CAPTURE') == '1'):
Attila Fazekasf86fa312013-07-30 19:56:39 +0200254 stderr = self.useFixture(fixtures.StringStream('stderr')).stream
255 self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
Attila Fazekas31388072013-08-15 08:58:07 +0200256 if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
257 os.environ.get('OS_LOG_CAPTURE') != '0'):
258 log_format = '%(asctime)-15s %(message)s'
259 self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
Attila Fazekas90445be2013-10-24 16:46:03 +0200260 format=log_format,
261 level=None))
Matthew Treinish78561ad2013-07-26 11:41:56 -0400262
Matthew Treinish3e046852013-07-23 16:00:24 -0400263 @classmethod
Ryan Hsu6c4bb3d2013-10-21 21:22:50 -0700264 def get_client_manager(cls):
265 """
266 Returns an Openstack client manager
267 """
268 cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
269
270 force_tenant_isolation = getattr(cls, 'force_tenant_isolation', None)
271 if (cls.config.compute.allow_tenant_isolation or
272 force_tenant_isolation):
273 creds = cls.isolated_creds.get_primary_creds()
274 username, tenant_name, password = creds
275 os = clients.Manager(username=username,
276 password=password,
277 tenant_name=tenant_name,
278 interface=cls._interface)
279 else:
280 os = clients.Manager(interface=cls._interface)
281 return os
282
283 @classmethod
284 def clear_isolated_creds(cls):
285 """
286 Clears isolated creds if set
287 """
288 if getattr(cls, 'isolated_creds'):
289 cls.isolated_creds.clear_isolated_creds()
290
291 @classmethod
Matthew Treinish3e046852013-07-23 16:00:24 -0400292 def _get_identity_admin_client(cls):
293 """
294 Returns an instance of the Identity Admin API client
295 """
296 os = clients.AdminManager(interface=cls._interface)
297 admin_client = os.identity_client
298 return admin_client
299
300 @classmethod
301 def _get_client_args(cls):
302
303 return (
304 cls.config,
305 cls.config.identity.admin_username,
306 cls.config.identity.admin_password,
307 cls.config.identity.uri
308 )
309
Attila Fazekasdc216422013-01-29 15:12:14 +0100310
Sean Dague35a7caf2013-05-10 10:38:22 -0400311def call_until_true(func, duration, sleep_for):
312 """
313 Call the given function until it returns True (and return True) or
314 until the specified duration (in seconds) elapses (and return
315 False).
316
317 :param func: A zero argument callable that returns True on success.
318 :param duration: The number of seconds for which to attempt a
319 successful call of the function.
320 :param sleep_for: The number of seconds to sleep after an unsuccessful
321 invocation of the function.
322 """
323 now = time.time()
324 timeout = now + duration
325 while now < timeout:
326 if func():
327 return True
328 LOG.debug("Sleeping for %d seconds", sleep_for)
329 time.sleep(sleep_for)
330 now = time.time()
331 return False