blob: a71bea215cca9e0172b178a334a861adc9db3d3e [file] [log] [blame]
David Kranz6308ec22012-02-22 09:36:48 -05001# Copyright 2011 Quanta Research Cambridge, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Defines various sub-classes of the `StressTestCase` and
15`PendingAction` class. The sub-classes of StressTestCase implement various
16API calls on the Nova cluster having to do with creating and deleting VMs.
17Each sub-class will have a corresponding PendingAction. These pending
18actions veriy that the API call was successful or not."""
19
20
21# system imports
22import random
23import time
24
25
26# local imports
27import test_case
28import pending_action
29from tempest.exceptions import TimeoutException
30
31
32class TestCreateVM(test_case.StressTestCase):
33 """Create a virtual machine in the Nova cluster."""
34 _vm_id = 0
35
36 def run(self, manager, state, *pargs, **kwargs):
37 """
38 Send an HTTP POST request to the nova cluster to build a
39 server. Update the state variable to track state of new server
40 and set to PENDING state.
41
42 `manager` : Manager object.
43 `state` : `State` object describing our view of state of cluster
44 `pargs` : positional arguments
45 `kwargs` : keyword arguments, which include:
46 `key_name` : name of keypair
47 `timeout` : how long to wait before issuing Exception
48 `image_ref` : index to image types availablexs
49 `flavor_ref`: index to flavor types available
50 (default = 1, which is tiny)
51 """
52
53 # restrict number of instances we can launch
54 if len(state.get_instances()) >= state.get_max_instances():
55 self._logger.debug("maximum number of instances created: %d" %
56 state.get_max_instances())
57 return None
58
59 _key_name = kwargs.get('key_name', '')
David Kranz30fe84a2012-03-20 16:25:47 -040060 _timeout = int(kwargs.get('timeout',
61 manager.config.compute.build_timeout))
David Kranz6308ec22012-02-22 09:36:48 -050062 _image_ref = kwargs.get('image_ref', manager.config.compute.image_ref)
63 _flavor_ref = kwargs.get('flavor_ref',
64 manager.config.compute.flavor_ref)
65
66 expected_server = {
67 'name': 'server' + str(TestCreateVM._vm_id),
68 'metadata': {
69 'key1': 'value1',
70 'key2': 'value2',
71 },
72 'imageRef': _image_ref,
73 'flavorRef': _flavor_ref,
74 'adminPass': 'testpwd',
75 'key_name': _key_name
76 }
77 TestCreateVM._vm_id = TestCreateVM._vm_id + 1
78 response, body = manager.servers_client.create_server(
79 expected_server['name'],
80 _image_ref,
81 _flavor_ref,
82 meta=expected_server['metadata'],
83 adminPass=expected_server['adminPass']
84 )
85
86 if (response.status != 202):
87 self._logger.error("response: %s" % response)
88 self._logger.error("body: %s" % body)
89 raise Exception
90
91 created_server = body
92
93 self._logger.info('setting machine %s to BUILD' %
94 created_server['id'])
95 state.set_instance_state(created_server['id'],
96 (created_server, 'BUILD'))
97
98 return VerifyCreateVM(manager,
99 state,
100 created_server,
101 expected_server)
102
103
104class VerifyCreateVM(pending_action.PendingAction):
105 """Verify that VM was built and is running"""
106 def __init__(self, manager,
107 state,
108 created_server,
109 expected_server):
110 super(VerifyCreateVM, self).__init__(manager,
111 state,
112 created_server,
113 )
114 self._expected = expected_server
115
116 def retry(self):
117 """
118 Check to see that the server was created and is running.
119 Update local view of state to indicate that it is running.
120 """
121 # don't run create verification
122 # if target machine has been deleted or is going to be deleted
123 if (self._target['id'] not in self._state.get_instances().keys() or
124 self._state.get_instances()[self._target['id']][1] ==
125 'TERMINATING'):
126 self._logger.info('machine %s is deleted or TERMINATING' %
127 self._target['id'])
128 return True
129
130 time_diff = time.time() - self._start_time
131 if time_diff > self._timeout:
132 self._logger.error('%d exceeded launch server timeout of %d' %
133 (time_diff, self._timeout))
134 raise TimeoutException
135
136 admin_pass = self._target['adminPass']
137 # Could check more things here.
138 if (self._expected['adminPass'] != admin_pass):
139 self._logger.error('expected: %s' %
140 (self._expected['adminPass']))
141 self._logger.error('returned: %s' %
142 (admin_pass))
143 raise Exception
144
145 if self._check_for_status('ACTIVE') != 'ACTIVE':
146 return False
147
148 self._logger.info('machine %s: BUILD -> ACTIVE [%.1f secs elapsed]' %
149 (self._target['id'], time.time() - self._start_time))
150 self._state.set_instance_state(self._target['id'],
151 (self._target, 'ACTIVE'))
152 return True
153
154
155class TestKillActiveVM(test_case.StressTestCase):
156 """Class to destroy a random ACTIVE server."""
157 def run(self, manager, state, *pargs, **kwargs):
158 """
159 Send an HTTP POST request to the nova cluster to destroy
160 a random ACTIVE server. Update `state` to indicate TERMINATING.
161
162 `manager` : Manager object.
163 `state` : `State` object describing our view of state of cluster
164 `pargs` : positional arguments
165 `kwargs` : keyword arguments, which include:
166 `timeout` : how long to wait before issuing Exception
167 """
168 # check for active instances
169 vms = state.get_instances()
170 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
171 # no active vms, so return null
172 if not active_vms:
173 self._logger.info('no ACTIVE instances to delete')
174 return
175
David Kranz30fe84a2012-03-20 16:25:47 -0400176 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500177
178 target = random.choice(active_vms)
179 killtarget = target[0]
180 manager.servers_client.delete_server(killtarget['id'])
181 self._logger.info('machine %s: ACTIVE -> TERMINATING' %
182 killtarget['id'])
183 state.set_instance_state(killtarget['id'],
184 (killtarget, 'TERMINATING'))
185 return VerifyKillActiveVM(manager, state,
186 killtarget, timeout=_timeout)
187
188
189class VerifyKillActiveVM(pending_action.PendingAction):
190 """Verify that server was destroyed"""
191
192 def retry(self):
193 """
194 Check to see that the server of interest is destroyed. Update
195 state to indicate that server is destroyed by deleting it from local
196 view of state.
197 """
198 tid = self._target['id']
199 # if target machine has been deleted from the state, then it was
200 # already verified to be deleted
201 if (not tid in self._state.get_instances().keys()):
202 return False
203
204 time_diff = time.time() - self._start_time
205 if time_diff > self._timeout:
206 self._logger.error('server %s: %d exceeds terminate timeout %d' %
207 (tid, time_diff, self._timeout))
208 raise TimeoutException
209
210 try:
211 self._manager.servers_client.get_server(tid)
212 except Exception:
213 # if we get a 404 response, is the machine really gone?
214 target = self._target
215 self._logger.info('machine %s: DELETED [%.1f secs elapsed]' %
216 (target['id'], time.time() - self._start_time))
David Kranz180fed12012-03-27 14:31:29 -0400217 self._state.delete_instance_state(target['id'])
David Kranz6308ec22012-02-22 09:36:48 -0500218 return True
219
220 return False
221
222
223class TestKillAnyVM(test_case.StressTestCase):
224 """Class to destroy a random server regardless of state."""
225
226 def run(self, manager, state, *pargs, **kwargs):
227 """
228 Send an HTTP POST request to the nova cluster to destroy
229 a random server. Update state to TERMINATING.
230
231 `manager` : Manager object.
232 `state` : `State` object describing our view of state of cluster
233 `pargs` : positional arguments
234 `kwargs` : keyword arguments, which include:
235 `timeout` : how long to wait before issuing Exception
236 """
237
238 vms = state.get_instances()
239 # no vms, so return null
240 if not vms:
241 self._logger.info('no active instances to delete')
242 return
243
David Kranz30fe84a2012-03-20 16:25:47 -0400244 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500245
246 target = random.choice(vms)
247 killtarget = target[0]
248
249 manager.servers_client.delete_server(killtarget['id'])
250 self._state.set_instance_state(killtarget['id'],
251 (killtarget, 'TERMINATING'))
252 # verify object will do the same thing as the active VM
253 return VerifyKillAnyVM(manager, state, killtarget, timeout=_timeout)
254
255VerifyKillAnyVM = VerifyKillActiveVM
256
257
258class TestUpdateVMName(test_case.StressTestCase):
259 """Class to change the name of the active server"""
260 def run(self, manager, state, *pargs, **kwargs):
261 """
262 Issue HTTP POST request to change the name of active server.
263 Update state of server to reflect name changing.
264
265 `manager` : Manager object.
266 `state` : `State` object describing our view of state of cluster
267 `pargs` : positional arguments
268 `kwargs` : keyword arguments, which include:
269 `timeout` : how long to wait before issuing Exception
270 """
271
272 # select one machine from active ones
273 vms = state.get_instances()
274 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
275 # no active vms, so return null
276 if not active_vms:
277 self._logger.info('no active instances to update')
278 return
279
David Kranz30fe84a2012-03-20 16:25:47 -0400280 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500281
282 target = random.choice(active_vms)
283 update_target = target[0]
284
285 # Update name by appending '_updated' to the name
286 new_name = update_target['name'] + '_updated'
287 (response, body) = \
288 manager.servers_client.update_server(update_target['id'],
289 name=new_name)
290 if (response.status != 200):
291 self._logger.error("response: %s " % response)
292 self._logger.error("body: %s " % body)
293 raise Exception
294
295 assert(new_name == body['name'])
296
297 self._logger.info('machine %s: ACTIVE -> UPDATING_NAME' %
298 body['id'])
299 state.set_instance_state(body['id'],
300 (body, 'UPDATING_NAME'))
301
302 return VerifyUpdateVMName(manager,
303 state,
304 body,
305 timeout=_timeout)
306
307
308class VerifyUpdateVMName(pending_action.PendingAction):
309 """Check that VM has new name"""
310 def retry(self):
311 """
312 Check that VM has new name. Update local view of `state` to RUNNING.
313 """
314 # don't run update verification
315 # if target machine has been deleted or is going to be deleted
316 if (not self._target['id'] in self._state.get_instances().keys() or
317 self._state.get_instances()[self._target['id']][1] ==
318 'TERMINATING'):
319 return False
320
321 if time.time() - self._start_time > self._timeout:
322 raise TimeoutException
323
324 response, body = \
325 self._manager.serverse_client.get_server(self._target['id'])
326 if (response.status != 200):
327 self._logger.error("response: %s " % response)
328 self._logger.error("body: %s " % body)
329 raise Exception
330
331 if self._target['name'] != body['name']:
332 self._logger.error(self._target['name'] +
333 ' vs. ' +
334 body['name'])
335 raise Exception
336
337 # log the update
338 self._logger.info('machine %s: UPDATING_NAME -> ACTIVE' %
339 self._target['id'])
340 self._state.set_instance_state(self._target['id'],
341 (body,
342 'ACTIVE'))
343 return True