TEMPEST: refactor error handling while installation

Tempest installation doesn't contain enough information for debugging
errors, which can be occured during installation.

This patch adds:
 - logging failed command and its output to:
   `rally.verification.verifiers.tempest.tempest.check_output`;
 - handling TempestSetupFailure in validation tempest scenarios;
 - fix in hacking check for logging module.

Change-Id: Ida723c6898cb6845459169f387ca36bf280fb644
This commit is contained in:
Andrey Kurilin 2014-11-21 20:15:37 +02:00
parent 8d0cde9c7f
commit e5af73a4d2
7 changed files with 70 additions and 28 deletions

View File

@ -14,7 +14,6 @@
# under the License.
import itertools
import logging
import time
import traceback
@ -22,6 +21,7 @@ from novaclient import exceptions as nova_exc
from novaclient.v1_1 import servers
from rally import exceptions
from rally import log as logging
LOG = logging.getLogger(__name__)

View File

@ -292,11 +292,14 @@ def tempest_tests_exists(config, clients, task):
if not tests:
return ValidationResult(False,
"Parameter 'test_name' or 'test_names' should "
"be specified.")
_("Parameter 'test_name' or 'test_names' "
"should be specified."))
verifier = tempest.Tempest(task["deployment_uuid"])
if not verifier.is_installed():
verifier.install()
try:
verifier.install()
except tempest.TempestSetupFailure as e:
return ValidationResult(False, e)
if not verifier.is_configured():
verifier.generate_config_file()

View File

@ -15,7 +15,6 @@
""" The Rally Service API. """
import logging
import os
import sys
from wsgiref import simple_server
@ -24,17 +23,17 @@ from oslo.config import cfg
from rally.aas.rest import app as rally_app
from rally.i18n import _
from rally import log
from rally import log as logging
CONF = cfg.CONF
LOG = log.getLogger(__name__)
LOG = logging.getLogger(__name__)
def main():
# Initialize configuration and logging.
CONF(sys.argv[1:], project='rally')
log.setup('rally')
logging.setup('rally')
# Prepare application and bind to the service socket.
host = CONF.rest.host
port = CONF.rest.port
@ -43,7 +42,7 @@ def main():
# Start application.
LOG.info(_('Starting server in PID %s') % os.getpid())
LOG.info(_("Configuration:"))
CONF.log_opt_values(LOG, logging.INFO)
CONF.log_opt_values(LOG, logging.logging.INFO)
try:
server.serve_forever()
except KeyboardInterrupt:

View File

@ -14,16 +14,17 @@
# under the License.
import logging
import os
import shutil
import subprocess
import sys
from oslo.config import cfg
from oslo.serialization import jsonutils
from rally import exceptions
from rally.i18n import _
from rally import log as logging
from rally import utils
from rally.verification.verifiers.tempest import config
from rally.verification.verifiers.tempest import subunit2json
@ -32,13 +33,19 @@ LOG = logging.getLogger(__name__)
class TempestSetupFailure(exceptions.RallyException):
msg_fmt = _("Unable to setup tempest: '%(message)s'")
msg_fmt = _("Unable to setup tempest: '%(message)s'.")
def check_output(*args, **kwargs):
output = subprocess.check_output(*args, **kwargs)
kwargs["stderr"] = subprocess.STDOUT
try:
output = subprocess.check_output(*args, **kwargs)
except subprocess.CalledProcessError as e:
LOG.debug("failed cmd: '%s'" % e.cmd)
LOG.debug("error output: '%s'" % e.output)
raise
if LOG.getEffectiveLevel() <= logging.DEBUG:
if cfg.CONF.rally_debug:
print(output)
@ -83,10 +90,15 @@ class Tempest(object):
self.validate_env()
print("No virtual environment found...Install the virtualenv.")
LOG.debug("Virtual environment directory: %s" % path_to_venv)
check_output("python ./tools/install_venv.py", shell=True,
cwd=self.path())
check_output("%s python setup.py install" % self.venv_wrapper,
shell=True, cwd=self.path())
try:
check_output("python ./tools/install_venv.py", shell=True,
cwd=self.path())
check_output("%s python setup.py install" % self.venv_wrapper,
shell=True, cwd=self.path())
except subprocess.CalledProcessError:
if os.path.exists(self.path(".venv")):
shutil.rmtree(self.path(".venv"))
raise TempestSetupFailure(_("failed to install virtualenv"))
def is_configured(self):
return os.path.isfile(self.config_file)
@ -106,11 +118,14 @@ class Tempest(object):
def _initialize_testr(self):
if not os.path.isdir(self.path(".testrepository")):
msg = _("Test Repository initialization.")
LOG.info(_("Starting: ") + msg)
subprocess.check_call("%s testr init" % self.venv_wrapper,
shell=True, cwd=self.path())
LOG.info(_("Completed: ") + msg)
print(_("Test Repository initialization."))
try:
check_output("%s testr init" % self.venv_wrapper,
shell=True, cwd=self.path())
except subprocess.CalledProcessError:
if os.path.exists(self.path(".testrepository")):
shutil.rmtree(self.path(".testrepository"))
raise TempestSetupFailure(_("failed to initialize testr"))
def is_installed(self):
return os.path.exists(self.path(".venv"))

