trove-integration/tests/integration/int_tests.py

493 lines
18 KiB
Python

#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# # Copyright (c) 2011 OpenStack, LLC.
# All Rights Reserved.
#
# 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.
"""Runs the tests.
There are a few initialization issues to deal with.
The first is flags, which must be initialized before any imports. The test
configuration has the same problem (it was based on flags back when the tests
resided outside of the Nova code).
The command line is picked apart so that Nose won't see commands it isn't
compatable with, such as "--flagfile" or "--group".
This script imports all other tests to make them known to Proboscis before
passing control to proboscis.TestProgram which itself calls nose, which then
call unittest.TestProgram and exits.
If "repl" is a command line argument, then the original stdout and stderr is
saved and sys.exit is neutralized so that unittest.TestProgram will not exit
and instead sys.stdout and stderr are restored so that interactive mode can
be used.
"""
from __future__ import absolute_import
import atexit
import gettext
import logging
import os
import time
import unittest
import sys
if os.environ.get("PYDEV_DEBUG", "False") == 'True':
from pydev import pydevd
pydevd.settrace('10.0.2.2', port=7864, stdoutToServer=True,
stderrToServer=True)
def add_support_for_localization():
"""Adds support for localization in the logging.
If ../nova/__init__.py exists, add ../ to Python search path, so that
it will override what happens to be installed in
/usr/(local/)lib/python...
"""
path = os.path.join(os.path.abspath(sys.argv[0]), os.pardir, os.pardir)
possible_topdir = os.path.normpath(path)
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir)
gettext.install('nova', unicode=1)
MAIN_RUNNER = None
def initialize_rdl_config(config_file):
import optparse
from trove.common import config
from trove import version
def create_options(parser):
parser.add_option('-p', '--port', dest="port", metavar="PORT",
type=int, default=9898,
help="Port the Trove API host listens on. "
"Default: %default")
config.add_common_options(parser)
config.add_log_options(parser)
def usage():
usage = ""
oparser = optparse.OptionParser(version="%%prog %s"
% version.version_string(),
usage=usage())
create_options(oparser)
(options, args) = config.parse_options(oparser, cli_args=[config_file])
conf = config.Config.load_paste_config('trove', options, args)
config.setup_logging(options, conf)
def initialize_nova_flags(config_file):
from nova import flags
from nova import log as logging
from nova import service
from nova import utils
flags.parse_args(['int_tests'], default_config_files=[config_file])
logging.setup()
utils.monkey_patch()
def _clean_up():
"""Shuts down any services this program has started and shows results."""
from tests.util import report
report.update()
if MAIN_RUNNER is not None:
MAIN_RUNNER.on_exit()
from tests.util.services import get_running_services
for service in get_running_services():
sys.stderr.write("Stopping service ")
for c in service.cmd:
sys.stderr.write(c + " ")
sys.stderr.write("...\n\r")
service.stop()
if __name__ == '__main__':
add_support_for_localization()
# Strip non-nose arguments out before passing this to nosetests
repl = False
nose_args = []
conf_file = "~/test.conf"
show_elapsed = True
groups = []
print("RUNNING TEST ARGS : " + str(sys.argv))
extra_test_conf_lines = []
rdl_config_file = None
nova_flag_file = None
index = 0
while index < len(sys.argv):
arg = sys.argv[index]
if arg[:2] == "-i" or arg == '--repl':
repl = True
elif arg[:7] == "--conf=":
conf_file = os.path.expanduser(arg[7:])
print("Setting TEST_CONF to " + conf_file)
os.environ["TEST_CONF"] = conf_file
elif arg[:8] == "--group=":
groups.append(arg[8:])
elif arg == "--test-config":
if index >= len(sys.argv) - 1:
print('Expected an argument to follow "--test-conf".')
sys.exit()
conf_line = sys.argv[index + 1]
extra_test_conf_lines.append(conf_line)
elif arg[:11] == "--flagfile=":
pass # Don't append this...
elif arg[:14] == "--config-file=":
rdl_config_file = arg[14:]
elif arg[:13] == "--nova-flags=":
nova_flag_file = arg[13:]
elif arg.startswith('--hide-elapsed'):
show_elapsed = False
else:
nose_args.append(arg)
index += 1
# Many of the test decorators depend on configuration values, so before
# start importing modules we have to load the test config followed by the
# flag files.
from trove.tests.config import CONFIG
# Find config file.
if not "TEST_CONF" in os.environ:
raise RuntimeError("Please define an environment variable named " +
"TEST_CONF with the location to a conf file.")
file_path = os.path.expanduser(os.environ["TEST_CONF"])
if not os.path.exists(file_path):
raise RuntimeError("Could not find TEST_CONF at " + file_path + ".")
# Load config file and then any lines we read from the arguments.
CONFIG.load_from_file(file_path)
for line in extra_test_conf_lines:
CONFIG.load_from_line(line)
# Reset values imported into tests/__init__.
# TODO(tim.simpson): Stop importing them from there.
from tests import initialize_globals
initialize_globals()
from tests import WHITE_BOX
if WHITE_BOX: # If white-box testing, set up the flags.
# Handle loading up RDL's config file madness.
initialize_rdl_config(rdl_config_file)
if nova_flag_file:
initialize_nova_flags(nova_flag_file)
# Set up the report, and print out how we're running the tests.
from tests.util import report
from datetime import datetime
report.log("Trove Integration Tests, %s" % datetime.now())
report.log("Invoked via command: " + str(sys.argv))
report.log("Groups = " + str(groups))
report.log("Test conf file = %s" % os.environ["TEST_CONF"])
if WHITE_BOX:
report.log("")
report.log("Test config file = %s" % rdl_config_file)
report.log("")
report.log("sys.path:")
for path in sys.path:
report.log("\t%s" % path)
# Now that all configurations are loaded its time to import everything.
import proboscis
# TODO(tim.simpson): Import these again once white box test functionality
# is restored.
# from tests.dns import check_domain
# from tests.dns import concurrency
# from tests.dns import conversion
# The DNS stuff is problematic. Not loading the other tests allow us to
# run its functional tests only.
ADD_DOMAINS = os.environ.get("ADD_DOMAINS", "False") == 'True'
if not ADD_DOMAINS:
from tests import initialize
from tests.api import delete_all
from trove.tests.api import flavors
from trove.tests.api import versions
from trove.tests.api import instances
from trove.tests.api.instances import GROUP_START_SIMPLE
from tests.api import instances_direct
from trove.tests.api import instances_actions
from trove.tests.api import instances_mysql_down
from tests.api import instances_pagination
from trove.tests.api import instances_delete
from tests.api import instances_quotas
from tests.api import instances_states
from trove.tests.api import databases
from trove.tests.api import root
from trove.tests.api import users
from trove.tests.api import user_access
from trove.tests.api import backups
from trove.tests.api.mgmt import accounts
from trove.tests.api.mgmt import admin_required
from tests.api.mgmt import hosts
from tests.api.mgmt import update_hosts
from trove.tests.api.mgmt import instances
from trove.tests.api.mgmt import storage
from tests.dns import dns
from tests.guest import amqp_restarts
from tests.reaper import volume_reaping
from tests.scheduler import driver
from tests.scheduler import SCHEDULER_DRIVER_GROUP
from tests.volumes import driver
from tests.volumes import VOLUMES_DRIVER
from tests.compute import guest_initialize_failure
from tests.openvz import compute_reboot_vz as compute_reboot
from tests import util
from tests.smoke import instance
from tests.recreates import create_11
from tests.recreates import login
if WHITE_BOX:
from tests.volumes import volumes_create
black_box_groups = [
flavors.GROUP,
users.GROUP,
user_access.GROUP,
databases.GROUP,
root.GROUP,
"services.initialize",
"dbaas.guest.initialize", # instances.GROUP_START,
"dbaas.preinstance",
"dbaas_quotas",
"dbaas.api.security_groups",
backups.GROUP,
"dbaas.api.instances.actions.resize.instance",
"dbaas.api.instances.actions.restart",
"dbaas.api.instances.actions.stop",
"dbaas.guest.shutdown",
versions.GROUP,
"dbaas.guest.start.test",
]
proboscis.register(groups=["blackbox"],
depends_on_groups=black_box_groups)
simple_black_box_groups = [
"services.initialize",
flavors.GROUP,
versions.GROUP,
GROUP_START_SIMPLE,
"dbaas.api.mgmt.admin",
"dbaas.preinstance"
]
proboscis.register(groups=["simple_blackbox"],
depends_on_groups=simple_black_box_groups)
black_box_mgmt_groups = [
accounts.GROUP,
hosts.GROUP,
storage.GROUP,
"dbaas.api.instances.actions.reboot",
"dbaas.api.mgmt.admin",
"dbaas.api.mgmt.instances",
]
proboscis.register(groups=["blackbox_mgmt"],
depends_on_groups=black_box_mgmt_groups)
heavy_black_box_groups = [
"dbaas.api.instances.pagination",
"dbaas.api.instances.delete",
"dbaas.api.instances.status",
"dbaas.api.instances.down",
"dbaas.api.mgmt.hosts.update",
"fake.dbaas.api.mgmt.instances",
"fake.dbaas.api.mgmt.accounts.broken",
"fake.dbaas.api.mgmt.allaccounts"
]
proboscis.register(groups=["heavy_blackbox"],
depends_on_groups=heavy_black_box_groups)
# This is for the old white-box tests...
host_ovz_groups = [
"dbaas.guest",
compute_reboot.GROUP,
"dbaas.guest.dns",
SCHEDULER_DRIVER_GROUP,
VOLUMES_DRIVER,
guest_initialize_failure.GROUP,
volume_reaping.GROUP
]
if WHITE_BOX and util.should_run_rsdns_tests():
host_ovz_groups += ["rsdns.conversion", "rsdns.domains",
"rsdns.eventlet"]
proboscis.register(groups=["host.ovz"],
depends_on_groups=host_ovz_groups)
atexit.register(_clean_up)
# Set up pretty colors.
from nose import config
from nose import core
from tests.colorizer import NovaTestResult
from tests.colorizer import NovaTestRunner
from proboscis.case import TestResult as ProboscisTestResult
from proboscis import SkipTest
class IntegrationTestResult(NovaTestResult, ProboscisTestResult):
"""
Makes the pretty colors from NovaTestResult compatble with Proboscis
SkipTest, and intercepts known bugs defined in the test config.
"""
def _intercept_known_bugs(self, test, err):
name = str(test)
excuse = CONFIG.known_bugs.get(name, None)
if excuse:
tracker_id, error_string = excuse
if error_string in str(err[1]):
skip = SkipTest("KNOWN BUG: %s\n%s"
% (tracker_id, str(err[1])))
self.onError(test)
super(IntegrationTestResult, self).addSkip(test, skip)
else:
result = (RuntimeError, RuntimeError(
'Test "%s" contains known bug %s.\n'
'Expected the following error string:\n%s\n'
'What was seen was the following:\n%s\n'
'If the bug is no longer happening, please change '
'the test config.'
% (name, tracker_id, error_string, str(err))), None)
self.onError(test)
super(IntegrationTestResult, self).addError(test, result)
return True
return False
def addFailure(self, test, err):
if self._intercept_known_bugs(test, err):
return
self.onError(test)
super(IntegrationTestResult, self).addFailure(test, err)
def addError(self, test, err):
if self._intercept_known_bugs(test, err):
return
self.onError(test)
super(IntegrationTestResult, self).addError(test, err)
def addSuccess(self, test):
if self._intercept_known_bugs(test, None):
return
super(IntegrationTestResult, self).addSuccess(test)
@staticmethod
def get_doc(cls_or_func):
"""Grabs the doc abbreviated doc string."""
try:
return cls_or_func.__doc__.split("\n")[0].strip()
except (AttributeError, IndexError):
return None
def startTest(self, test):
unittest.TestResult.startTest(self, test)
self.start_time = time.time()
test_name = None
try:
entry = test.test.__proboscis_case__.entry
if entry.method:
current_class = entry.method.im_class
test_name = self.get_doc(entry.home) or entry.home.__name__
else:
current_class = entry.home
except AttributeError:
current_class = test.test.__class__
if self.showAll:
if current_class.__name__ != self._last_case:
self.stream.writeln(current_class.__name__)
self._last_case = current_class.__name__
try:
doc = self.get_doc(current_class)
except (AttributeError, IndexError):
doc = None
if doc:
self.stream.writeln(' ' + doc)
if not test_name:
if hasattr(test.test, 'shortDescription'):
test_name = test.test.shortDescription()
if not test_name:
test_name = test.test._testMethodName
self.stream.write(' %s' %
' %s' % str(test_name).ljust(60))
self.stream.flush()
class IntegrationTestRunner(NovaTestRunner):
def init(self):
self.__result = None
self.__finished = False
self.__start_time = None
def _makeResult(self):
self.__result = IntegrationTestResult(
self.stream, self.descriptions, self.verbosity, self.config,
show_elapsed=self.show_elapsed)
self.__start_time = time.time()
return self.__result
def on_exit(self):
if self.__result is None:
print("Exiting before tests even started.")
else:
if not self.__finished:
msg = "Tests aborted, trying to print available results..."
print(msg)
stop_time = time.time()
self.__result.printErrors()
self.__result.printSummary(self.__start_time, stop_time)
self.config.plugins.finalize(self.__result)
if self.show_elapsed:
self._writeSlowTests(self.__result)
def run(self, test):
result = super(IntegrationTestRunner, self).run(test)
self.__finished = True
return result
c = config.Config(stream=sys.stdout,
env=os.environ,
verbosity=3,
plugins=core.DefaultPluginManager())
runner = IntegrationTestRunner(stream=c.stream,
verbosity=c.verbosity,
config=c,
show_elapsed=show_elapsed)
runner.init()
MAIN_RUNNER = runner
if repl:
# Turn off the following "feature" of the unittest module in case
# we want to start a REPL.
sys.exit = lambda x: None
proboscis.TestProgram(argv=nose_args, groups=groups,
testRunner=runner).run_and_exit()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__