blob: 90793ac83a2f333c7adf0e104a0b73dac825d09d [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):
Jay Pipes051075a2012-04-28 17:39:37 -040061
62 """
63 Base test case class for all Tempest tests
64
65 Contains basic setup and convenience methods
66 """
67 manager_class = None
68
69 @classmethod
70 def setUpClass(cls):
71 cls.manager = cls.manager_class()
Maru Newbydec13ec2012-08-30 11:19:17 -070072 for attr_name in cls.manager.client_attr_names:
73 # Ensure that pre-existing class attributes won't be
74 # accidentally overriden.
75 assert not hasattr(cls, attr_name)
76 client = getattr(cls.manager, attr_name)
77 setattr(cls, attr_name, client)
Jay Pipes051075a2012-04-28 17:39:37 -040078 cls.resource_keys = {}
Attila Fazekasdc216422013-01-29 15:12:14 +010079 cls.os_resources = []
Jay Pipes051075a2012-04-28 17:39:37 -040080
81 def set_resource(self, key, thing):
82 LOG.debug("Adding %r to shared resources of %s" %
83 (thing, self.__class__.__name__))
84 self.resource_keys[key] = thing
Attila Fazekasdc216422013-01-29 15:12:14 +010085 self.os_resources.append(thing)
Jay Pipes051075a2012-04-28 17:39:37 -040086
87 def get_resource(self, key):
88 return self.resource_keys[key]
89
90 def remove_resource(self, key):
91 thing = self.resource_keys[key]
Attila Fazekasdc216422013-01-29 15:12:14 +010092 self.os_resources.remove(thing)
Jay Pipes051075a2012-04-28 17:39:37 -040093 del self.resource_keys[key]
94
95
Maru Newbyd3bf0aa2012-09-05 20:01:43 -070096def call_until_true(func, duration, sleep_for):
97 """
98 Call the given function until it returns True (and return True) or
99 until the specified duration (in seconds) elapses (and return
100 False).
101
102 :param func: A zero argument callable that returns True on success.
103 :param duration: The number of seconds for which to attempt a successful
104 call of the function.
105 :param sleep_for: The number of seconds to sleep after an unsuccessful
106 invocation of the function.
107 """
108 now = time.time()
109 timeout = now + duration
110 while now < timeout:
111 if func():
112 return True
113 LOG.debug("Sleeping for %d seconds", sleep_for)
114 time.sleep(sleep_for)
115 now = time.time()
116 return False
117
118
Maru Newbydec13ec2012-08-30 11:19:17 -0700119class DefaultClientTest(TestCase):
Jay Pipes051075a2012-04-28 17:39:37 -0400120
121 """
Maru Newbydec13ec2012-08-30 11:19:17 -0700122 Base test case class that provides the default clients to access
123 the various OpenStack APIs.
Jay Pipes051075a2012-04-28 17:39:37 -0400124 """
125
Maru Newbydec13ec2012-08-30 11:19:17 -0700126 manager_class = manager.DefaultClientManager
Jay Pipes051075a2012-04-28 17:39:37 -0400127
128 def status_timeout(self, things, thing_id, expected_status):
129 """
130 Given a thing and an expected status, do a loop, sleeping
131 for a configurable amount of time, checking for the
132 expected status to show. At any time, if the returned
133 status of the thing is ERROR, fail out.
134 """
Maru Newbyd3bf0aa2012-09-05 20:01:43 -0700135 def check_status():
Jay Pipes051075a2012-04-28 17:39:37 -0400136 # python-novaclient has resources available to its client
137 # that all implement a get() method taking an identifier
138 # for the singular resource to retrieve.
139 thing = things.get(thing_id)
140 new_status = thing.status
141 if new_status == 'ERROR':
142 self.fail("%s failed to get to expected status."
143 "In ERROR state."
144 % thing)
145 elif new_status == expected_status:
Maru Newbyd3bf0aa2012-09-05 20:01:43 -0700146 return True # All good.
Jay Pipes051075a2012-04-28 17:39:37 -0400147 LOG.debug("Waiting for %s to get to %s status. "
148 "Currently in %s status",
149 thing, expected_status, new_status)
Maru Newbyd3bf0aa2012-09-05 20:01:43 -0700150 if not call_until_true(check_status,
151 self.config.compute.build_timeout,
152 self.config.compute.build_interval):
153 self.fail("Timed out waiting for thing %s to become %s"
154 % (thing_id, expected_status))
Jay Pipes051075a2012-04-28 17:39:37 -0400155
156
157class ComputeFuzzClientTest(TestCase):
158
159 """
160 Base test case class for OpenStack Compute API (Nova)
161 that uses the Tempest REST fuzz client libs for calling the API.
162 """
163
164 manager_class = manager.ComputeFuzzClientManager
165
166 def status_timeout(self, client_get_method, thing_id, expected_status):
167 """
168 Given a method to get a resource and an expected status, do a loop,
169 sleeping for a configurable amount of time, checking for the
170 expected status to show. At any time, if the returned
171 status of the thing is ERROR, fail out.
172
173 :param client_get_method: The callable that will retrieve the thing
174 with ID :param:thing_id
175 :param thing_id: The ID of the thing to get
176 :param expected_status: String value of the expected status of the
177 thing that we are looking for.
178
179 :code ..
180
181 Usage:
182
183 def test_some_server_action(self):
184 client = self.servers_client
185 resp, server = client.create_server('random_server')
186 self.status_timeout(client.get_server, server['id'], 'ACTIVE')
187 """
Maru Newbyd3bf0aa2012-09-05 20:01:43 -0700188 def check_status():
Jay Pipes051075a2012-04-28 17:39:37 -0400189 # Tempest REST client has resources available to its client
190 # that all implement a various get_$resource() methods taking
191 # an identifier for the singular resource to retrieve.
192 thing = client_get_method(thing_id)
193 new_status = thing['status']
194 if new_status == 'ERROR':
195 self.fail("%s failed to get to expected status."
196 "In ERROR state."
197 % thing)
198 elif new_status == expected_status:
Maru Newbyd3bf0aa2012-09-05 20:01:43 -0700199 return True # All good.
Jay Pipes051075a2012-04-28 17:39:37 -0400200 LOG.debug("Waiting for %s to get to %s status. "
201 "Currently in %s status",
202 thing, expected_status, new_status)
Maru Newbyd3bf0aa2012-09-05 20:01:43 -0700203 if not call_until_true(check_status,
204 self.config.compute.build_timeout,
205 self.config.compute.build_interval):
206 self.fail("Timed out waiting for thing %s to become %s"
207 % (thing_id, expected_status))