Updates tests to run in other configurations.

* Changes the reddwarf.conf.test flag so "use_nova_server_volume"
  is turned off, as it should be, and fixes a resulting fake mode bug.
* Fixes tests to not check for volume when
  "reddwarf_main_instance_has_volume" is specified in the test config.
* Changes poll_until to reuse Reddwarf implementation instead of
  rewriting it just for the tests.
* Adds tests to confirm its possible to log into a real MySQL instance.
  These are only run for "real mode" in a VM or other environment, but
  it's necessary that they live here.
* Changes the "get_address" method of the InstanceTestInfo object to
  get pulled from the test config (necessary when hitting other
  environments).
* Fixes some bugs in guest agent models.
* Deleted the pagination test. It can't run very quickly in the tox
  fake mode, so there's no point in keeping it here (it still exists
  in Reddwarf-Integration).

Change-Id: I2835762c4180e1ca594b27194564b8f993aa4066
Fixes: bug #1085188
This commit is contained in:
Tim Simpson 2012-11-30 18:01:41 -06:00
parent 46de3f0ad1
commit bec9bc586a
13 changed files with 81 additions and 280 deletions

View File

@ -80,7 +80,7 @@ agent_call_low_timeout = 5
agent_call_high_timeout = 100
server_delete_time_out=10
use_nova_server_volume = True
use_nova_server_volume = False
# ============ notifer queue kombu connection options ========================

View File

@ -2,7 +2,7 @@
"report_directory":"rdli-test-report",
"start_services": false,
"simulate_events":true,
"test_mgmt":false,
"use_local_ovz":false,
"use_venv":false,

View File

@ -16,7 +16,7 @@ import logging
from datetime import datetime
from datetime import timedelta
from reddwarf import db
from reddwarf.db import get_db_api
from reddwarf.common import config
from reddwarf.common import exception
from reddwarf.common import utils
@ -55,7 +55,7 @@ class AgentHeartBeat(dbmodels.DatabaseModelBase):
self['updated_at'] = utils.utcnow()
LOG.debug(_("Saving %s: %s") %
(self.__class__.__name__, self.__dict__))
return db.db_api.save(self)
return get_db_api().save(self)
@staticmethod
def is_active(agent):

View File

@ -39,6 +39,29 @@ FAKE = test_config.values['fake_mode']
@test(depends_on_groups=[GROUP_START],
groups=[tests.INSTANCES, "dbaas.guest.mysql"],
enabled=not test_config.values['fake_mode'])
class TestMysqlAccess(object):
"""
Make sure that MySQL server was secured.
"""
@time_out(60 * 2)
@test
def test_mysql_admin(self):
"""Ensure we aren't allowed access with os_admin and wrong password."""
assert_mysql_connection_fails("os_admin", "asdfd-asdf234",
instance_info.get_address())
@test
def test_mysql_root(self):
"""Ensure we aren't allowed access with root and wrong password."""
assert_mysql_connection_fails("root", "dsfgnear",
instance_info.get_address())
@test(depends_on_groups=[GROUP_START],
depends_on_classes=[TestMysqlAccess],
groups=[tests.DBAAS_API, GROUP, tests.INSTANCES])
class TestDatabases(object):
"""

View File

@ -97,10 +97,7 @@ class InstanceTestInfo(object):
def get_address(self):
result = self.dbaas_admin.mgmt.instances.show(self.id)
addresses = result.server['addresses']
if "infranet" in addresses:
address = addresses['infranet'][0]
else:
address = addresses['private'][0]
address = addresses[test_config.visible_address_group][0]
return address['addr']
def get_local_id(self):
@ -445,7 +442,7 @@ class WaitForGuestInstallationToFinish(object):
Wait until the Guest is finished installing. It takes quite a while...
"""
@test(groups=['lemon'])
@test
@time_out(60 * 16)
def test_instance_created(self):
# This version just checks the REST API status.
@ -715,7 +712,7 @@ class CheckDiagnosticsAfterTests(object):
class DeleteInstance(object):
""" Delete the created instance """
@time_out(3)
@time_out(3 * 60)
@test(runs_after_groups=[GROUP_START, GROUP_TEST, tests.INSTANCES])
def test_delete(self):
if do_not_delete_instance():

View File

