blob: 25cbbb07e7119d7e7b1d13e2cfe4aa94ab6c41bd [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
David Kranz6308ec22012-02-22 09:36:48 -050020import random
David Kranz6308ec22012-02-22 09:36:48 -050021
David Kranz6308ec22012-02-22 09:36:48 -050022import pending_action
Matthew Treinish8d6836b2012-12-10 10:07:56 -050023import test_case
David Kranz6308ec22012-02-22 09:36:48 -050024
25
26class TestCreateVM(test_case.StressTestCase):
27 """Create a virtual machine in the Nova cluster."""
28 _vm_id = 0
29
30 def run(self, manager, state, *pargs, **kwargs):
31 """
32 Send an HTTP POST request to the nova cluster to build a
33 server. Update the state variable to track state of new server
34 and set to PENDING state.
35
36 `manager` : Manager object.
37 `state` : `State` object describing our view of state of cluster
38 `pargs` : positional arguments
39 `kwargs` : keyword arguments, which include:
40 `key_name` : name of keypair
David Kranz6308ec22012-02-22 09:36:48 -050041 `image_ref` : index to image types availablexs
42 `flavor_ref`: index to flavor types available
43 (default = 1, which is tiny)
44 """
45
46 # restrict number of instances we can launch
47 if len(state.get_instances()) >= state.get_max_instances():
48 self._logger.debug("maximum number of instances created: %d" %
49 state.get_max_instances())
50 return None
51
52 _key_name = kwargs.get('key_name', '')
David Kranz6308ec22012-02-22 09:36:48 -050053 _image_ref = kwargs.get('image_ref', manager.config.compute.image_ref)
54 _flavor_ref = kwargs.get('flavor_ref',
55 manager.config.compute.flavor_ref)
56
57 expected_server = {
58 'name': 'server' + str(TestCreateVM._vm_id),
59 'metadata': {
60 'key1': 'value1',
61 'key2': 'value2',
Zhongyue Luo30a563f2012-09-30 23:43:50 +090062 },
David Kranz6308ec22012-02-22 09:36:48 -050063 'imageRef': _image_ref,
64 'flavorRef': _flavor_ref,
65 'adminPass': 'testpwd',
Zhongyue Luo30a563f2012-09-30 23:43:50 +090066 'key_name': _key_name,
67 }
David Kranz6308ec22012-02-22 09:36:48 -050068 TestCreateVM._vm_id = TestCreateVM._vm_id + 1
Zhongyue Luo30a563f2012-09-30 23:43:50 +090069 create_server = manager.servers_client.create_server
70 response, body = create_server(expected_server['name'],
71 _image_ref,
72 _flavor_ref,
73 meta=expected_server['metadata'],
74 adminPass=expected_server['adminPass'])
David Kranz6308ec22012-02-22 09:36:48 -050075
76 if (response.status != 202):
77 self._logger.error("response: %s" % response)
78 self._logger.error("body: %s" % body)
79 raise Exception
80
81 created_server = body
82
83 self._logger.info('setting machine %s to BUILD' %
84 created_server['id'])
85 state.set_instance_state(created_server['id'],
86 (created_server, 'BUILD'))
87
88 return VerifyCreateVM(manager,
89 state,
90 created_server,
91 expected_server)
92
93
David Kranz779c7f82012-05-01 16:50:32 -040094class VerifyCreateVM(pending_action.PendingServerAction):
Sean Daguef237ccb2013-01-04 15:19:14 -050095 """Verify that VM was built and is running."""
David Kranz6308ec22012-02-22 09:36:48 -050096 def __init__(self, manager,
97 state,
98 created_server,
99 expected_server):
100 super(VerifyCreateVM, self).__init__(manager,
101 state,
102 created_server,
103 )
104 self._expected = expected_server
105
106 def retry(self):
107 """
108 Check to see that the server was created and is running.
109 Update local view of state to indicate that it is running.
110 """
111 # don't run create verification
112 # if target machine has been deleted or is going to be deleted
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900113 target_id = self._target['id']
David Kranz6308ec22012-02-22 09:36:48 -0500114 if (self._target['id'] not in self._state.get_instances().keys() or
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900115 self._state.get_instances()[target_id][1] == 'TERMINATING'):
David Kranz6308ec22012-02-22 09:36:48 -0500116 self._logger.info('machine %s is deleted or TERMINATING' %
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800117 self._target['id'])
David Kranz6308ec22012-02-22 09:36:48 -0500118 return True
119
David Kranz6308ec22012-02-22 09:36:48 -0500120 admin_pass = self._target['adminPass']
121 # Could check more things here.
122 if (self._expected['adminPass'] != admin_pass):
123 self._logger.error('expected: %s' %
124 (self._expected['adminPass']))
125 self._logger.error('returned: %s' %
126 (admin_pass))
127 raise Exception
128
129 if self._check_for_status('ACTIVE') != 'ACTIVE':
130 return False
131
132 self._logger.info('machine %s: BUILD -> ACTIVE [%.1f secs elapsed]' %
David Kranz779c7f82012-05-01 16:50:32 -0400133 (self._target['id'], self.elapsed()))
David Kranz6308ec22012-02-22 09:36:48 -0500134 self._state.set_instance_state(self._target['id'],
135 (self._target, 'ACTIVE'))
136 return True
137
138
139class TestKillActiveVM(test_case.StressTestCase):
140 """Class to destroy a random ACTIVE server."""
141 def run(self, manager, state, *pargs, **kwargs):
142 """
143 Send an HTTP POST request to the nova cluster to destroy
144 a random ACTIVE server. Update `state` to indicate TERMINATING.
145
146 `manager` : Manager object.
147 `state` : `State` object describing our view of state of cluster
148 `pargs` : positional arguments
149 `kwargs` : keyword arguments, which include:
150 `timeout` : how long to wait before issuing Exception
151 """
152 # check for active instances
153 vms = state.get_instances()
154 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
155 # no active vms, so return null
156 if not active_vms:
157 self._logger.info('no ACTIVE instances to delete')
158 return
159
David Kranz30fe84a2012-03-20 16:25:47 -0400160 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500161
162 target = random.choice(active_vms)
163 killtarget = target[0]
164 manager.servers_client.delete_server(killtarget['id'])
165 self._logger.info('machine %s: ACTIVE -> TERMINATING' %
166 killtarget['id'])
167 state.set_instance_state(killtarget['id'],
168 (killtarget, 'TERMINATING'))
169 return VerifyKillActiveVM(manager, state,
170 killtarget, timeout=_timeout)
171
172
David Kranz779c7f82012-05-01 16:50:32 -0400173class VerifyKillActiveVM(pending_action.PendingServerAction):
Sean Daguef237ccb2013-01-04 15:19:14 -0500174 """Verify that server was destroyed."""
David Kranz6308ec22012-02-22 09:36:48 -0500175
176 def retry(self):
177 """
178 Check to see that the server of interest is destroyed. Update
179 state to indicate that server is destroyed by deleting it from local
180 view of state.
181 """
182 tid = self._target['id']
183 # if target machine has been deleted from the state, then it was
184 # already verified to be deleted
185 if (not tid in self._state.get_instances().keys()):
186 return False
187
David Kranz6308ec22012-02-22 09:36:48 -0500188 try:
189 self._manager.servers_client.get_server(tid)
190 except Exception:
191 # if we get a 404 response, is the machine really gone?
192 target = self._target
193 self._logger.info('machine %s: DELETED [%.1f secs elapsed]' %
David Kranz779c7f82012-05-01 16:50:32 -0400194 (target['id'], self.elapsed()))
David Kranz180fed12012-03-27 14:31:29 -0400195 self._state.delete_instance_state(target['id'])
David Kranz6308ec22012-02-22 09:36:48 -0500196 return True
197
198 return False
199
200
201class TestKillAnyVM(test_case.StressTestCase):
202 """Class to destroy a random server regardless of state."""
203
204 def run(self, manager, state, *pargs, **kwargs):
205 """
206 Send an HTTP POST request to the nova cluster to destroy
207 a random server. Update state to TERMINATING.
208
209 `manager` : Manager object.
210 `state` : `State` object describing our view of state of cluster
211 `pargs` : positional arguments
212 `kwargs` : keyword arguments, which include:
213 `timeout` : how long to wait before issuing Exception
214 """
215
216 vms = state.get_instances()
217 # no vms, so return null
218 if not vms:
219 self._logger.info('no active instances to delete')
220 return
221
David Kranz30fe84a2012-03-20 16:25:47 -0400222 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500223
224 target = random.choice(vms)
225 killtarget = target[0]
226
227 manager.servers_client.delete_server(killtarget['id'])
228 self._state.set_instance_state(killtarget['id'],
229 (killtarget, 'TERMINATING'))
230 # verify object will do the same thing as the active VM
231 return VerifyKillAnyVM(manager, state, killtarget, timeout=_timeout)
232
233VerifyKillAnyVM = VerifyKillActiveVM
234
235
236class TestUpdateVMName(test_case.StressTestCase):
Sean Daguef237ccb2013-01-04 15:19:14 -0500237 """Class to change the name of the active server."""
David Kranz6308ec22012-02-22 09:36:48 -0500238 def run(self, manager, state, *pargs, **kwargs):
239 """
240 Issue HTTP POST request to change the name of active server.
241 Update state of server to reflect name changing.
242
243 `manager` : Manager object.
244 `state` : `State` object describing our view of state of cluster
245 `pargs` : positional arguments
246 `kwargs` : keyword arguments, which include:
247 `timeout` : how long to wait before issuing Exception
248 """
249
250 # select one machine from active ones
251 vms = state.get_instances()
252 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
253 # no active vms, so return null
254 if not active_vms:
255 self._logger.info('no active instances to update')
256 return
257
David Kranz30fe84a2012-03-20 16:25:47 -0400258 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500259
260 target = random.choice(active_vms)
261 update_target = target[0]
262
263 # Update name by appending '_updated' to the name
264 new_name = update_target['name'] + '_updated'
265 (response, body) = \
266 manager.servers_client.update_server(update_target['id'],
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800267 name=new_name)
David Kranz6308ec22012-02-22 09:36:48 -0500268 if (response.status != 200):
269 self._logger.error("response: %s " % response)
270 self._logger.error("body: %s " % body)
271 raise Exception
272
273 assert(new_name == body['name'])
274
275 self._logger.info('machine %s: ACTIVE -> UPDATING_NAME' %
276 body['id'])
277 state.set_instance_state(body['id'],
278 (body, 'UPDATING_NAME'))
279
280 return VerifyUpdateVMName(manager,
281 state,
282 body,
283 timeout=_timeout)
284
285
David Kranz779c7f82012-05-01 16:50:32 -0400286class VerifyUpdateVMName(pending_action.PendingServerAction):
Sean Daguef237ccb2013-01-04 15:19:14 -0500287 """Check that VM has new name."""
David Kranz6308ec22012-02-22 09:36:48 -0500288 def retry(self):
289 """
290 Check that VM has new name. Update local view of `state` to RUNNING.
291 """
292 # don't run update verification
293 # if target machine has been deleted or is going to be deleted
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900294 target_id = self._target['id']
David Kranz6308ec22012-02-22 09:36:48 -0500295 if (not self._target['id'] in self._state.get_instances().keys() or
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900296 self._state.get_instances()[target_id][1] == 'TERMINATING'):
David Kranz6308ec22012-02-22 09:36:48 -0500297 return False
298
David Kranz6308ec22012-02-22 09:36:48 -0500299 response, body = \
300 self._manager.serverse_client.get_server(self._target['id'])
301 if (response.status != 200):
302 self._logger.error("response: %s " % response)
303 self._logger.error("body: %s " % body)
304 raise Exception
305
306 if self._target['name'] != body['name']:
307 self._logger.error(self._target['name'] +
308 ' vs. ' +
309 body['name'])
310 raise Exception
311
312 # log the update
313 self._logger.info('machine %s: UPDATING_NAME -> ACTIVE' %
314 self._target['id'])
315 self._state.set_instance_state(self._target['id'],
316 (body,
317 'ACTIVE'))
318 return True