merged trunk
This commit is contained in:
@@ -605,3 +605,7 @@ class InstanceExists(Duplicate):
|
|||||||
|
|
||||||
class MigrationError(NovaException):
|
class MigrationError(NovaException):
|
||||||
message = _("Migration error") + ": %(reason)s"
|
message = _("Migration error") + ": %(reason)s"
|
||||||
|
|
||||||
|
|
||||||
|
class MalformedRequestBody(NovaException):
|
||||||
|
message = _("Malformed message body: %(reason)s")
|
||||||
|
|||||||
@@ -106,12 +106,14 @@ def _wrap_method(function, self):
|
|||||||
def _process(func, zone):
|
def _process(func, zone):
|
||||||
"""Worker stub for green thread pool. Give the worker
|
"""Worker stub for green thread pool. Give the worker
|
||||||
an authenticated nova client and zone info."""
|
an authenticated nova client and zone info."""
|
||||||
nova = novaclient.OpenStack(zone.username, zone.password, zone.api_url)
|
nova = novaclient.OpenStack(zone.username, zone.password, None,
|
||||||
|
zone.api_url)
|
||||||
nova.authenticate()
|
nova.authenticate()
|
||||||
return func(nova, zone)
|
return func(nova, zone)
|
||||||
|
|
||||||
|
|
||||||
def call_zone_method(context, method, errors_to_ignore=None, *args, **kwargs):
|
def call_zone_method(context, method_name, errors_to_ignore=None,
|
||||||
|
novaclient_collection_name='zones', *args, **kwargs):
|
||||||
"""Returns a list of (zone, call_result) objects."""
|
"""Returns a list of (zone, call_result) objects."""
|
||||||
if not isinstance(errors_to_ignore, (list, tuple)):
|
if not isinstance(errors_to_ignore, (list, tuple)):
|
||||||
# This will also handle the default None
|
# This will also handle the default None
|
||||||
@@ -121,7 +123,7 @@ def call_zone_method(context, method, errors_to_ignore=None, *args, **kwargs):
|
|||||||
results = []
|
results = []
|
||||||
for zone in db.zone_get_all(context):
|
for zone in db.zone_get_all(context):
|
||||||
try:
|
try:
|
||||||
nova = novaclient.OpenStack(zone.username, zone.password,
|
nova = novaclient.OpenStack(zone.username, zone.password, None,
|
||||||
zone.api_url)
|
zone.api_url)
|
||||||
nova.authenticate()
|
nova.authenticate()
|
||||||
except novaclient.exceptions.BadRequest, e:
|
except novaclient.exceptions.BadRequest, e:
|
||||||
@@ -131,18 +133,16 @@ def call_zone_method(context, method, errors_to_ignore=None, *args, **kwargs):
|
|||||||
#TODO (dabo) - add logic for failure counts per zone,
|
#TODO (dabo) - add logic for failure counts per zone,
|
||||||
# with escalation after a given number of failures.
|
# with escalation after a given number of failures.
|
||||||
continue
|
continue
|
||||||
zone_method = getattr(nova.zones, method)
|
novaclient_collection = getattr(nova, novaclient_collection_name)
|
||||||
|
collection_method = getattr(novaclient_collection, method_name)
|
||||||
|
|
||||||
def _error_trap(*args, **kwargs):
|
def _error_trap(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
return zone_method(*args, **kwargs)
|
return collection_method(*args, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if type(e) in errors_to_ignore:
|
if type(e) in errors_to_ignore:
|
||||||
return None
|
return None
|
||||||
# TODO (dabo) - want to be able to re-raise here.
|
raise
|
||||||
# Returning a string now; raising was causing issues.
|
|
||||||
# raise e
|
|
||||||
return "ERROR", "%s" % e
|
|
||||||
|
|
||||||
res = pool.spawn(_error_trap, *args, **kwargs)
|
res = pool.spawn(_error_trap, *args, **kwargs)
|
||||||
results.append((zone, res))
|
results.append((zone, res))
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ class SchedulerManager(manager.Manager):
|
|||||||
host = getattr(self.driver, driver_method)(elevated, *args,
|
host = getattr(self.driver, driver_method)(elevated, *args,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
except AttributeError, e:
|
except AttributeError, e:
|
||||||
LOG.exception(_("Driver Method %(driver_method)s missing: %(e)s")
|
LOG.warning(_("Driver Method %(driver_method)s missing: %(e)s."
|
||||||
% locals())
|
"Reverting to schedule()") % locals())
|
||||||
host = self.driver.schedule(elevated, topic, *args, **kwargs)
|
host = self.driver.schedule(elevated, topic, *args, **kwargs)
|
||||||
|
|
||||||
if not host:
|
if not host:
|
||||||
|
|||||||
@@ -88,9 +88,10 @@ class ZoneAwareScheduler(driver.Scheduler):
|
|||||||
instance_properties = request_spec['instance_properties']
|
instance_properties = request_spec['instance_properties']
|
||||||
|
|
||||||
name = instance_properties['display_name']
|
name = instance_properties['display_name']
|
||||||
image_id = instance_properties['image_id']
|
image_ref = instance_properties['image_ref']
|
||||||
meta = instance_properties['metadata']
|
meta = instance_properties['metadata']
|
||||||
flavor_id = instance_type['flavorid']
|
flavor_id = instance_type['flavorid']
|
||||||
|
reservation_id = instance_properties['reservation_id']
|
||||||
|
|
||||||
files = kwargs['injected_files']
|
files = kwargs['injected_files']
|
||||||
ipgroup = None # Not supported in OS API ... yet
|
ipgroup = None # Not supported in OS API ... yet
|
||||||
@@ -99,18 +100,20 @@ class ZoneAwareScheduler(driver.Scheduler):
|
|||||||
child_blob = zone_info['child_blob']
|
child_blob = zone_info['child_blob']
|
||||||
zone = db.zone_get(context, child_zone)
|
zone = db.zone_get(context, child_zone)
|
||||||
url = zone.api_url
|
url = zone.api_url
|
||||||
LOG.debug(_("Forwarding instance create call to child zone %(url)s")
|
LOG.debug(_("Forwarding instance create call to child zone %(url)s"
|
||||||
|
". ReservationID=%(reservation_id)s")
|
||||||
% locals())
|
% locals())
|
||||||
nova = None
|
nova = None
|
||||||
try:
|
try:
|
||||||
nova = novaclient.OpenStack(zone.username, zone.password, url)
|
nova = novaclient.OpenStack(zone.username, zone.password, None,
|
||||||
|
url)
|
||||||
nova.authenticate()
|
nova.authenticate()
|
||||||
except novaclient.exceptions.BadRequest, e:
|
except novaclient.exceptions.BadRequest, e:
|
||||||
raise exception.NotAuthorized(_("Bad credentials attempting "
|
raise exception.NotAuthorized(_("Bad credentials attempting "
|
||||||
"to talk to zone at %(url)s.") % locals())
|
"to talk to zone at %(url)s.") % locals())
|
||||||
|
|
||||||
nova.servers.create(name, image_id, flavor_id, ipgroup, meta, files,
|
nova.servers.create(name, image_ref, flavor_id, ipgroup, meta, files,
|
||||||
child_blob)
|
child_blob, reservation_id=reservation_id)
|
||||||
|
|
||||||
def _provision_resource_from_blob(self, context, item, instance_id,
|
def _provision_resource_from_blob(self, context, item, instance_id,
|
||||||
request_spec, kwargs):
|
request_spec, kwargs):
|
||||||
@@ -182,7 +185,11 @@ class ZoneAwareScheduler(driver.Scheduler):
|
|||||||
if not build_plan:
|
if not build_plan:
|
||||||
raise driver.NoValidHost(_('No hosts were available'))
|
raise driver.NoValidHost(_('No hosts were available'))
|
||||||
|
|
||||||
for item in build_plan:
|
for num in xrange(request_spec['num_instances']):
|
||||||
|
if not build_plan:
|
||||||
|
break
|
||||||
|
|
||||||
|
item = build_plan.pop(0)
|
||||||
self._provision_resource(context, item, instance_id, request_spec,
|
self._provision_resource(context, item, instance_id, request_spec,
|
||||||
kwargs)
|
kwargs)
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,8 @@ class ZoneState(object):
|
|||||||
|
|
||||||
def _call_novaclient(zone):
|
def _call_novaclient(zone):
|
||||||
"""Call novaclient. Broken out for testing purposes."""
|
"""Call novaclient. Broken out for testing purposes."""
|
||||||
client = novaclient.OpenStack(zone.username, zone.password, zone.api_url)
|
client = novaclient.OpenStack(zone.username, zone.password, None,
|
||||||
|
zone.api_url)
|
||||||
return client.zones.info()._info
|
return client.zones.info()._info
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1109,10 +1109,4 @@ class CallZoneMethodTest(test.TestCase):
|
|||||||
def test_call_zone_method_generates_exception(self):
|
def test_call_zone_method_generates_exception(self):
|
||||||
context = {}
|
context = {}
|
||||||
method = 'raises_exception'
|
method = 'raises_exception'
|
||||||
results = api.call_zone_method(context, method)
|
self.assertRaises(Exception, api.call_zone_method, context, method)
|
||||||
|
|
||||||
# FIXME(sirp): for now the _error_trap code is catching errors and
|
|
||||||
# converting them to a ("ERROR", "string") tuples. The code (and this
|
|
||||||
# test) should eventually handle real exceptions.
|
|
||||||
expected = [(1, ('ERROR', 'testing'))]
|
|
||||||
self.assertEqual(expected, results)
|
|
||||||
|
|||||||
92
run_tests.py
92
run_tests.py
@@ -56,9 +56,11 @@ To run a single test module:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
|
import heapq
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
gettext.install('nova', unicode=1)
|
gettext.install('nova', unicode=1)
|
||||||
|
|
||||||
@@ -183,9 +185,21 @@ class _NullColorizer(object):
|
|||||||
self.stream.write(text)
|
self.stream.write(text)
|
||||||
|
|
||||||
|
|
||||||
|
def get_elapsed_time_color(elapsed_time):
|
||||||
|
if elapsed_time > 1.0:
|
||||||
|
return 'red'
|
||||||
|
elif elapsed_time > 0.25:
|
||||||
|
return 'yellow'
|
||||||
|
else:
|
||||||
|
return 'green'
|
||||||
|
|
||||||
|
|
||||||
class NovaTestResult(result.TextTestResult):
|
class NovaTestResult(result.TextTestResult):
|
||||||
def __init__(self, *args, **kw):
|
def __init__(self, *args, **kw):
|
||||||
|
self.show_elapsed = kw.pop('show_elapsed')
|
||||||
result.TextTestResult.__init__(self, *args, **kw)
|
result.TextTestResult.__init__(self, *args, **kw)
|
||||||
|
self.num_slow_tests = 5
|
||||||
|
self.slow_tests = [] # this is a fixed-sized heap
|
||||||
self._last_case = None
|
self._last_case = None
|
||||||
self.colorizer = None
|
self.colorizer = None
|
||||||
# NOTE(vish): reset stdout for the terminal check
|
# NOTE(vish): reset stdout for the terminal check
|
||||||
@@ -200,25 +214,40 @@ class NovaTestResult(result.TextTestResult):
|
|||||||
def getDescription(self, test):
|
def getDescription(self, test):
|
||||||
return str(test)
|
return str(test)
|
||||||
|
|
||||||
|
def _handleElapsedTime(self, test):
|
||||||
|
self.elapsed_time = time.time() - self.start_time
|
||||||
|
item = (self.elapsed_time, test)
|
||||||
|
# Record only the n-slowest tests using heap
|
||||||
|
if len(self.slow_tests) >= self.num_slow_tests:
|
||||||
|
heapq.heappushpop(self.slow_tests, item)
|
||||||
|
else:
|
||||||
|
heapq.heappush(self.slow_tests, item)
|
||||||
|
|
||||||
|
def _writeElapsedTime(self, test):
|
||||||
|
color = get_elapsed_time_color(self.elapsed_time)
|
||||||
|
self.colorizer.write(" %.2f" % self.elapsed_time, color)
|
||||||
|
|
||||||
|
def _writeResult(self, test, long_result, color, short_result, success):
|
||||||
|
if self.showAll:
|
||||||
|
self.colorizer.write(long_result, color)
|
||||||
|
if self.show_elapsed and success:
|
||||||
|
self._writeElapsedTime(test)
|
||||||
|
self.stream.writeln()
|
||||||
|
elif self.dots:
|
||||||
|
self.stream.write(short_result)
|
||||||
|
self.stream.flush()
|
||||||
|
|
||||||
# NOTE(vish): copied from unittest with edit to add color
|
# NOTE(vish): copied from unittest with edit to add color
|
||||||
def addSuccess(self, test):
|
def addSuccess(self, test):
|
||||||
unittest.TestResult.addSuccess(self, test)
|
unittest.TestResult.addSuccess(self, test)
|
||||||
if self.showAll:
|
self._handleElapsedTime(test)
|
||||||
self.colorizer.write("OK", 'green')
|
self._writeResult(test, 'OK', 'green', '.', True)
|
||||||
self.stream.writeln()
|
|
||||||
elif self.dots:
|
|
||||||
self.stream.write('.')
|
|
||||||
self.stream.flush()
|
|
||||||
|
|
||||||
# NOTE(vish): copied from unittest with edit to add color
|
# NOTE(vish): copied from unittest with edit to add color
|
||||||
def addFailure(self, test, err):
|
def addFailure(self, test, err):
|
||||||
unittest.TestResult.addFailure(self, test, err)
|
unittest.TestResult.addFailure(self, test, err)
|
||||||
if self.showAll:
|
self._handleElapsedTime(test)
|
||||||
self.colorizer.write("FAIL", 'red')
|
self._writeResult(test, 'FAIL', 'red', 'F', False)
|
||||||
self.stream.writeln()
|
|
||||||
elif self.dots:
|
|
||||||
self.stream.write('F')
|
|
||||||
self.stream.flush()
|
|
||||||
|
|
||||||
# NOTE(vish): copied from nose with edit to add color
|
# NOTE(vish): copied from nose with edit to add color
|
||||||
def addError(self, test, err):
|
def addError(self, test, err):
|
||||||
@@ -226,6 +255,7 @@ class NovaTestResult(result.TextTestResult):
|
|||||||
errorClasses. If the exception is a registered class, the
|
errorClasses. If the exception is a registered class, the
|
||||||
error will be added to the list for that class, not errors.
|
error will be added to the list for that class, not errors.
|
||||||
"""
|
"""
|
||||||
|
self._handleElapsedTime(test)
|
||||||
stream = getattr(self, 'stream', None)
|
stream = getattr(self, 'stream', None)
|
||||||
ec, ev, tb = err
|
ec, ev, tb = err
|
||||||
try:
|
try:
|
||||||
@@ -252,14 +282,11 @@ class NovaTestResult(result.TextTestResult):
|
|||||||
self.errors.append((test, exc_info))
|
self.errors.append((test, exc_info))
|
||||||
test.passed = False
|
test.passed = False
|
||||||
if stream is not None:
|
if stream is not None:
|
||||||
if self.showAll:
|
self._writeResult(test, 'ERROR', 'red', 'E', False)
|
||||||
self.colorizer.write("ERROR", 'red')
|
|
||||||
self.stream.writeln()
|
|
||||||
elif self.dots:
|
|
||||||
stream.write('E')
|
|
||||||
|
|
||||||
def startTest(self, test):
|
def startTest(self, test):
|
||||||
unittest.TestResult.startTest(self, test)
|
unittest.TestResult.startTest(self, test)
|
||||||
|
self.start_time = time.time()
|
||||||
current_case = test.test.__class__.__name__
|
current_case = test.test.__class__.__name__
|
||||||
|
|
||||||
if self.showAll:
|
if self.showAll:
|
||||||
@@ -273,21 +300,47 @@ class NovaTestResult(result.TextTestResult):
|
|||||||
|
|
||||||
|
|
||||||
class NovaTestRunner(core.TextTestRunner):
|
class NovaTestRunner(core.TextTestRunner):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.show_elapsed = kwargs.pop('show_elapsed')
|
||||||
|
core.TextTestRunner.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
def _makeResult(self):
|
def _makeResult(self):
|
||||||
return NovaTestResult(self.stream,
|
return NovaTestResult(self.stream,
|
||||||
self.descriptions,
|
self.descriptions,
|
||||||
self.verbosity,
|
self.verbosity,
|
||||||
self.config)
|
self.config,
|
||||||
|
show_elapsed=self.show_elapsed)
|
||||||
|
|
||||||
|
def _writeSlowTests(self, result_):
|
||||||
|
# Pare out 'fast' tests
|
||||||
|
slow_tests = [item for item in result_.slow_tests
|
||||||
|
if get_elapsed_time_color(item[0]) != 'green']
|
||||||
|
if slow_tests:
|
||||||
|
slow_total_time = sum(item[0] for item in slow_tests)
|
||||||
|
self.stream.writeln("Slowest %i tests took %.2f secs:"
|
||||||
|
% (len(slow_tests), slow_total_time))
|
||||||
|
for elapsed_time, test in sorted(slow_tests, reverse=True):
|
||||||
|
time_str = "%.2f" % elapsed_time
|
||||||
|
self.stream.writeln(" %s %s" % (time_str.ljust(10), test))
|
||||||
|
|
||||||
|
def run(self, test):
|
||||||
|
result_ = core.TextTestRunner.run(self, test)
|
||||||
|
if self.show_elapsed:
|
||||||
|
self._writeSlowTests(result_)
|
||||||
|
return result_
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
logging.setup()
|
logging.setup()
|
||||||
# If any argument looks like a test name but doesn't have "nova.tests" in
|
# If any argument looks like a test name but doesn't have "nova.tests" in
|
||||||
# front of it, automatically add that so we don't have to type as much
|
# front of it, automatically add that so we don't have to type as much
|
||||||
|
show_elapsed = True
|
||||||
argv = []
|
argv = []
|
||||||
for x in sys.argv:
|
for x in sys.argv:
|
||||||
if x.startswith('test_'):
|
if x.startswith('test_'):
|
||||||
argv.append('nova.tests.%s' % x)
|
argv.append('nova.tests.%s' % x)
|
||||||
|
elif x.startswith('--hide-elapsed'):
|
||||||
|
show_elapsed = False
|
||||||
else:
|
else:
|
||||||
argv.append(x)
|
argv.append(x)
|
||||||
|
|
||||||
@@ -300,5 +353,6 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
runner = NovaTestRunner(stream=c.stream,
|
runner = NovaTestRunner(stream=c.stream,
|
||||||
verbosity=c.verbosity,
|
verbosity=c.verbosity,
|
||||||
config=c)
|
config=c,
|
||||||
|
show_elapsed=show_elapsed)
|
||||||
sys.exit(not core.run(config=c, testRunner=runner, argv=argv))
|
sys.exit(not core.run(config=c, testRunner=runner, argv=argv))
|
||||||
|
|||||||
10
run_tests.sh
10
run_tests.sh
@@ -10,6 +10,7 @@ function usage {
|
|||||||
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
||||||
echo " -p, --pep8 Just run pep8"
|
echo " -p, --pep8 Just run pep8"
|
||||||
echo " -h, --help Print this usage message"
|
echo " -h, --help Print this usage message"
|
||||||
|
echo " --hide-elapsed Don't print the elapsed time for each test along with slow test list"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
|
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
|
||||||
echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
|
echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
|
||||||
@@ -24,6 +25,7 @@ function process_option {
|
|||||||
-N|--no-virtual-env) let always_venv=0; let never_venv=1;;
|
-N|--no-virtual-env) let always_venv=0; let never_venv=1;;
|
||||||
-f|--force) let force=1;;
|
-f|--force) let force=1;;
|
||||||
-p|--pep8) let just_pep8=1;;
|
-p|--pep8) let just_pep8=1;;
|
||||||
|
-*) noseopts="$noseopts $1";;
|
||||||
*) noseargs="$noseargs $1"
|
*) noseargs="$noseargs $1"
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -34,6 +36,7 @@ always_venv=0
|
|||||||
never_venv=0
|
never_venv=0
|
||||||
force=0
|
force=0
|
||||||
noseargs=
|
noseargs=
|
||||||
|
noseopts=
|
||||||
wrapper=""
|
wrapper=""
|
||||||
just_pep8=0
|
just_pep8=0
|
||||||
|
|
||||||
@@ -72,7 +75,7 @@ function run_pep8 {
|
|||||||
--exclude=vcsversion.py ${srcfiles}
|
--exclude=vcsversion.py ${srcfiles}
|
||||||
}
|
}
|
||||||
|
|
||||||
NOSETESTS="python run_tests.py $noseargs"
|
NOSETESTS="python run_tests.py $noseopts $noseargs"
|
||||||
|
|
||||||
if [ $never_venv -eq 0 ]
|
if [ $never_venv -eq 0 ]
|
||||||
then
|
then
|
||||||
@@ -107,7 +110,10 @@ fi
|
|||||||
|
|
||||||
run_tests || exit
|
run_tests || exit
|
||||||
|
|
||||||
# Also run pep8 if no options were provided.
|
# NOTE(sirp): we only want to run pep8 when we're running the full-test suite,
|
||||||
|
# not when we're running tests individually. To handle this, we need to
|
||||||
|
# distinguish between options (noseopts), which begin with a '-', and
|
||||||
|
# arguments (noseargs).
|
||||||
if [ -z "$noseargs" ]; then
|
if [ -z "$noseargs" ]; then
|
||||||
run_pep8
|
run_pep8
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user