blob: ccb225106bb68c41203bb5dbff2765e2fb2ecb44 [file] [log] [blame]
Jay Pipes051075a2012-04-28 17:39:37 -04001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2012 OpenStack, LLC
4# 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
18import logging
19import time
20
Chris Yeoh55530bb2013-02-08 16:04:27 +103021import nose.plugins.attrib
Attila Fazekasdc216422013-01-29 15:12:14 +010022import testresources
ivan-zhu1feeb382013-01-24 10:14:39 +080023import testtools
Jay Pipes051075a2012-04-28 17:39:37 -040024
Attila Fazekasdc216422013-01-29 15:12:14 +010025from tempest import config
Jay Pipes051075a2012-04-28 17:39:37 -040026from tempest import manager
27
28LOG = logging.getLogger(__name__)
29
30
Chris Yeoh55530bb2013-02-08 16:04:27 +103031def attr(*args, **kwargs):
32 """A decorator which applies the nose and testtools attr decorator
33
34 This decorator applies the nose attr decorator as well as the
35 the testtools.testcase.attr if it is in the list of attributes
Attila Fazekasb2902af2013-02-16 16:22:44 +010036 to testtools we want to apply.
37 """
Chris Yeoh55530bb2013-02-08 16:04:27 +103038
39 def decorator(f):
40 testtool_attributes = ('smoke')
41
42 if 'type' in kwargs and kwargs['type'] in testtool_attributes:
43 return nose.plugins.attrib.attr(*args, **kwargs)(
44 testtools.testcase.attr(kwargs['type'])(f))
45 else:
46 return nose.plugins.attrib.attr(*args, **kwargs)(f)
47
48 return decorator
49
50
Attila Fazekasdc216422013-01-29 15:12:14 +010051class BaseTestCase(testtools.TestCase,
52 testtools.testcase.WithAttributes,
53 testresources.ResourcedTestCase):
54 def __init__(self, *args, **kwargs):
55 super(BaseTestCase, self).__init__(*args, **kwargs)
56 #NOTE(afazekas): inspection workaround
57 BaseTestCase.config = config.TempestConfig()
58
59
60class TestCase(BaseTestCase):
Pavel Sedláka2b757c2013-02-25 18:16:04 +010061 """Base test case class for all Tempest tests
Jay Pipes051075a2012-04-28 17:39:37 -040062
63 Contains basic setup and convenience methods
64 """
Pavel Sedláka2b757c2013-02-25 18:16:04 +010065
Jay Pipes051075a2012-04-28 17:39:37 -040066 manager_class = None
67
68 @classmethod
69 def setUpClass(cls):
70 cls.manager = cls.manager_class()
Maru Newbydec13ec2012-08-30 11:19:17 -070071 for attr_name in cls.manager.client_attr_names:
72 # Ensure that pre-existing class attributes won't be
73 # accidentally overriden.
74 assert not hasattr(cls, attr_name)
75 client = getattr(cls.manager, attr_name)
76 setattr(cls, attr_name, client)
Jay Pipes051075a2012-04-28 17:39:37 -040077 cls.resource_keys = {}
Attila Fazekasdc216422013-01-29 15:12:14 +010078 cls.os_resources = []
Jay Pipes051075a2012-04-28 17:39:37 -040079
80 def set_resource(self, key, thing):
81 LOG.debug("Adding %r to shared resources of %s" %
82 (thing, self.__class__.__name__))
83 self.resource_keys[key] = thing
Attila Fazekasdc216422013-01-29 15:12:14 +010084 self.os_resources.append(thing)
Jay Pipes051075a2012-04-28 17:39:37 -040085
86 def get_resource(self, key):
87 return self.resource_keys[key]
88
89 def remove_resource(self, key):
90 thing = self.resource_keys[key]
Attila Fazekasdc216422013-01-29 15:12:14 +010091 self.os_resources.remove(thing)
Jay Pipes051075a2012-04-28 17:39:37 -040092 del self.resource_keys[key]
93
94
Maru Newbyd3bf0aa2012-09-05 20:01:43 -070095def call_until_true(func, duration, sleep_for):
96 """
97 Call the given function until it returns True (and return True) or
98 until the specified duration (in seconds) elapses (and return
99 False).
100
101 :param func: A zero argument callable that returns True on success.
102 :param duration: The number of seconds for which to attempt a successful
103 call of the function.
104 :param sleep_for: The number of seconds to sleep after an unsuccessful
105 invocation of the function.
106 """
107 now = time.time()
108 timeout = now + duration
109 while now < timeout:
110 if func():
111 return True
112 LOG.debug("Sleeping for %d seconds", sleep_for)
113 time.sleep(sleep_for)
114 now = time.time()
115 return False
116
117
Matthew Treinishfbf40fd2013-04-09 11:10:58 -0400118def status_timeout(things, thing_id, expected_status):
119 """
120 Given a thing and an expected status, do a loop, sleeping
121 for a configurable amount of time, checking for the
122 expected status to show. At any time, if the returned
123 status of the thing is ERROR, fail out.
124 """
125 def check_status():
126 # python-novaclient has resources available to its client
127 # that all implement a get() method taking an identifier
128 # for the singular resource to retrieve.
129 thing = things.get(thing_id)
130 new_status = thing.status
131 if new_status == 'ERROR':
132 self.fail("%s failed to get to expected status."
133 "In ERROR state."
134 % thing)
135 elif new_status == expected_status:
136 return True # All good.
137 LOG.debug("Waiting for %s to get to %s status. "
138 "Currently in %s status",
139 thing, expected_status, new_status)
140 conf = config.TempestConfig()
141 if not call_until_true(check_status,
142 conf.compute.build_timeout,
143 conf.compute.build_interval):
144 self.fail("Timed out waiting for thing %s to become %s"
145 % (thing_id, expected_status))
146
147
148class DefaultClientSmokeTest(TestCase):
Jay Pipes051075a2012-04-28 17:39:37 -0400149
150 """
Matthew Treinishfbf40fd2013-04-09 11:10:58 -0400151 Base smoke test case class that provides the default clients to
152 access the various OpenStack APIs.
153
154 Smoke tests are tests that have the following characteristics:
155
156 * Test basic operations of an API, typically in an order that
157 a regular user would perform those operations
158 * Test only the correct inputs and action paths -- no fuzz or
159 random input data is sent, only valid inputs.
160 * Use only the default client tool for calling an API
Jay Pipes051075a2012-04-28 17:39:37 -0400161 """
162
Maru Newbydec13ec2012-08-30 11:19:17 -0700163 manager_class = manager.DefaultClientManager
Jay Pipes051075a2012-04-28 17:39:37 -0400164
Matthew Treinishfbf40fd2013-04-09 11:10:58 -0400165 @classmethod
166 def tearDownClass(cls):
167 # NOTE(jaypipes): Because smoke tests are typically run in a specific
168 # order, and because test methods in smoke tests generally create
169 # resources in a particular order, we destroy resources in the reverse
170 # order in which resources are added to the smoke test class object
171 while cls.os_resources:
172 thing = cls.os_resources.pop()
173 LOG.debug("Deleting %r from shared resources of %s" %
174 (thing, cls.__name__))
175
176 try:
177 # OpenStack resources are assumed to have a delete()
178 # method which destroys the resource...
179 thing.delete()
180 except Exception as e:
181 # If the resource is already missing, mission accomplished.
182 if e.__class__.__name__ == 'NotFound':
183 continue
184 raise
185
186 def is_deletion_complete():
187 # Deletion testing is only required for objects whose
188 # existence cannot be checked via retrieval.
189 if isinstance(thing, dict):
190 return True
191 try:
192 thing.get()
193 except Exception as e:
194 # Clients are expected to return an exception
195 # called 'NotFound' if retrieval fails.
196 if e.__class__.__name__ == 'NotFound':
197 return True
198 raise
199 return False
200
201 # Block until resource deletion has completed or timed-out
202 call_until_true(is_deletion_complete, 10, 1)
Jay Pipes051075a2012-04-28 17:39:37 -0400203
204
205class ComputeFuzzClientTest(TestCase):
206
207 """
208 Base test case class for OpenStack Compute API (Nova)
209 that uses the Tempest REST fuzz client libs for calling the API.
210 """
211
212 manager_class = manager.ComputeFuzzClientManager