blob: 57c923a694cb350acb41c3c22dd0a37c54d20cfe [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
David Kranz779c7f82012-05-01 16:50:32 -040015`PendingServerAction` class. Sub-classes of StressTestCase implement various
David Kranz6308ec22012-02-22 09:36:48 -050016API calls on the Nova cluster having to do with creating and deleting VMs.
David Kranz779c7f82012-05-01 16:50:32 -040017Each sub-class will have a corresponding PendingServerAction. These pending
David Kranz6308ec22012-02-22 09:36:48 -050018actions 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
David Kranz6308ec22012-02-22 09:36:48 -050029
30
31class TestCreateVM(test_case.StressTestCase):
32 """Create a virtual machine in the Nova cluster."""
33 _vm_id = 0
34
35 def run(self, manager, state, *pargs, **kwargs):
36 """
37 Send an HTTP POST request to the nova cluster to build a
38 server. Update the state variable to track state of new server
39 and set to PENDING state.
40
41 `manager` : Manager object.
42 `state` : `State` object describing our view of state of cluster
43 `pargs` : positional arguments
44 `kwargs` : keyword arguments, which include:
45 `key_name` : name of keypair
46 `timeout` : how long to wait before issuing Exception
47 `image_ref` : index to image types availablexs
48 `flavor_ref`: index to flavor types available
49 (default = 1, which is tiny)
50 """
51
52 # restrict number of instances we can launch
53 if len(state.get_instances()) >= state.get_max_instances():
54 self._logger.debug("maximum number of instances created: %d" %
55 state.get_max_instances())
56 return None
57
58 _key_name = kwargs.get('key_name', '')
David Kranz30fe84a2012-03-20 16:25:47 -040059 _timeout = int(kwargs.get('timeout',
60 manager.config.compute.build_timeout))
David Kranz6308ec22012-02-22 09:36:48 -050061 _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
David Kranz779c7f82012-05-01 16:50:32 -0400103class VerifyCreateVM(pending_action.PendingServerAction):
David Kranz6308ec22012-02-22 09:36:48 -0500104 """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
David Kranz6308ec22012-02-22 09:36:48 -0500129 admin_pass = self._target['adminPass']
130 # Could check more things here.
131 if (self._expected['adminPass'] != admin_pass):
132 self._logger.error('expected: %s' %
133 (self._expected['adminPass']))
134 self._logger.error('returned: %s' %
135 (admin_pass))
136 raise Exception
137
138 if self._check_for_status('ACTIVE') != 'ACTIVE':
139 return False
140
141 self._logger.info('machine %s: BUILD -> ACTIVE [%.1f secs elapsed]' %
David Kranz779c7f82012-05-01 16:50:32 -0400142 (self._target['id'], self.elapsed()))
David Kranz6308ec22012-02-22 09:36:48 -0500143 self._state.set_instance_state(self._target['id'],
144 (self._target, 'ACTIVE'))
145 return True
146
147
148class TestKillActiveVM(test_case.StressTestCase):
149 """Class to destroy a random ACTIVE server."""
150 def run(self, manager, state, *pargs, **kwargs):
151 """
152 Send an HTTP POST request to the nova cluster to destroy
153 a random ACTIVE server. Update `state` to indicate TERMINATING.
154
155 `manager` : Manager object.
156 `state` : `State` object describing our view of state of cluster
157 `pargs` : positional arguments
158 `kwargs` : keyword arguments, which include:
159 `timeout` : how long to wait before issuing Exception
160 """
161 # check for active instances
162 vms = state.get_instances()
163 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
164 # no active vms, so return null
165 if not active_vms:
166 self._logger.info('no ACTIVE instances to delete')
167 return
168
David Kranz30fe84a2012-03-20 16:25:47 -0400169 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500170
171 target = random.choice(active_vms)
172 killtarget = target[0]
173 manager.servers_client.delete_server(killtarget['id'])
174 self._logger.info('machine %s: ACTIVE -> TERMINATING' %
175 killtarget['id'])
176 state.set_instance_state(killtarget['id'],
177 (killtarget, 'TERMINATING'))
178 return VerifyKillActiveVM(manager, state,
179 killtarget, timeout=_timeout)
180
181
David Kranz779c7f82012-05-01 16:50:32 -0400182class VerifyKillActiveVM(pending_action.PendingServerAction):
David Kranz6308ec22012-02-22 09:36:48 -0500183 """Verify that server was destroyed"""
184
185 def retry(self):
186 """
187 Check to see that the server of interest is destroyed. Update
188 state to indicate that server is destroyed by deleting it from local
189 view of state.
190 """
191 tid = self._target['id']
192 # if target machine has been deleted from the state, then it was
193 # already verified to be deleted
194 if (not tid in self._state.get_instances().keys()):
195 return False
196
David Kranz6308ec22012-02-22 09:36:48 -0500197 try:
198 self._manager.servers_client.get_server(tid)
199 except Exception:
200 # if we get a 404 response, is the machine really gone?
201 target = self._target
202 self._logger.info('machine %s: DELETED [%.1f secs elapsed]' %
David Kranz779c7f82012-05-01 16:50:32 -0400203 (target['id'], self.elapsed()))
David Kranz180fed12012-03-27 14:31:29 -0400204 self._state.delete_instance_state(target['id'])
David Kranz6308ec22012-02-22 09:36:48 -0500205 return True
206
207 return False
208
209
210class TestKillAnyVM(test_case.StressTestCase):
211 """Class to destroy a random server regardless of state."""
212
213 def run(self, manager, state, *pargs, **kwargs):
214 """
215 Send an HTTP POST request to the nova cluster to destroy
216 a random server. Update state to TERMINATING.
217
218 `manager` : Manager object.
219 `state` : `State` object describing our view of state of cluster
220 `pargs` : positional arguments
221 `kwargs` : keyword arguments, which include:
222 `timeout` : how long to wait before issuing Exception
223 """
224
225 vms = state.get_instances()
226 # no vms, so return null
227 if not vms:
228 self._logger.info('no active instances to delete')
229 return
230
David Kranz30fe84a2012-03-20 16:25:47 -0400231 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500232
233 target = random.choice(vms)
234 killtarget = target[0]
235
236 manager.servers_client.delete_server(killtarget['id'])
237 self._state.set_instance_state(killtarget['id'],
238 (killtarget, 'TERMINATING'))
239 # verify object will do the same thing as the active VM
240 return VerifyKillAnyVM(manager, state, killtarget, timeout=_timeout)
241
242VerifyKillAnyVM = VerifyKillActiveVM
243
244
245class TestUpdateVMName(test_case.StressTestCase):
246 """Class to change the name of the active server"""
247 def run(self, manager, state, *pargs, **kwargs):
248 """
249 Issue HTTP POST request to change the name of active server.
250 Update state of server to reflect name changing.
251
252 `manager` : Manager object.
253 `state` : `State` object describing our view of state of cluster
254 `pargs` : positional arguments
255 `kwargs` : keyword arguments, which include:
256 `timeout` : how long to wait before issuing Exception
257 """
258
259 # select one machine from active ones
260 vms = state.get_instances()
261 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
262 # no active vms, so return null
263 if not active_vms:
264 self._logger.info('no active instances to update')
265 return
266
David Kranz30fe84a2012-03-20 16:25:47 -0400267 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500268
269 target = random.choice(active_vms)
270 update_target = target[0]
271
272 # Update name by appending '_updated' to the name
273 new_name = update_target['name'] + '_updated'
274 (response, body) = \
275 manager.servers_client.update_server(update_target['id'],
276 name=new_name)
277 if (response.status != 200):
278 self._logger.error("response: %s " % response)
279 self._logger.error("body: %s " % body)
280 raise Exception
281
282 assert(new_name == body['name'])
283
284 self._logger.info('machine %s: ACTIVE -> UPDATING_NAME' %
285 body['id'])
286 state.set_instance_state(body['id'],
287 (body, 'UPDATING_NAME'))
288
289 return VerifyUpdateVMName(manager,
290 state,
291 body,
292 timeout=_timeout)
293
294
David Kranz779c7f82012-05-01 16:50:32 -0400295class VerifyUpdateVMName(pending_action.PendingServerAction):
David Kranz6308ec22012-02-22 09:36:48 -0500296 """Check that VM has new name"""
297 def retry(self):
298 """
299 Check that VM has new name. Update local view of `state` to RUNNING.
300 """
301 # don't run update verification
302 # if target machine has been deleted or is going to be deleted
303 if (not self._target['id'] in self._state.get_instances().keys() or
304 self._state.get_instances()[self._target['id']][1] ==
305 'TERMINATING'):
306 return False
307
David Kranz6308ec22012-02-22 09:36:48 -0500308 response, body = \
309 self._manager.serverse_client.get_server(self._target['id'])
310 if (response.status != 200):
311 self._logger.error("response: %s " % response)
312 self._logger.error("body: %s " % body)
313 raise Exception
314
315 if self._target['name'] != body['name']:
316 self._logger.error(self._target['name'] +
317 ' vs. ' +
318 body['name'])
319 raise Exception
320
321 # log the update
322 self._logger.info('machine %s: UPDATING_NAME -> ACTIVE' %
323 self._target['id'])
324 self._state.set_instance_state(self._target['id'],
325 (body,
326 'ACTIVE'))
327 return True