blob: 113e5cb7b5cf21a711dde6994d50fd00ecd502e3 [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',
Zhongyue Luo30a563f2012-09-30 23:43:50 +090070 },
David Kranz6308ec22012-02-22 09:36:48 -050071 'imageRef': _image_ref,
72 'flavorRef': _flavor_ref,
73 'adminPass': 'testpwd',
Zhongyue Luo30a563f2012-09-30 23:43:50 +090074 'key_name': _key_name,
75 }
David Kranz6308ec22012-02-22 09:36:48 -050076 TestCreateVM._vm_id = TestCreateVM._vm_id + 1
Zhongyue Luo30a563f2012-09-30 23:43:50 +090077 create_server = manager.servers_client.create_server
78 response, body = create_server(expected_server['name'],
79 _image_ref,
80 _flavor_ref,
81 meta=expected_server['metadata'],
82 adminPass=expected_server['adminPass'])
David Kranz6308ec22012-02-22 09:36:48 -050083
84 if (response.status != 202):
85 self._logger.error("response: %s" % response)
86 self._logger.error("body: %s" % body)
87 raise Exception
88
89 created_server = body
90
91 self._logger.info('setting machine %s to BUILD' %
92 created_server['id'])
93 state.set_instance_state(created_server['id'],
94 (created_server, 'BUILD'))
95
96 return VerifyCreateVM(manager,
97 state,
98 created_server,
99 expected_server)
100
101
David Kranz779c7f82012-05-01 16:50:32 -0400102class VerifyCreateVM(pending_action.PendingServerAction):
David Kranz6308ec22012-02-22 09:36:48 -0500103 """Verify that VM was built and is running"""
104 def __init__(self, manager,
105 state,
106 created_server,
107 expected_server):
108 super(VerifyCreateVM, self).__init__(manager,
109 state,
110 created_server,
111 )
112 self._expected = expected_server
113
114 def retry(self):
115 """
116 Check to see that the server was created and is running.
117 Update local view of state to indicate that it is running.
118 """
119 # don't run create verification
120 # if target machine has been deleted or is going to be deleted
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900121 target_id = self._target['id']
David Kranz6308ec22012-02-22 09:36:48 -0500122 if (self._target['id'] not in self._state.get_instances().keys() or
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900123 self._state.get_instances()[target_id][1] == 'TERMINATING'):
David Kranz6308ec22012-02-22 09:36:48 -0500124 self._logger.info('machine %s is deleted or TERMINATING' %
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800125 self._target['id'])
David Kranz6308ec22012-02-22 09:36:48 -0500126 return True
127
David Kranz6308ec22012-02-22 09:36:48 -0500128 admin_pass = self._target['adminPass']
129 # Could check more things here.
130 if (self._expected['adminPass'] != admin_pass):
131 self._logger.error('expected: %s' %
132 (self._expected['adminPass']))
133 self._logger.error('returned: %s' %
134 (admin_pass))
135 raise Exception
136
137 if self._check_for_status('ACTIVE') != 'ACTIVE':
138 return False
139
140 self._logger.info('machine %s: BUILD -> ACTIVE [%.1f secs elapsed]' %
David Kranz779c7f82012-05-01 16:50:32 -0400141 (self._target['id'], self.elapsed()))
David Kranz6308ec22012-02-22 09:36:48 -0500142 self._state.set_instance_state(self._target['id'],
143 (self._target, 'ACTIVE'))
144 return True
145
146
147class TestKillActiveVM(test_case.StressTestCase):
148 """Class to destroy a random ACTIVE server."""
149 def run(self, manager, state, *pargs, **kwargs):
150 """
151 Send an HTTP POST request to the nova cluster to destroy
152 a random ACTIVE server. Update `state` to indicate TERMINATING.
153
154 `manager` : Manager object.
155 `state` : `State` object describing our view of state of cluster
156 `pargs` : positional arguments
157 `kwargs` : keyword arguments, which include:
158 `timeout` : how long to wait before issuing Exception
159 """
160 # check for active instances
161 vms = state.get_instances()
162 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
163 # no active vms, so return null
164 if not active_vms:
165 self._logger.info('no ACTIVE instances to delete')
166 return
167
David Kranz30fe84a2012-03-20 16:25:47 -0400168 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500169
170 target = random.choice(active_vms)
171 killtarget = target[0]
172 manager.servers_client.delete_server(killtarget['id'])
173 self._logger.info('machine %s: ACTIVE -> TERMINATING' %
174 killtarget['id'])
175 state.set_instance_state(killtarget['id'],
176 (killtarget, 'TERMINATING'))
177 return VerifyKillActiveVM(manager, state,
178 killtarget, timeout=_timeout)
179
180
David Kranz779c7f82012-05-01 16:50:32 -0400181class VerifyKillActiveVM(pending_action.PendingServerAction):
David Kranz6308ec22012-02-22 09:36:48 -0500182 """Verify that server was destroyed"""
183
184 def retry(self):
185 """
186 Check to see that the server of interest is destroyed. Update
187 state to indicate that server is destroyed by deleting it from local
188 view of state.
189 """
190 tid = self._target['id']
191 # if target machine has been deleted from the state, then it was
192 # already verified to be deleted
193 if (not tid in self._state.get_instances().keys()):
194 return False
195
David Kranz6308ec22012-02-22 09:36:48 -0500196 try:
197 self._manager.servers_client.get_server(tid)
198 except Exception:
199 # if we get a 404 response, is the machine really gone?
200 target = self._target
201 self._logger.info('machine %s: DELETED [%.1f secs elapsed]' %
David Kranz779c7f82012-05-01 16:50:32 -0400202 (target['id'], self.elapsed()))
David Kranz180fed12012-03-27 14:31:29 -0400203 self._state.delete_instance_state(target['id'])
David Kranz6308ec22012-02-22 09:36:48 -0500204 return True
205
206 return False
207
208
209class TestKillAnyVM(test_case.StressTestCase):
210 """Class to destroy a random server regardless of state."""
211
212 def run(self, manager, state, *pargs, **kwargs):
213 """
214 Send an HTTP POST request to the nova cluster to destroy
215 a random server. Update state to TERMINATING.
216
217 `manager` : Manager object.
218 `state` : `State` object describing our view of state of cluster
219 `pargs` : positional arguments
220 `kwargs` : keyword arguments, which include:
221 `timeout` : how long to wait before issuing Exception
222 """
223
224 vms = state.get_instances()
225 # no vms, so return null
226 if not vms:
227 self._logger.info('no active instances to delete')
228 return
229
David Kranz30fe84a2012-03-20 16:25:47 -0400230 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500231
232 target = random.choice(vms)
233 killtarget = target[0]
234
235 manager.servers_client.delete_server(killtarget['id'])
236 self._state.set_instance_state(killtarget['id'],
237 (killtarget, 'TERMINATING'))
238 # verify object will do the same thing as the active VM
239 return VerifyKillAnyVM(manager, state, killtarget, timeout=_timeout)
240
241VerifyKillAnyVM = VerifyKillActiveVM
242
243
244class TestUpdateVMName(test_case.StressTestCase):
245 """Class to change the name of the active server"""
246 def run(self, manager, state, *pargs, **kwargs):
247 """
248 Issue HTTP POST request to change the name of active server.
249 Update state of server to reflect name changing.
250
251 `manager` : Manager object.
252 `state` : `State` object describing our view of state of cluster
253 `pargs` : positional arguments
254 `kwargs` : keyword arguments, which include:
255 `timeout` : how long to wait before issuing Exception
256 """
257
258 # select one machine from active ones
259 vms = state.get_instances()
260 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
261 # no active vms, so return null
262 if not active_vms:
263 self._logger.info('no active instances to update')
264 return
265
David Kranz30fe84a2012-03-20 16:25:47 -0400266 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500267
268 target = random.choice(active_vms)
269 update_target = target[0]
270
271 # Update name by appending '_updated' to the name
272 new_name = update_target['name'] + '_updated'
273 (response, body) = \
274 manager.servers_client.update_server(update_target['id'],
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800275 name=new_name)
David Kranz6308ec22012-02-22 09:36:48 -0500276 if (response.status != 200):
277 self._logger.error("response: %s " % response)
278 self._logger.error("body: %s " % body)
279 raise Exception
280
281 assert(new_name == body['name'])
282
283 self._logger.info('machine %s: ACTIVE -> UPDATING_NAME' %
284 body['id'])
285 state.set_instance_state(body['id'],
286 (body, 'UPDATING_NAME'))
287
288 return VerifyUpdateVMName(manager,
289 state,
290 body,
291 timeout=_timeout)
292
293
David Kranz779c7f82012-05-01 16:50:32 -0400294class VerifyUpdateVMName(pending_action.PendingServerAction):
David Kranz6308ec22012-02-22 09:36:48 -0500295 """Check that VM has new name"""
296 def retry(self):
297 """
298 Check that VM has new name. Update local view of `state` to RUNNING.
299 """
300 # don't run update verification
301 # if target machine has been deleted or is going to be deleted
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900302 target_id = self._target['id']
David Kranz6308ec22012-02-22 09:36:48 -0500303 if (not self._target['id'] in self._state.get_instances().keys() or
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900304 self._state.get_instances()[target_id][1] == 'TERMINATING'):
David Kranz6308ec22012-02-22 09:36:48 -0500305 return False
306
David Kranz6308ec22012-02-22 09:36:48 -0500307 response, body = \
308 self._manager.serverse_client.get_server(self._target['id'])
309 if (response.status != 200):
310 self._logger.error("response: %s " % response)
311 self._logger.error("body: %s " % body)
312 raise Exception
313
314 if self._target['name'] != body['name']:
315 self._logger.error(self._target['name'] +
316 ' vs. ' +
317 body['name'])
318 raise Exception
319
320 # log the update
321 self._logger.info('machine %s: UPDATING_NAME -> ACTIVE' %
322 self._target['id'])
323 self._state.set_instance_state(self._target['id'],
324 (body,
325 'ACTIVE'))
326 return True