View File

@ -82,10 +82,10 @@ def check_assert_methods_from_mock(logical_line, filename):
def check_import_of_logging(logical_line, filename):
"""Check correctness import of logging module N310."""
excluded_files = ["./rally/log.py"]
excluded_files = ["./rally/log.py", "./tests/unit/test_log.py"]
forbidden_imports = ["from rally.openstack.common import log",
"import rally.openstack.common.log"
"import rally.openstack.common.log",
"import logging"]
if filename not in excluded_files:

View File

@ -21,6 +21,7 @@ from rally.benchmark import validation
from rally import consts
from rally import exceptions
from rally import objects
from rally.verification.verifiers.tempest import tempest
from tests.unit import test
@ -334,6 +335,29 @@ class ValidatorsTestCase(test.TestCase):
None, task)
self.assertFalse(result.is_valid, result.msg)
@mock.patch("rally.benchmark.validation.tempest.Tempest")
@mock.patch("rally.objects.task.db.task_create")
def test_tempest_tests_exists_tempest_installation_failed(
self, mock_create, mock_tempest):
mock_create.return_value = {
'status': 'init',
'deployment_uuid': 'deployment-uuid',
'verification_log': '',
'uuid': 'task-uuid',
'created_at': '',
'failed': False,
'tag': '',
'id': 42}
mock_tempest().is_installed.return_value = False
mock_tempest().install.side_effect = tempest.TempestSetupFailure
task = objects.Task(deployment_uuid='deployment-uuid')
validator = self._unwrap_validator(validation.tempest_tests_exists)
result = validator({"args": {"test_name": "a"}}, None, task)
self.assertFalse(result.is_valid, result.msg)
mock_tempest().is_installed.assert_called_once_with()
def test_tempest_set_exists_missing_args(self):
validator = self._unwrap_validator(validation.tempest_set_exists)
result = validator({}, None, None)

View File

@ -100,10 +100,10 @@ class TempestUtilsTestCase(BaseTestCase):
mock_isdir.assert_called_once_with(self.verifier.path(".venv"))
mock_sp.assert_has_calls([
mock.call("python ./tools/install_venv.py", shell=True,
cwd=self.verifier.path()),
cwd=self.verifier.path(), stderr=subprocess.STDOUT),
mock.call("%s python setup.py install" %
self.verifier.venv_wrapper, shell=True,
cwd=self.verifier.path())])
cwd=self.verifier.path(), stderr=subprocess.STDOUT)])
@mock.patch("os.path.isdir", return_value=False)
@testtools.skipIf(sys.version_info >= (2, 7),
@ -124,8 +124,9 @@ class TempestUtilsTestCase(BaseTestCase):
self.verifier.path(".testrepository"))
self.assertFalse(mock_sp.called)
@testtools.skipIf(sys.version_info < (2, 7), "Incompatible Python Version")
@mock.patch("os.path.isdir", return_value=False)
@mock.patch(TEMPEST_PATH + ".tempest.subprocess.check_call")
@mock.patch(TEMPEST_PATH + ".tempest.subprocess.check_output")
def test__initialize_testr_when_testr_not_initialized(
self, mock_sp, mock_isdir):
self.verifier._initialize_testr()
@ -134,7 +135,7 @@ class TempestUtilsTestCase(BaseTestCase):
self.verifier.path(".testrepository"))
mock_sp.assert_called_once_with(
'%s testr init' % self.verifier.venv_wrapper, shell=True,
cwd=self.verifier.path())
cwd=self.verifier.path(), stderr=subprocess.STDOUT)
@mock.patch.object(subunit2json, 'main')
@mock.patch('os.path.isfile', return_value=False)