blob: 3f62ac32047651d4f05604e520437a5460008671 [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', '')
60 _timeout = int(kwargs.get('timeout', 60))
61 _image_ref = kwargs.get('image_ref', manager.config.compute.image_ref)
62 _flavor_ref = kwargs.get('flavor_ref',
63 manager.config.compute.flavor_ref)
64
65 expected_server = {
66 'name': 'server' + str(TestCreateVM._vm_id),
67 'metadata': {
68 'key1': 'value1',
69 'key2': 'value2',
70 },
71 'imageRef': _image_ref,
72 'flavorRef': _flavor_ref,
73 'adminPass': 'testpwd',
74 'key_name': _key_name
75 }
76 TestCreateVM._vm_id = TestCreateVM._vm_id + 1
77 response, body = manager.servers_client.create_server(
78 expected_server['name'],
79 _image_ref,
80 _flavor_ref,
81 meta=expected_server['metadata'],
82 adminPass=expected_server['adminPass']
83 )
84
85 if (response.status != 202):
86 self._logger.error("response: %s" % response)
87 self._logger.error("body: %s" % body)
88 raise Exception
89
90 created_server = body
91
92 self._logger.info('setting machine %s to BUILD' %
93 created_server['id'])
94 state.set_instance_state(created_server['id'],
95 (created_server, 'BUILD'))
96
97 return VerifyCreateVM(manager,
98 state,
99 created_server,
100 expected_server)
101
102
103class VerifyCreateVM(pending_action.PendingAction):
104 """Verify that VM was built and is running"""
105 def __init__(self, manager,
106 state,
107 created_server,
108 expected_server):
109 super(VerifyCreateVM, self).__init__(manager,
110 state,
111 created_server,
112 )
113 self._expected = expected_server
114
115 def retry(self):
116 """
117 Check to see that the server was created and is running.
118 Update local view of state to indicate that it is running.
119 """
120 # don't run create verification
121 # if target machine has been deleted or is going to be deleted
122 if (self._target['id'] not in self._state.get_instances().keys() or
123 self._state.get_instances()[self._target['id']][1] ==
124 'TERMINATING'):
125 self._logger.info('machine %s is deleted or TERMINATING' %
126 self._target['id'])
127 return True
128
129 time_diff = time.time() - self._start_time
130 if time_diff > self._timeout:
131 self._logger.error('%d exceeded launch server timeout of %d' %
132 (time_diff, self._timeout))
133 raise TimeoutException
134
135 admin_pass = self._target['adminPass']
136 # Could check more things here.
137 if (self._expected['adminPass'] != admin_pass):
138 self._logger.error('expected: %s' %
139 (self._expected['adminPass']))
140 self._logger.error('returned: %s' %
141 (admin_pass))
142 raise Exception
143
144 if self._check_for_status('ACTIVE') != 'ACTIVE':
145 return False
146
147 self._logger.info('machine %s: BUILD -> ACTIVE [%.1f secs elapsed]' %
148 (self._target['id'], time.time() - self._start_time))
149 self._state.set_instance_state(self._target['id'],
150 (self._target, 'ACTIVE'))
151 return True
152
153
154class TestKillActiveVM(test_case.StressTestCase):
155 """Class to destroy a random ACTIVE server."""
156 def run(self, manager, state, *pargs, **kwargs):
157 """
158 Send an HTTP POST request to the nova cluster to destroy
159 a random ACTIVE server. Update `state` to indicate TERMINATING.
160
161 `manager` : Manager object.
162 `state` : `State` object describing our view of state of cluster
163 `pargs` : positional arguments
164 `kwargs` : keyword arguments, which include:
165 `timeout` : how long to wait before issuing Exception
166 """
167 # check for active instances
168 vms = state.get_instances()
169 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
170 # no active vms, so return null
171 if not active_vms:
172 self._logger.info('no ACTIVE instances to delete')
173 return
174
175 _timeout = kwargs.get('timeout', 600)
176
177 target = random.choice(active_vms)
178 killtarget = target[0]
179 manager.servers_client.delete_server(killtarget['id'])
180 self._logger.info('machine %s: ACTIVE -> TERMINATING' %
181 killtarget['id'])
182 state.set_instance_state(killtarget['id'],
183 (killtarget, 'TERMINATING'))
184 return VerifyKillActiveVM(manager, state,
185 killtarget, timeout=_timeout)
186
187
188class VerifyKillActiveVM(pending_action.PendingAction):
189 """Verify that server was destroyed"""
190
191 def retry(self):
192 """
193 Check to see that the server of interest is destroyed. Update
194 state to indicate that server is destroyed by deleting it from local
195 view of state.
196 """
197 tid = self._target['id']
198 # if target machine has been deleted from the state, then it was
199 # already verified to be deleted
200 if (not tid in self._state.get_instances().keys()):
201 return False
202
203 time_diff = time.time() - self._start_time
204 if time_diff > self._timeout:
205 self._logger.error('server %s: %d exceeds terminate timeout %d' %
206 (tid, time_diff, self._timeout))
207 raise TimeoutException
208
209 try:
210 self._manager.servers_client.get_server(tid)
211 except Exception:
212 # if we get a 404 response, is the machine really gone?
213 target = self._target
214 self._logger.info('machine %s: DELETED [%.1f secs elapsed]' %
215 (target['id'], time.time() - self._start_time))
216 self._state.delete_machine_state(target['id'])
217 return True
218
219 return False
220
221
222class TestKillAnyVM(test_case.StressTestCase):
223 """Class to destroy a random server regardless of state."""
224
225 def run(self, manager, state, *pargs, **kwargs):
226 """
227 Send an HTTP POST request to the nova cluster to destroy
228 a random server. Update state to TERMINATING.
229
230 `manager` : Manager object.
231 `state` : `State` object describing our view of state of cluster
232 `pargs` : positional arguments
233 `kwargs` : keyword arguments, which include:
234 `timeout` : how long to wait before issuing Exception
235 """
236
237 vms = state.get_instances()
238 # no vms, so return null
239 if not vms:
240 self._logger.info('no active instances to delete')
241 return
242
243 _timeout = kwargs.get('timeout', 60)
244
245 target = random.choice(vms)
246 killtarget = target[0]
247
248 manager.servers_client.delete_server(killtarget['id'])
249 self._state.set_instance_state(killtarget['id'],
250 (killtarget, 'TERMINATING'))
251 # verify object will do the same thing as the active VM
252 return VerifyKillAnyVM(manager, state, killtarget, timeout=_timeout)
253
254VerifyKillAnyVM = VerifyKillActiveVM
255
256
257class TestUpdateVMName(test_case.StressTestCase):
258 """Class to change the name of the active server"""
259 def run(self, manager, state, *pargs, **kwargs):
260 """
261 Issue HTTP POST request to change the name of active server.
262 Update state of server to reflect name changing.
263
264 `manager` : Manager object.
265 `state` : `State` object describing our view of state of cluster
266 `pargs` : positional arguments
267 `kwargs` : keyword arguments, which include:
268 `timeout` : how long to wait before issuing Exception
269 """
270
271 # select one machine from active ones
272 vms = state.get_instances()
273 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
274 # no active vms, so return null
275 if not active_vms:
276 self._logger.info('no active instances to update')
277 return
278
279 _timeout = kwargs.get('timeout', 600)
280
281 target = random.choice(active_vms)
282 update_target = target[0]
283
284 # Update name by appending '_updated' to the name
285 new_name = update_target['name'] + '_updated'
286 (response, body) = \
287 manager.servers_client.update_server(update_target['id'],
288 name=new_name)
289 if (response.status != 200):
290 self._logger.error("response: %s " % response)
291 self._logger.error("body: %s " % body)
292 raise Exception
293
294 assert(new_name == body['name'])
295
296 self._logger.info('machine %s: ACTIVE -> UPDATING_NAME' %
297 body['id'])
298 state.set_instance_state(body['id'],
299 (body, 'UPDATING_NAME'))
300
301 return VerifyUpdateVMName(manager,
302 state,
303 body,
304 timeout=_timeout)
305
306
307class VerifyUpdateVMName(pending_action.PendingAction):
308 """Check that VM has new name"""
309 def retry(self):
310 """
311 Check that VM has new name. Update local view of `state` to RUNNING.
312 """
313 # don't run update verification
314 # if target machine has been deleted or is going to be deleted
315 if (not self._target['id'] in self._state.get_instances().keys() or
316 self._state.get_instances()[self._target['id']][1] ==
317 'TERMINATING'):
318 return False
319
320 if time.time() - self._start_time > self._timeout:
321 raise TimeoutException
322
323 response, body = \
324 self._manager.serverse_client.get_server(self._target['id'])
325 if (response.status != 200):
326 self._logger.error("response: %s " % response)
327 self._logger.error("body: %s " % body)
328 raise Exception
329
330 if self._target['name'] != body['name']:
331 self._logger.error(self._target['name'] +
332 ' vs. ' +
333 body['name'])
334 raise Exception
335
336 # log the update
337 self._logger.info('machine %s: UPDATING_NAME -> ACTIVE' %
338 self._target['id'])
339 self._state.set_instance_state(self._target['id'],
340 (body,
341 'ACTIVE'))
342 return True