fuel-ostf/fuel_health/common/test_mixins.py

226 lines
8.5 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 Mirantis, 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.
import signal
import traceback
from fuel_health.common import log as logging
LOG = logging.getLogger(__name__)
class FuelTestAssertMixin(object):
"""Mixin class with a set of assert methods created to abstract
from unittest assertion methods and provide human
readable descriptions where possible
"""
def verify_response_status(self, status,
appl='Application', msg='', failed_step=''):
"""Method provides human readable message
for the HTTP response status verification
:param appl: the name of application requested
:param status: response status
:param msg: message to be used instead the default one
:failed_step: the step of the test scenario that has failed
"""
if status in [200, 201, 202]:
return
human_readable_statuses = {
400: ('Something changed in {appl} and request is no '
'longer recognized as valid. Please verify that you '
'are not sending an HTTP request to an HTTPS socket'),
401: 'Unauthorized, please check Keystone and {appl} connectivity',
403: ('Forbidden, please check if Keystone and {appl} '
'security policies have changed'),
404: '{appl} server is running but the application is not found',
500: '{appl} server is experiencing some problems',
503: '{appl} server is experiencing problems'
}
human_readable_status_groups = {
3: ('Status {status}. Redirection. Please check that all {appl}'
' proxy settings are correct'),
4: ('Status {status}. Client error. Please verify that your {appl}'
' configuration corresponds to the one defined in '
'Fuel configuration '),
5: 'Status {status}. Server error. Please check {appl} logs'
}
unknown_msg = '{appl} status - {status} is unknown'
if status in human_readable_statuses:
status_msg = human_readable_statuses[status].format(
status=status, appl=appl)
else:
status_msg = human_readable_status_groups.get(
status / 100, unknown_msg).format(status=status, appl=appl)
failed_step_msg = ''
if failed_step:
failed_step_msg = ('Step %s failed: ' % str(failed_step))
self.fail(''.join((failed_step_msg +
'Status - {status} '.format(
status=status), status_msg, '\n', msg)))
def verify_response_body(self, body, content='', msg='', failed_step=''):
"""Method provides human readable message for the verification if
HTTP response body contains desired keyword
:param body: response body
:param content: content type that should be present in response body
:param msg: message to be used instead the default one
"""
if content in body:
return
if failed_step:
msg = ('Step %s failed: ' % str(failed_step)) + msg
self.fail(msg)
def verify_response_body_value(self, body_structure, value='', msg='',
failed_step=''):
"""Method provides human readable message for verification if
HTTP response body element contains desired keyword.
:param body_structure: body element value (e.g. body['name'], body);
:param value: expected value of body element (e.g. u'test-flavor');
:param msg: message to be used instead of the default one.
"""
if type(body_structure) is dict:
if value in body_structure.values():
return
else:
if body_structure == value:
return
failed_step_msg = ''
if failed_step:
failed_step_msg = ('Step {step} failed: {msg}{refer}'.format(
step=str(failed_step),
msg=msg,
refer=" Please refer to OpenStack"
" logs for more details."))
self.fail(failed_step_msg)
def verify_response_body_content(self, exp_content, act_content, msg='',
failed_step=''):
failed_step_msg = ''
if exp_content == act_content:
return
if failed_step:
failed_step_msg = ('Step %s failed: ' % str(failed_step))
self.fail(
''.join(
failed_step_msg + 'Actual value - {actual_content}'.format(
actual_content=act_content), '\n', msg))
def verify_elements_list(self, elements, attrs, msg='', failed_step=''):
"""Method provides human readable message for the verification of
list of elements with specific parameters
:param elements: the list of elements from response
:param attrs: required attributes for each element
:param msg: message to be used instead the default one
:param failed_step: step with failed action
"""
msg = "Step {0} failed: {1}".format(failed_step, msg)
self.verify_response_true(elements, msg)
for element in elements:
for attribute in attrs:
if not hasattr(element, attribute):
self.fail(msg)
def verify_response_body_not_equal(self, exp_content, act_content, msg='',
failed_step=''):
failed_step_msg = ''
if exp_content != act_content:
return
if failed_step:
failed_step_msg = ('Step %s failed: ' % str(failed_step))
self.fail(''.join(
failed_step_msg + 'Actual value - {actual_content}'.format(
actual_content=act_content), '\n', msg))
def verify_response_true(self, resp, msg, failed_step=''):
if resp:
return
failed_step_msg = ''
if failed_step:
failed_step_msg = ('Step %s failed: ' % str(failed_step))
message = "{0}{1}. Please refer to OpenStack logs for more details."
self.fail(message.format(failed_step_msg, msg))
def verify(self, secs, func, step='', msg='', action='', *args, **kwargs):
"""Arguments:
:secs: timeout time;
:func: function to be verified;
:step: number of test step;
:msg: message that will be displayed if an exception occurs;
:action: action that is performed by the method.
"""
try:
with timeout(secs, action):
result = func(*args, **kwargs)
except Exception as exc:
LOG.debug(traceback.format_exc())
if type(exc) is AssertionError:
msg = str(exc)
self.fail("Step %s failed: " % step + msg +
" Please refer to OpenStack logs for more details.")
else:
return result
class TimeOutError(Exception):
def __init__(self):
Exception.__init__(self)
def _raise_TimeOut(sig, stack):
raise TimeOutError()
class timeout(object):
"""Timeout context that will stop code running within context
if timeout is reached
>>with timeout(2):
... requests.get("http://msdn.com")
"""
def __init__(self, timeout, action):
self.timeout = timeout
self.action = action
def __enter__(self):
signal.signal(signal.SIGALRM, _raise_TimeOut)
signal.alarm(self.timeout)
def __exit__(self, exc_type, exc_val, exc_tb):
signal.alarm(0) # disable the alarm
if exc_type is not TimeOutError:
return False # never swallow other exceptions
else:
LOG.info("Timeout {timeout}s exceeded for {call}".format(
call=self.action,
timeout=self.timeout
))
msg = ("Time limit exceeded while waiting for {call} to "
"finish.").format(call=self.action)
raise AssertionError(msg)