# 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 `PendingAction` class. The 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 PendingAction. These pending actions veriy that the API call was successful or not.""" # system imports import random import time # local imports import test_case import pending_action from tempest.exceptions import TimeoutException 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 `timeout` : how long to wait before issuing Exception `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', '') _timeout = int(kwargs.get('timeout', manager.config.compute.build_timeout)) _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 response, body = manager.servers_client.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.PendingAction): """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 if (self._target['id'] not in self._state.get_instances().keys() or self._state.get_instances()[self._target['id']][1] == 'TERMINATING'): self._logger.info('machine %s is deleted or TERMINATING' % self._target['id']) return True time_diff = time.time() - self._start_time if time_diff > self._timeout: self._logger.error('%d exceeded launch server timeout of %d' % (time_diff, self._timeout)) raise TimeoutException 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'], time.time() - self._start_time)) 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.PendingAction): """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 time_diff = time.time() - self._start_time if time_diff > self._timeout: self._logger.error('server %s: %d exceeds terminate timeout %d' % (tid, time_diff, self._timeout)) raise TimeoutException 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'], time.time() - self._start_time)) 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.PendingAction): """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 if (not self._target['id'] in self._state.get_instances().keys() or self._state.get_instances()[self._target['id']][1] == 'TERMINATING'): return False if time.time() - self._start_time > self._timeout: raise TimeoutException 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