blob: e1cb4d10987b73ee0faa4e84d520ea46cbe38f35 [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
21import time
22
David Kranz6308ec22012-02-22 09:36:48 -050023import pending_action
Matthew Treinish8d6836b2012-12-10 10:07:56 -050024import test_case
David Kranz6308ec22012-02-22 09:36:48 -050025
26
27class TestCreateVM(test_case.StressTestCase):
28 """Create a virtual machine in the Nova cluster."""
29 _vm_id = 0
30
31 def run(self, manager, state, *pargs, **kwargs):
32 """
33 Send an HTTP POST request to the nova cluster to build a
34 server. Update the state variable to track state of new server
35 and set to PENDING state.
36
37 `manager` : Manager object.
38 `state` : `State` object describing our view of state of cluster
39 `pargs` : positional arguments
40 `kwargs` : keyword arguments, which include:
41 `key_name` : name of keypair
42 `timeout` : how long to wait before issuing Exception
43 `image_ref` : index to image types availablexs
44 `flavor_ref`: index to flavor types available
45 (default = 1, which is tiny)
46 """
47
48 # restrict number of instances we can launch
49 if len(state.get_instances()) >= state.get_max_instances():
50 self._logger.debug("maximum number of instances created: %d" %
51 state.get_max_instances())
52 return None
53
54 _key_name = kwargs.get('key_name', '')
David Kranz30fe84a2012-03-20 16:25:47 -040055 _timeout = int(kwargs.get('timeout',
56 manager.config.compute.build_timeout))
David Kranz6308ec22012-02-22 09:36:48 -050057 _image_ref = kwargs.get('image_ref', manager.config.compute.image_ref)
58 _flavor_ref = kwargs.get('flavor_ref',
59 manager.config.compute.flavor_ref)
60
61 expected_server = {
62 'name': 'server' + str(TestCreateVM._vm_id),
63 'metadata': {
64 'key1': 'value1',
65 'key2': 'value2',
Zhongyue Luo30a563f2012-09-30 23:43:50 +090066 },
David Kranz6308ec22012-02-22 09:36:48 -050067 'imageRef': _image_ref,
68 'flavorRef': _flavor_ref,
69 'adminPass': 'testpwd',
Zhongyue Luo30a563f2012-09-30 23:43:50 +090070 'key_name': _key_name,
71 }
David Kranz6308ec22012-02-22 09:36:48 -050072 TestCreateVM._vm_id = TestCreateVM._vm_id + 1
Zhongyue Luo30a563f2012-09-30 23:43:50 +090073 create_server = manager.servers_client.create_server
74 response, body = create_server(expected_server['name'],
75 _image_ref,
76 _flavor_ref,
77 meta=expected_server['metadata'],
78 adminPass=expected_server['adminPass'])
David Kranz6308ec22012-02-22 09:36:48 -050079
80 if (response.status != 202):
81 self._logger.error("response: %s" % response)
82 self._logger.error("body: %s" % body)
83 raise Exception
84
85 created_server = body
86
87 self._logger.info('setting machine %s to BUILD' %
88 created_server['id'])
89 state.set_instance_state(created_server['id'],
90 (created_server, 'BUILD'))
91
92 return VerifyCreateVM(manager,
93 state,
94 created_server,
95 expected_server)
96
97
David Kranz779c7f82012-05-01 16:50:32 -040098class VerifyCreateVM(pending_action.PendingServerAction):
Sean Daguef237ccb2013-01-04 15:19:14 -050099 """Verify that VM was built and is running."""
David Kranz6308ec22012-02-22 09:36:48 -0500100 def __init__(self, manager,
101 state,
102 created_server,
103 expected_server):
104 super(VerifyCreateVM, self).__init__(manager,
105 state,
106 created_server,
107 )
108 self._expected = expected_server
109
110 def retry(self):
111 """
112 Check to see that the server was created and is running.
113 Update local view of state to indicate that it is running.
114 """
115 # don't run create verification
116 # if target machine has been deleted or is going to be deleted
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900117 target_id = self._target['id']
David Kranz6308ec22012-02-22 09:36:48 -0500118 if (self._target['id'] not in self._state.get_instances().keys() or
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900119 self._state.get_instances()[target_id][1] == 'TERMINATING'):
David Kranz6308ec22012-02-22 09:36:48 -0500120 self._logger.info('machine %s is deleted or TERMINATING' %
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800121 self._target['id'])
David Kranz6308ec22012-02-22 09:36:48 -0500122 return True
123
David Kranz6308ec22012-02-22 09:36:48 -0500124 admin_pass = self._target['adminPass']
125 # Could check more things here.
126 if (self._expected['adminPass'] != admin_pass):
127 self._logger.error('expected: %s' %
128 (self._expected['adminPass']))
129 self._logger.error('returned: %s' %
130 (admin_pass))
131 raise Exception
132
133 if self._check_for_status('ACTIVE') != 'ACTIVE':
134 return False
135
136 self._logger.info('machine %s: BUILD -> ACTIVE [%.1f secs elapsed]' %
David Kranz779c7f82012-05-01 16:50:32 -0400137 (self._target['id'], self.elapsed()))
David Kranz6308ec22012-02-22 09:36:48 -0500138 self._state.set_instance_state(self._target['id'],
139 (self._target, 'ACTIVE'))
140 return True
141
142
143class TestKillActiveVM(test_case.StressTestCase):
144 """Class to destroy a random ACTIVE server."""
145 def run(self, manager, state, *pargs, **kwargs):
146 """
147 Send an HTTP POST request to the nova cluster to destroy
148 a random ACTIVE server. Update `state` to indicate TERMINATING.
149
150 `manager` : Manager object.
151 `state` : `State` object describing our view of state of cluster
152 `pargs` : positional arguments
153 `kwargs` : keyword arguments, which include:
154 `timeout` : how long to wait before issuing Exception
155 """
156 # check for active instances
157 vms = state.get_instances()
158 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
159 # no active vms, so return null
160 if not active_vms:
161 self._logger.info('no ACTIVE instances to delete')
162 return
163
David Kranz30fe84a2012-03-20 16:25:47 -0400164 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500165
166 target = random.choice(active_vms)
167 killtarget = target[0]
168 manager.servers_client.delete_server(killtarget['id'])
169 self._logger.info('machine %s: ACTIVE -> TERMINATING' %
170 killtarget['id'])
171 state.set_instance_state(killtarget['id'],
172 (killtarget, 'TERMINATING'))
173 return VerifyKillActiveVM(manager, state,
174 killtarget, timeout=_timeout)
175
176
David Kranz779c7f82012-05-01 16:50:32 -0400177class VerifyKillActiveVM(pending_action.PendingServerAction):
Sean Daguef237ccb2013-01-04 15:19:14 -0500178 """Verify that server was destroyed."""
David Kranz6308ec22012-02-22 09:36:48 -0500179
180 def retry(self):
181 """
182 Check to see that the server of interest is destroyed. Update
183 state to indicate that server is destroyed by deleting it from local
184 view of state.
185 """
186 tid = self._target['id']
187 # if target machine has been deleted from the state, then it was
188 # already verified to be deleted
189 if (not tid in self._state.get_instances().keys()):
190 return False
191
David Kranz6308ec22012-02-22 09:36:48 -0500192 try:
193 self._manager.servers_client.get_server(tid)
194 except Exception:
195 # if we get a 404 response, is the machine really gone?
196 target = self._target
197 self._logger.info('machine %s: DELETED [%.1f secs elapsed]' %
David Kranz779c7f82012-05-01 16:50:32 -0400198 (target['id'], self.elapsed()))
David Kranz180fed12012-03-27 14:31:29 -0400199 self._state.delete_instance_state(target['id'])
David Kranz6308ec22012-02-22 09:36:48 -0500200 return True
201
202 return False
203
204
205class TestKillAnyVM(test_case.StressTestCase):
206 """Class to destroy a random server regardless of state."""
207
208 def run(self, manager, state, *pargs, **kwargs):
209 """
210 Send an HTTP POST request to the nova cluster to destroy
211 a random server. Update state to TERMINATING.
212
213 `manager` : Manager object.
214 `state` : `State` object describing our view of state of cluster
215 `pargs` : positional arguments
216 `kwargs` : keyword arguments, which include:
217 `timeout` : how long to wait before issuing Exception
218 """
219
220 vms = state.get_instances()
221 # no vms, so return null
222 if not vms:
223 self._logger.info('no active instances to delete')
224 return
225
David Kranz30fe84a2012-03-20 16:25:47 -0400226 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500227
228 target = random.choice(vms)
229 killtarget = target[0]
230
231 manager.servers_client.delete_server(killtarget['id'])
232 self._state.set_instance_state(killtarget['id'],
233 (killtarget, 'TERMINATING'))
234 # verify object will do the same thing as the active VM
235 return VerifyKillAnyVM(manager, state, killtarget, timeout=_timeout)
236
237VerifyKillAnyVM = VerifyKillActiveVM
238
239
240class TestUpdateVMName(test_case.StressTestCase):
Sean Daguef237ccb2013-01-04 15:19:14 -0500241 """Class to change the name of the active server."""
David Kranz6308ec22012-02-22 09:36:48 -0500242 def run(self, manager, state, *pargs, **kwargs):
243 """
244 Issue HTTP POST request to change the name of active server.
245 Update state of server to reflect name changing.
246
247 `manager` : Manager object.
248 `state` : `State` object describing our view of state of cluster
249 `pargs` : positional arguments
250 `kwargs` : keyword arguments, which include:
251 `timeout` : how long to wait before issuing Exception
252 """
253
254 # select one machine from active ones
255 vms = state.get_instances()
256 active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
257 # no active vms, so return null
258 if not active_vms:
259 self._logger.info('no active instances to update')
260 return
261
David Kranz30fe84a2012-03-20 16:25:47 -0400262 _timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
David Kranz6308ec22012-02-22 09:36:48 -0500263
264 target = random.choice(active_vms)
265 update_target = target[0]
266
267 # Update name by appending '_updated' to the name
268 new_name = update_target['name'] + '_updated'
269 (response, body) = \
270 manager.servers_client.update_server(update_target['id'],
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800271 name=new_name)
David Kranz6308ec22012-02-22 09:36:48 -0500272 if (response.status != 200):
273 self._logger.error("response: %s " % response)
274 self._logger.error("body: %s " % body)
275 raise Exception
276
277 assert(new_name == body['name'])
278
279 self._logger.info('machine %s: ACTIVE -> UPDATING_NAME' %
280 body['id'])
281 state.set_instance_state(body['id'],
282 (body, 'UPDATING_NAME'))
283
284 return VerifyUpdateVMName(manager,
285 state,
286 body,
287 timeout=_timeout)
288
289
David Kranz779c7f82012-05-01 16:50:32 -0400290class VerifyUpdateVMName(pending_action.PendingServerAction):
Sean Daguef237ccb2013-01-04 15:19:14 -0500291 """Check that VM has new name."""
David Kranz6308ec22012-02-22 09:36:48 -0500292 def retry(self):
293 """
294 Check that VM has new name. Update local view of `state` to RUNNING.
295 """
296 # don't run update verification
297 # if target machine has been deleted or is going to be deleted
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900298 target_id = self._target['id']
David Kranz6308ec22012-02-22 09:36:48 -0500299 if (not self._target['id'] in self._state.get_instances().keys() or
Zhongyue Luo76888ee2012-09-30 23:58:52 +0900300 self._state.get_instances()[target_id][1] == 'TERMINATING'):
David Kranz6308ec22012-02-22 09:36:48 -0500301 return False
302
David Kranz6308ec22012-02-22 09:36:48 -0500303 response, body = \
304 self._manager.serverse_client.get_server(self._target['id'])
305 if (response.status != 200):
306 self._logger.error("response: %s " % response)
307 self._logger.error("body: %s " % body)
308 raise Exception
309
310 if self._target['name'] != body['name']:
311 self._logger.error(self._target['name'] +
312 ' vs. ' +
313 body['name'])
314 raise Exception
315
316 # log the update
317 self._logger.info('machine %s: UPDATING_NAME -> ACTIVE' %
318 self._target['id'])
319 self._state.set_instance_state(self._target['id'],
320 (body,
321 'ACTIVE'))
322 return True