@ -313,6 +313,8 @@ class StopTests(RebootTestBase):
Confirms the get call behaves appropriately while an instance is
down.
"""
if not CONFIG.reddwarf_main_instance_has_volume:
raise SkipTest("Not testing volumes.")
instance = self.dbaas.instances.get(self.instance_id)
with TypeCheck("instance", instance) as check:
check.has_field("volume", dict)
@ -322,8 +324,7 @@ class StopTests(RebootTestBase):
check.true(isinstance(instance.volume.get('used', None), float))
@test(depends_on=[test_set_up],
runs_after=[test_instance_get_shows_volume_info_while_mysql_is_down],
groups=['donut'])
runs_after=[test_instance_get_shows_volume_info_while_mysql_is_down])
def test_successful_restart_when_in_shutdown_state(self):
"""Restart MySQL via the REST API successfully when MySQL is down."""
self.successful_restart()
@ -486,7 +487,8 @@ def resize_should_not_delete_users():
@test(depends_on_classes=[ResizeInstanceTest], depends_on=[create_user],
groups=[GROUP, tests.INSTANCES])
groups=[GROUP, tests.INSTANCES],
enabled=CONFIG.reddwarf_main_instance_has_volume)
class ResizeInstanceVolume(object):
""" Resize the volume of the instance """

View File

@ -1,227 +0,0 @@
from proboscis.decorators import time_out
from proboscis import after_class
from proboscis import before_class
from proboscis import test
from proboscis.asserts import assert_equal
from proboscis.asserts import assert_false
from proboscis.asserts import assert_is
from proboscis.asserts import assert_is_not
from proboscis.asserts import assert_is_none
from proboscis.asserts import assert_not_equal
from proboscis.asserts import assert_raises
from proboscis.asserts import assert_true
from proboscis.asserts import Check
from proboscis.asserts import fail
import time
from reddwarfclient import exceptions
from reddwarf.tests import util
from reddwarf.tests.util import create_dbaas_client
from reddwarf.tests.util import test_config
from reddwarf.tests.util.users import Requirements
class TestBase(object):
def set_up(self):
"""Create a ton of instances."""
reqs = Requirements(is_admin=False)
self.user = test_config.users.find_user(reqs)
self.dbaas = create_dbaas_client(self.user)
def delete_instances(self):
chunk = 0
while True:
chunk += 1
attempts = 0
instances = self.dbaas.instances.list()
if len(instances) == 0:
break
# Sit around and try to delete this chunk.
while True:
instance_results = []
attempts += 1
deleted_count = 0
for instance in instances:
try:
instance.delete()
result = "[w]"
except exceptions.UnprocessableEntity:
result = "[W]"
except exceptions.NotFound:
result = "[O]"
deleted_count += 1
except Exception:
result = "[X]"
instance_results.append(result)
print("Chunk %d, attempt %d : %s"
% (chunk, attempts, ",".join(instance_results)))
if deleted_count == len(instances):
break
time.sleep(0.2)
def create_instances(self):
self.ids = []
for index in range(self.max):
name = "multi-%03d" % index
result = self.dbaas.instances.create(name, 1,
{'size': 1}, [], [])
self.ids.append(result.id)
# Sort the list of IDs in order, so we can confirm the lists pagination
# returns is also sorted correctly.
self.ids.sort()
@staticmethod
def assert_instances_sorted_by_ids(instances):
# Assert that the strings are always increasing.
last_id = ""
for instance in instances:
assert_true(last_id < instance.id)
def print_list(self, instances):
print("Length = %d" % len(instances))
print(",".join([instance.id for instance in instances]))
def test_pagination(self, requested_limit, requested_marker,
expected_length, expected_marker, expected_last_item):
instances = self.dbaas.instances.list(limit=requested_limit,
marker=requested_marker)
marker = instances.next
self.print_list(instances)
# Better get as many as we asked for.
assert_equal(len(instances), expected_length)
# The last one should be roughly this one in the list.
assert_equal(instances[-1].id, expected_last_item)
# Because limit < count, the marker must be something.
if expected_marker:
assert_is_not(marker, None)
assert_equal(marker, expected_marker)
else:
assert_is_none(marker)
self.assert_instances_sorted_by_ids(instances)
@test(runs_after_groups=["dbaas.guest.shutdown"],
groups=['dbaas.api.instances.pagination'])
class SimpleCreateAndDestroy(TestBase):
"""
It turns out a big part of guaranteeing pagination works is to make sure
we can create a big batch of instances and delete them without problems.
Even in fake mode though its worth it to check this is the case.
"""
max = 5
@before_class
def set_up(self):
"""Create a ton of instances."""
super(SimpleCreateAndDestroy, self).set_up()
self.delete_instances()
@test
def spin_up(self):
self.create_instances()
@after_class(always_run=True)
def tear_down(self):
self.delete_instances()
@test(runs_after_groups=["dbaas.guest.shutdown"],
groups=['dbaas.api.instances.pagination'])
class InstancePagination50(TestBase):
max = 50
@before_class
def set_up(self):
"""Create a ton of instances."""
super(InstancePagination50, self).set_up()
self.delete_instances()
self.create_instances()
@after_class(always_run=True)
def tear_down(self):
"""Tear down all instances."""
self.delete_instances()
@test
def pagination_short(self):
self.test_pagination(requested_limit=10, requested_marker=None,
expected_length=10, expected_marker=self.ids[9],
expected_last_item=self.ids[9])
@test
def pagination_default(self):
self.test_pagination(requested_limit=None, requested_marker=None,
expected_length=20, expected_marker=self.ids[19],
expected_last_item=self.ids[19])
@test
def pagination_full(self):
self.test_pagination(requested_limit=50, requested_marker=None,
expected_length=20, expected_marker=self.ids[19],
expected_last_item=self.ids[19])
@test(runs_after_groups=["dbaas.guest.shutdown"],
groups=['dbaas.api.instances.pagination'])
class InstancePagination20(TestBase):
max = 20
@before_class
def set_up(self):
"""Create a ton of instances."""
super(InstancePagination20, self).set_up()
self.delete_instances()
self.create_instances()
@after_class(always_run=True)
def tear_down(self):
"""Tear down all instances."""
self.delete_instances()
@test
def pagination_short(self):
self.test_pagination(requested_limit=10, requested_marker=None,
expected_length=10, expected_marker=self.ids[9],
expected_last_item=self.ids[9])
@test
def pagination_default(self):
self.test_pagination(requested_limit=None, requested_marker=None,
expected_length=20, expected_marker=None,
expected_last_item=self.ids[19])
@test
def pagination_full(self):
self.test_pagination(requested_limit=20, requested_marker=None,
expected_length=20, expected_marker=None,
expected_last_item=self.ids[19])
@test
def pagination_overkill(self):
self.test_pagination(requested_limit=30, requested_marker=None,
expected_length=20, expected_marker=None,
expected_last_item=self.ids[19])
@test
def pagination_last_half(self):
self.test_pagination(requested_limit=10, requested_marker=self.ids[9],
expected_length=10, expected_marker=None,
expected_last_item=self.ids[19])
@test
def pagination_third_quarter(self):
self.test_pagination(requested_limit=5, requested_marker=self.ids[9],
expected_length=5, expected_marker=self.ids[14],
expected_last_item=self.ids[14])
@test
def pagination_fourth_quarter(self):
self.test_pagination(requested_limit=20, requested_marker=self.ids[14],
expected_length=5, expected_marker=None,
expected_last_item=self.ids[19])

View File

@ -95,7 +95,7 @@ def mgmt_instance_get():
instance.has_field('tenant_id', basestring)
instance.has_field('updated', basestring)
# Can be None if no volume is given on this instance.
if CONFIG.values['reddwarf_main_instance_has_volume']:
if CONFIG.reddwarf_main_instance_has_volume:
instance.has_field('volume', dict, volume_check)
else:
instance.has_field('volume', None)
@ -110,13 +110,14 @@ def mgmt_instance_get():
server.has_element("name", basestring)
server.has_element("status", basestring)
server.has_element("tenant_id", basestring)
with CollectionCheck("volume", api_instance.volume) as volume:
volume.has_element("attachments", list)
volume.has_element("availability_zone", basestring)
volume.has_element("created_at", (basestring, None))
volume.has_element("id", basestring)
volume.has_element("size", int)
volume.has_element("status", basestring)
if CONFIG.reddwarf_main_instance_has_volume:
with CollectionCheck("volume", api_instance.volume) as volume:
volume.has_element("attachments", list)
volume.has_element("availability_zone", basestring)
volume.has_element("created_at", (basestring, None))
volume.has_element("id", basestring)
volume.has_element("size", int)
volume.has_element("status", basestring)
@test(groups=["fake." + GROUP])
@ -130,8 +131,11 @@ class WhenMgmtInstanceGetIsCalledButServerIsNotReady(object):
self.client = create_client(is_admin=True)
self.mgmt = self.client.management
# Fake nova will fail a server ending with 'test_SERVER_ERROR'."
# Fake volume will fail if the size is 13.
# TODO(tim.simpson): This would be a lot nicer looking if we used a
# traditional mock framework.
response = self.client.instances.create('test_SERVER_ERROR', 1,
{'size': 1}, [])
{'size': 13}, [])
poll_until(lambda: self.client.instances.get(response.id),
lambda instance: instance.status == 'ERROR',
time_out=10)

View File

@ -34,6 +34,7 @@ from reddwarf.tests.api.instances import GROUP_START
from reddwarf.tests.api.instances import instance_info
from reddwarf.tests import util
from reddwarf.tests.util import test_config
from reddwarf.tests.api.databases import TestMysqlAccess
GROUP = "dbaas.api.root"
@ -45,7 +46,7 @@ def log_in_as_root(root_password):
return con
@test(depends_on_groups=[GROUP_START],
@test(depends_on_classes=[TestMysqlAccess],
runs_after=[TestUsers],
groups=[tests.DBAAS_API, GROUP, tests.INSTANCES])
class TestRoot(object):

View File

@ -35,13 +35,13 @@ from reddwarf.tests.api.instances import GROUP_START
from reddwarf.tests.api.instances import instance_info
from reddwarf.tests import util
from reddwarf.tests.util import test_config
from reddwarf.tests.api.databases import TestMysqlAccess
GROUP = "dbaas.api.users"
FAKE = test_config.values['fake_mode']
@test(depends_on_groups=[GROUP_START],
@test(depends_on_classes=[TestMysqlAccess],
groups=[tests.DBAAS_API, GROUP, tests.INSTANCES],
runs_after=[TestDatabases])
class TestUsers(object):

View File

@ -78,6 +78,7 @@ class TestConfig(object):
"in_proc_server": True,
"report_directory": os.environ.get("REPORT_DIRECTORY", None),
"sleep_mode": "simulated",
"simulate_events": False,
}
self._frozen_values = FrozenDict(self._values)
self._users = None

View File

@ -445,6 +445,8 @@ class FakeVolumes(object):
self.db[id] = volume
if size == 9:
volume.schedule_status("error", 2)
elif size == 13:
raise Exception("No volume for you!")
else:
volume.schedule_status("available", 2)
LOG.info("FAKE_VOLUMES_DB : %s" % FAKE_VOLUMES_DB)

View File

@ -26,16 +26,14 @@
.. moduleauthor:: Tim Simpson <tim.simpson@rackspace.com>
"""
# This emulates the old way we did things, which was to load the config
# as a module.
# TODO(tim.simpson): Change all references from "test_config" to CONFIG.
from reddwarf.tests.config import CONFIG as test_config
import re
import subprocess
import sys
import time
from reddwarf.tests.config import CONFIG as test_config
try:
from eventlet import event
from eventlet import greenthread
@ -57,9 +55,10 @@ from proboscis.asserts import ASSERTION_ERROR
from proboscis import SkipTest
from reddwarfclient import Dbaas
from reddwarfclient.client import ReddwarfHTTPClient
from reddwarf.tests.util import test_config
from reddwarf.tests.util import test_config as CONFIG
from reddwarf.tests.util.client import TestClient as TestClient
from reddwarf.tests.util.users import Requirements
from reddwarf.common.exception import PollTimeOut
WHITE_BOX = test_config.white_box
@ -179,7 +178,7 @@ def create_nova_client(user, service_type=None):
service_type = test_config.nova_client['nova_service_type']
openstack = Client(user.auth_user, user.auth_key,
user.tenant, test_config.nova_client['auth_url'],
service_type=service_type)
service_type=service_type, no_cache=True)
openstack.authenticate()
return TestClient(openstack)
@ -196,31 +195,30 @@ def string_in_list(str, substr_list):
return any([str.find(x) >= 0 for x in substr_list])
class PollTimeOut(RuntimeError):
message = _("Polling request timed out.")
if CONFIG.simulate_events:
# Without event let, this just calls time.sleep.
def poll_until(retriever, condition=lambda value: value,
sleep_time=1, time_out=None):
"""Retrieves object until it passes condition, then returns it.
If time_out_limit is passed in, PollTimeOut will be raised once that
amount of time is eclipsed.
# Without event let, this just calls time.sleep.
def poll_until(retriever, condition=lambda value: value,
sleep_time=1, time_out=None):
"""Retrieves object until it passes condition, then returns it.
"""
start_time = time.time()
If time_out_limit is passed in, PollTimeOut will be raised once that
amount of time is eclipsed.
def check_timeout():
if time_out is not None and time.time() > start_time + time_out:
raise PollTimeOut
"""
start_time = time.time()
def check_timeout():
if time_out is not None and time.time() > start_time + time_out:
raise PollTimeOut
while True:
obj = retriever()
if condition(obj):
return
check_timeout()
time.sleep(sleep_time)
while True:
obj = retriever()
if condition(obj):
return
check_timeout()
time.sleep(sleep_time)
else:
from reddwarf.common.utils import poll_until
class LocalSqlClient(object):