tempest/stress/test_servers.py

319 lines
12 KiB
Python

# Copyright 2011 Quanta Research Cambridge, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Defines various sub-classes of the `StressTestCase` and
`PendingServerAction` class. Sub-classes of StressTestCase implement various
API calls on the Nova cluster having to do with creating and deleting VMs.
Each sub-class will have a corresponding PendingServerAction. These pending
actions veriy that the API call was successful or not."""
import random
from stress import pending_action
from stress import test_case
class TestCreateVM(test_case.StressTestCase):
"""Create a virtual machine in the Nova cluster."""
_vm_id = 0
def run(self, manager, state, *pargs, **kwargs):
"""
Send an HTTP POST request to the nova cluster to build a
server. Update the state variable to track state of new server
and set to PENDING state.
`manager` : Manager object.
`state` : `State` object describing our view of state of cluster
`pargs` : positional arguments
`kwargs` : keyword arguments, which include:
`key_name` : name of keypair
`image_ref` : index to image types availablexs
`flavor_ref`: index to flavor types available
(default = 1, which is tiny)
"""
# restrict number of instances we can launch
if len(state.get_instances()) >= state.get_max_instances():
self._logger.debug("maximum number of instances created: %d" %
state.get_max_instances())
return None
_key_name = kwargs.get('key_name', '')
_image_ref = kwargs.get('image_ref', manager.config.compute.image_ref)
_flavor_ref = kwargs.get('flavor_ref',
manager.config.compute.flavor_ref)
expected_server = {
'name': 'server' + str(TestCreateVM._vm_id),
'metadata': {
'key1': 'value1',
'key2': 'value2',
},
'imageRef': _image_ref,
'flavorRef': _flavor_ref,
'adminPass': 'testpwd',
'key_name': _key_name,
}
TestCreateVM._vm_id = TestCreateVM._vm_id + 1
create_server = manager.servers_client.create_server
response, body = create_server(expected_server['name'],
_image_ref,
_flavor_ref,
meta=expected_server['metadata'],
adminPass=expected_server['adminPass'])
if (response.status != 202):
self._logger.error("response: %s" % response)
self._logger.error("body: %s" % body)
raise Exception
created_server = body
self._logger.info('setting machine %s to BUILD' %
created_server['id'])
state.set_instance_state(created_server['id'],
(created_server, 'BUILD'))
return VerifyCreateVM(manager,
state,
created_server,
expected_server)
class VerifyCreateVM(pending_action.PendingServerAction):
"""Verify that VM was built and is running."""
def __init__(self, manager,
state,
created_server,
expected_server):
super(VerifyCreateVM, self).__init__(manager,
state,
created_server,
)
self._expected = expected_server
def retry(self):
"""
Check to see that the server was created and is running.
Update local view of state to indicate that it is running.
"""
# don't run create verification
# if target machine has been deleted or is going to be deleted
target_id = self._target['id']
if (self._target['id'] not in self._state.get_instances().keys() or
self._state.get_instances()[target_id][1] == 'TERMINATING'):
self._logger.info('machine %s is deleted or TERMINATING' %
self._target['id'])
return True
admin_pass = self._target['adminPass']
# Could check more things here.
if (self._expected['adminPass'] != admin_pass):
self._logger.error('expected: %s' %
(self._expected['adminPass']))
self._logger.error('returned: %s' %
(admin_pass))
raise Exception
if self._check_for_status('ACTIVE') != 'ACTIVE':
return False
self._logger.info('machine %s: BUILD -> ACTIVE [%.1f secs elapsed]' %
(self._target['id'], self.elapsed()))
self._state.set_instance_state(self._target['id'],
(self._target, 'ACTIVE'))
return True
class TestKillActiveVM(test_case.StressTestCase):
"""Class to destroy a random ACTIVE server."""
def run(self, manager, state, *pargs, **kwargs):
"""
Send an HTTP POST request to the nova cluster to destroy
a random ACTIVE server. Update `state` to indicate TERMINATING.
`manager` : Manager object.
`state` : `State` object describing our view of state of cluster
`pargs` : positional arguments
`kwargs` : keyword arguments, which include:
`timeout` : how long to wait before issuing Exception
"""
# check for active instances
vms = state.get_instances()
active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
# no active vms, so return null
if not active_vms:
self._logger.info('no ACTIVE instances to delete')
return
_timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
target = random.choice(active_vms)
killtarget = target[0]
manager.servers_client.delete_server(killtarget['id'])
self._logger.info('machine %s: ACTIVE -> TERMINATING' %
killtarget['id'])
state.set_instance_state(killtarget['id'],
(killtarget, 'TERMINATING'))
return VerifyKillActiveVM(manager, state,
killtarget, timeout=_timeout)
class VerifyKillActiveVM(pending_action.PendingServerAction):
"""Verify that server was destroyed."""
def retry(self):
"""
Check to see that the server of interest is destroyed. Update
state to indicate that server is destroyed by deleting it from local
view of state.
"""
tid = self._target['id']
# if target machine has been deleted from the state, then it was
# already verified to be deleted
if (not tid in self._state.get_instances().keys()):
return False
try:
self._manager.servers_client.get_server(tid)
except Exception:
# if we get a 404 response, is the machine really gone?
target = self._target
self._logger.info('machine %s: DELETED [%.1f secs elapsed]' %
(target['id'], self.elapsed()))
self._state.delete_instance_state(target['id'])
return True
return False
class TestKillAnyVM(test_case.StressTestCase):
"""Class to destroy a random server regardless of state."""
def run(self, manager, state, *pargs, **kwargs):
"""
Send an HTTP POST request to the nova cluster to destroy
a random server. Update state to TERMINATING.
`manager` : Manager object.
`state` : `State` object describing our view of state of cluster
`pargs` : positional arguments
`kwargs` : keyword arguments, which include:
`timeout` : how long to wait before issuing Exception
"""
vms = state.get_instances()
# no vms, so return null
if not vms:
self._logger.info('no active instances to delete')
return
_timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
target = random.choice(vms)
killtarget = target[0]
manager.servers_client.delete_server(killtarget['id'])
self._state.set_instance_state(killtarget['id'],
(killtarget, 'TERMINATING'))
# verify object will do the same thing as the active VM
return VerifyKillAnyVM(manager, state, killtarget, timeout=_timeout)
VerifyKillAnyVM = VerifyKillActiveVM
class TestUpdateVMName(test_case.StressTestCase):
"""Class to change the name of the active server."""
def run(self, manager, state, *pargs, **kwargs):
"""
Issue HTTP POST request to change the name of active server.
Update state of server to reflect name changing.
`manager` : Manager object.
`state` : `State` object describing our view of state of cluster
`pargs` : positional arguments
`kwargs` : keyword arguments, which include:
`timeout` : how long to wait before issuing Exception
"""
# select one machine from active ones
vms = state.get_instances()
active_vms = [v for k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
# no active vms, so return null
if not active_vms:
self._logger.info('no active instances to update')
return
_timeout = kwargs.get('timeout', manager.config.compute.build_timeout)
target = random.choice(active_vms)
update_target = target[0]
# Update name by appending '_updated' to the name
new_name = update_target['name'] + '_updated'
(response, body) = \
manager.servers_client.update_server(update_target['id'],
name=new_name)
if (response.status != 200):
self._logger.error("response: %s " % response)
self._logger.error("body: %s " % body)
raise Exception
assert(new_name == body['name'])
self._logger.info('machine %s: ACTIVE -> UPDATING_NAME' %
body['id'])
state.set_instance_state(body['id'],
(body, 'UPDATING_NAME'))
return VerifyUpdateVMName(manager,
state,
body,
timeout=_timeout)
class VerifyUpdateVMName(pending_action.PendingServerAction):
"""Check that VM has new name."""
def retry(self):
"""
Check that VM has new name. Update local view of `state` to RUNNING.
"""
# don't run update verification
# if target machine has been deleted or is going to be deleted
target_id = self._target['id']
if (not self._target['id'] in self._state.get_instances().keys() or
self._state.get_instances()[target_id][1] == 'TERMINATING'):
return False
response, body = \
self._manager.serverse_client.get_server(self._target['id'])
if (response.status != 200):
self._logger.error("response: %s " % response)
self._logger.error("body: %s " % body)
raise Exception
if self._target['name'] != body['name']:
self._logger.error(self._target['name'] +
' vs. ' +
body['name'])
raise Exception
# log the update
self._logger.info('machine %s: UPDATING_NAME -> ACTIVE' %
self._target['id'])
self._state.set_instance_state(self._target['id'],
(body,
'ACTIVE'))
return True