Add instance create int-tests

Add generic integration tests for instance create.
Also moved some global constants to the base runner.

Note: Did some additional cleanup work on other test groups as well.
      Broke down some negative tests into more self-contained units.
      Added a comment string on tests that did not have it.
      Had to fix database_actions test to work for users defining
      hosts (like MySQL users).

Change-Id: I7c1413fa5742c276454e26ebbbeae8e02569a392
This commit is contained in:
Petr Malik 2015-08-20 12:48:53 -04:00 committed by Peter Stachowski
parent 1cb7dc7792
commit aeadbfc974
21 changed files with 926 additions and 209 deletions

View File

@ -38,6 +38,7 @@ from trove.tests.scenario.groups import backup_group
from trove.tests.scenario.groups import cluster_actions_group
from trove.tests.scenario.groups import database_actions_group
from trove.tests.scenario.groups import instance_actions_group
from trove.tests.scenario.groups import instance_create_group
from trove.tests.scenario.groups import instance_delete_group
from trove.tests.scenario.groups import negative_cluster_actions_group
from trove.tests.scenario.groups import replication_group
@ -119,37 +120,39 @@ proboscis.register(groups=["blackbox_mgmt"],
#
# Group designations for datastore agnostic int-tests
#
initial_groups = [
instance_create_groups = [
GROUP_SERVICES_INITIALIZE,
flavors.GROUP,
versions.GROUP,
instances.GROUP_START_SIMPLE,
instance_create_group.GROUP,
instance_delete_group.GROUP
]
backup_groups = list(initial_groups)
backup_groups = list(instance_create_groups)
backup_groups.extend([backup_group.GROUP])
user_actions_groups = list(initial_groups)
user_actions_groups = list(instance_create_groups)
user_actions_groups.extend([user_actions_group.GROUP])
database_actions_groups = list(initial_groups)
database_actions_groups = list(instance_create_groups)
database_actions_groups.extend([database_actions_group.GROUP])
cluster_actions_groups = list(initial_groups)
cluster_actions_groups = list(instance_create_groups)
cluster_actions_groups.extend([cluster_actions_group.GROUP,
negative_cluster_actions_group.GROUP])
instance_actions_groups = list(initial_groups)
instance_actions_groups = list(instance_create_groups)
instance_actions_groups.extend([instance_actions_group.GROUP])
replication_groups = list(initial_groups)
replication_groups = list(instance_create_groups)
replication_groups.extend([replication_group.GROUP])
# Module based groups
register(["backup"], backup_groups)
register(["cluster"], cluster_actions_groups)
register(["database"], database_actions_group)
register(["database"], database_actions_groups)
register(["instance_actions"], instance_actions_groups)
register(["instance_create"], instance_create_groups)
register(["user"], user_actions_groups)
register(["replication"], replication_groups)

View File

@ -15,7 +15,7 @@
from proboscis import test
from trove.tests.api.instances import WaitForGuestInstallationToFinish
from trove.tests.scenario.groups import instance_create_group
from trove.tests.scenario.groups.test_group import TestGroup
@ -25,8 +25,7 @@ GROUP_BACKUP_LIST = "scenario.backup_list_group"
GROUP_RESTORE = "scenario.restore_group"
@test(depends_on_classes=[WaitForGuestInstallationToFinish],
groups=[GROUP])
@test(depends_on_groups=[instance_create_group.GROUP], groups=[GROUP])
class BackupGroup(TestGroup):
"""Test Backup and Restore functionality."""

View File

@ -15,16 +15,13 @@
from proboscis import test
from trove.tests.api.instances import GROUP_START_SIMPLE
from trove.tests.api.instances import WaitForGuestInstallationToFinish
from trove.tests.scenario.groups.test_group import TestGroup
GROUP = "scenario.cluster_actions_group"
@test(depends_on_groups=[GROUP_START_SIMPLE], groups=[GROUP],
runs_after=[WaitForGuestInstallationToFinish])
@test(groups=[GROUP])
class ClusterActionsGroup(TestGroup):
def __init__(self):
@ -33,12 +30,15 @@ class ClusterActionsGroup(TestGroup):
@test
def cluster_create(self):
"""Create a cluster."""
self.test_runner.run_cluster_create()
@test(depends_on=[cluster_create])
def test_cluster_communication(self):
"""Validate the cluster data and properties."""
self.test_runner.run_cluster_communication()
@test(depends_on=[cluster_create], runs_after=[test_cluster_communication])
def cluster_delete(self):
"""Delete an existing cluster."""
self.test_runner.run_cluster_delete()

View File

@ -15,49 +15,94 @@
from proboscis import test
from trove.tests.api.instances import GROUP_START_SIMPLE
from trove.tests.api.instances import WaitForGuestInstallationToFinish
from trove.tests.scenario.groups import instance_create_group
from trove.tests.scenario.groups.test_group import TestGroup
from trove.tests.scenario.groups import user_actions_group
GROUP = "dbaas.api.database_actions_tests"
@test(depends_on_groups=[GROUP_START_SIMPLE], groups=[GROUP],
runs_after=[WaitForGuestInstallationToFinish],
runs_after_groups=[user_actions_group.GROUP])
GROUP = "scenario.database_actions_group"
@test(depends_on_groups=[instance_create_group.GROUP], groups=[GROUP])
class DatabaseActionsGroup(TestGroup):
def __init__(self):
super(DatabaseActionsGroup, self).__init__(
'database_actions_runners', 'DatabaseActionsRunner')
self.instance_create_runner = self.get_runner(
'instance_create_runners', 'InstanceCreateRunner')
@test
def create_initialized_instance(self):
"""Create an instance with initial databases."""
self.instance_create_runner.run_initialized_instance_create(
with_dbs=True, with_users=False, configuration_id=None)
@test(runs_after=[create_initialized_instance])
def create_databases(self):
"""Create databases on an existing instance."""
self.test_runner.run_databases_create()
@test(depends_on=[create_databases])
def list_databases(self):
"""List the created databases."""
self.test_runner.run_databases_list()
@test(depends_on=[create_databases],
runs_after=[list_databases])
def negative_create_database(self):
self.test_runner.run_negative_database_create()
def create_database_with_no_attributes(self):
"""Ensure creating a database with blank specification fails."""
self.test_runner.run_database_create_with_no_attributes()
@test(depends_on=[create_databases],
runs_after=[negative_create_database])
runs_after=[create_database_with_no_attributes])
def create_database_with_blank_name(self):
"""Ensure creating a database with blank name fails."""
self.test_runner.run_database_create_with_blank_name()
@test(depends_on=[create_databases],
runs_after=[create_database_with_blank_name])
def create_existing_database(self):
"""Ensure creating an existing database fails."""
self.test_runner.run_existing_database_create()
@test(depends_on=[create_databases],
runs_after=[create_existing_database])
def delete_database(self):
"""Delete the created databases."""
self.test_runner.run_database_delete()
@test
def nonexisting_database_delete(self):
@test(runs_after=[delete_database])
def delete_nonexisting_database(self):
"""Delete non-existing databases."""
self.test_runner.run_nonexisting_database_delete()
@test
def system_database_create(self):
@test(runs_after=[delete_nonexisting_database])
def create_system_database(self):
"""Ensure creating a system database fails."""
self.test_runner.run_system_database_create()
@test
def system_database_delete(self):
@test(runs_after=[create_system_database])
def delete_system_database(self):
"""Ensure deleting a system database fails."""
self.test_runner.run_system_database_delete()
@test(depends_on=[create_initialized_instance],
runs_after=[delete_system_database])
def wait_for_instances(self):
"""Waiting for all instances to become active."""
self.instance_create_runner.wait_for_created_instances()
@test(depends_on=[wait_for_instances])
def add_initialized_instance_data(self):
"""Add data to the initialized instance."""
self.instance_create_runner.run_add_initialized_instance_data()
@test(runs_after=[add_initialized_instance_data])
def validate_initialized_instance(self):
"""Validate the initialized instance data and properties."""
self.instance_create_runner.run_validate_initialized_instance()
@test(runs_after=[validate_initialized_instance])
def delete_initialized_instance(self):
"""Delete the initialized instance."""
self.instance_create_runner.run_initialized_instance_delete()

View File

@ -15,16 +15,14 @@
from proboscis import test
from trove.tests.api.instances import GROUP_START_SIMPLE
from trove.tests.api.instances import WaitForGuestInstallationToFinish
from trove.tests.scenario.groups import instance_create_group
from trove.tests.scenario.groups.test_group import TestGroup
GROUP = "scenario.instance_actions_group"
@test(depends_on_groups=[GROUP_START_SIMPLE], groups=[GROUP],
runs_after=[WaitForGuestInstallationToFinish])
@test(depends_on_groups=[instance_create_group.GROUP], groups=[GROUP])
class InstanceActionsGroup(TestGroup):
def __init__(self):
@ -33,12 +31,15 @@ class InstanceActionsGroup(TestGroup):
@test
def instance_restart(self):
"""Restart an existing instance."""
self.test_runner.run_instance_restart()
@test(depends_on=[instance_restart])
def instance_resize_volume(self):
"""Resize attached volume."""
self.test_runner.run_instance_resize_volume()
@test(depends_on=[instance_resize_volume])
def instance_resize_flavor(self):
"""Resize instance flavor."""
self.test_runner.run_instance_resize_flavor()

View File

@ -0,0 +1,73 @@
# Copyright 2015 Tesora Inc.
# 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.
from proboscis import test
from trove.tests.api.instances import InstanceSetup
from trove.tests import PRE_INSTANCES
from trove.tests.scenario.groups.test_group import TestGroup
GROUP = "scenario.instance_create_group"
@test(depends_on_classes=[InstanceSetup], runs_after_groups=[PRE_INSTANCES],
groups=[GROUP])
class InstanceCreateGroup(TestGroup):
def __init__(self):
super(InstanceCreateGroup, self).__init__(
'instance_create_runners', 'InstanceCreateRunner')
@test
def create_empty_instance(self):
"""Create an empty instance."""
self.test_runner.run_empty_instance_create()
@test(runs_after=[create_empty_instance])
def create_initial_configuration(self):
"""Create a configuration group for a new initialized instance."""
self.test_runner.run_initial_configuration_create()
@test(runs_after=[create_initial_configuration])
def create_initialized_instance(self):
"""Create an instance with initial properties."""
self.test_runner.run_initialized_instance_create()
@test(runs_after=[create_initialized_instance])
def wait_for_instances(self):
"""Waiting for all instances to become active."""
self.test_runner.wait_for_created_instances()
@test(depends_on=[wait_for_instances])
def add_initialized_instance_data(self):
"""Add data to the initialized instance."""
self.test_runner.run_add_initialized_instance_data()
@test(runs_after=[add_initialized_instance_data])
def validate_initialized_instance(self):
"""Validate the initialized instance data and properties."""
self.test_runner.run_validate_initialized_instance()
@test(runs_after=[validate_initialized_instance])
def delete_initialized_instance(self):
"""Delete the initialized instance."""
self.test_runner.run_initialized_instance_delete()
@test(depends_on=[create_initial_configuration,
delete_initialized_instance])
def delete_initial_configuration(self):
"""Delete the initial configuration group."""
self.test_runner.run_initial_configuration_delete()

View File

@ -15,19 +15,25 @@
from proboscis import test
from trove.tests.api.instances import GROUP_START_SIMPLE
from trove.tests.scenario.groups import backup_group
from trove.tests.scenario.groups import database_actions_group
from trove.tests.scenario.groups import instance_actions_group
from trove.tests.scenario.groups import instance_create_group
from trove.tests.scenario.groups import replication_group
from trove.tests.scenario.groups.test_group import TestGroup
from trove.tests.scenario.groups import user_actions_group
GROUP = "scenario.instance_delete_group"
@test(depends_on_groups=[GROUP_START_SIMPLE], groups=[GROUP],
runs_after_groups=[backup_group.GROUP_BACKUP, replication_group.GROUP,
instance_actions_group.GROUP])
@test(depends_on_groups=[instance_create_group.GROUP],
groups=[GROUP],
runs_after_groups=[backup_group.GROUP_BACKUP,
database_actions_group.GROUP,
instance_actions_group.GROUP,
replication_group.GROUP,
user_actions_group.GROUP])
class InstanceDeleteGroup(TestGroup):
def __init__(self):
@ -36,4 +42,5 @@ class InstanceDeleteGroup(TestGroup):
@test
def instance_delete(self):
"""Delete an existing instance."""
self.test_runner.run_instance_delete()

View File

@ -15,15 +15,13 @@
from proboscis import test
from trove.tests.api.instances import GROUP_START_SIMPLE
from trove.tests.api.instances import WaitForGuestInstallationToFinish
from trove.tests.scenario.groups.test_group import TestGroup
GROUP = "scenario.negative_cluster_actions_group"
@test(depends_on_groups=[GROUP_START_SIMPLE], groups=[GROUP],
runs_after=[WaitForGuestInstallationToFinish])
@test(groups=[GROUP])
class NegativeClusterActionsGroup(TestGroup):
def __init__(self):
@ -32,8 +30,10 @@ class NegativeClusterActionsGroup(TestGroup):
@test
def create_constrained_size_cluster(self):
"""Ensure creating a cluster with wrong number of nodes fails."""
self.test_runner.run_create_constrained_size_cluster()
@test
def create_heterogeneous_cluster(self):
"""Ensure creating a cluster with unequal nodes fails."""
self.test_runner.run_create_heterogeneous_cluster()

View File

@ -15,15 +15,14 @@
from proboscis import test
from trove.tests.api.instances import GROUP_START_SIMPLE
from trove.tests.scenario.groups import backup_group
from trove.tests.scenario.groups import instance_create_group
from trove.tests.scenario.groups.test_group import TestGroup
GROUP = "scenario.replication_group"
@test(depends_on_groups=[GROUP_START_SIMPLE], groups=[GROUP],
runs_after=[backup_group.GROUP_BACKUP])
@test(depends_on_groups=[instance_create_group.GROUP], groups=[GROUP])
class ReplicationGroup(TestGroup):
"""Test Replication functionality."""

View File

@ -29,15 +29,29 @@ class TestGroup(object):
TEST_HELPER_BASE_NAME = 'TestHelper'
def __init__(self, runner_module_name, runner_base_name, *args, **kwargs):
self._test_runner = self.get_runner(
runner_module_name, runner_base_name, *args, **kwargs)
def get_runner(self, runner_module_name, runner_base_name,
*args, **kwargs):
class_prefix = self._get_test_datastore()
runner_cls = self._load_dynamic_class(
runner_module_name, class_prefix, runner_base_name,
self.TEST_RUNNERS_NS)
self._test_runner = runner_cls(*args, **kwargs)
runner = runner_cls(*args, **kwargs)
helper_cls = self._load_dynamic_class(
self.TEST_HELPER_MODULE_NAME, class_prefix,
self.TEST_HELPER_BASE_NAME, self.TEST_HELPERS_NS)
self._test_runner._test_helper = helper_cls(self._build_class_name(
runner._test_helper = helper_cls(self._build_class_name(
class_prefix, self.TEST_HELPER_BASE_NAME, strip_test=True))
return runner
def get_helper(self):
class_prefix = self._get_test_datastore()
helper_cls = self._load_dynamic_class(
self.TEST_HELPER_MODULE_NAME, class_prefix,
self.TEST_HELPER_BASE_NAME, self.TEST_HELPERS_NS)
return helper_cls(self._build_class_name(
class_prefix, self.TEST_HELPER_BASE_NAME, strip_test=True))
def _get_test_datastore(self):

View File

@ -15,80 +15,159 @@
from proboscis import test
from trove.tests.api.instances import GROUP_START_SIMPLE
from trove.tests.api.instances import WaitForGuestInstallationToFinish
from trove.tests.scenario.groups import instance_actions_group
from trove.tests.scenario.groups import instance_create_group
from trove.tests.scenario.groups.test_group import TestGroup
GROUP = "dbaas.api.user_actions_tests"
GROUP = "scenario.user_actions_group"
@test(depends_on_groups=[GROUP_START_SIMPLE], groups=[GROUP],
runs_after=[WaitForGuestInstallationToFinish],
runs_after_groups=[instance_actions_group.GROUP])
@test(depends_on_groups=[instance_create_group.GROUP], groups=[GROUP])
class UserActionsGroup(TestGroup):
def __init__(self):
super(UserActionsGroup, self).__init__(
'user_actions_runners', 'UserActionsRunner')
self.instance_create_runner = self.get_runner(
'instance_create_runners', 'InstanceCreateRunner')
self.database_actions_runner = self.get_runner(
'database_actions_runners', 'DatabaseActionsRunner')
@test
def create_initialized_instance(self):
"""Create an instance with initial users."""
self.instance_create_runner.run_initialized_instance_create(
with_dbs=False, with_users=True, configuration_id=None)
@test(runs_after=[create_initialized_instance])
def create_user_databases(self):
"""Create user databases on an existing instance."""
# These databases may be referenced by the users (below) so we need to
# create them first.
self.database_actions_runner.run_databases_create()
@test(runs_after=[create_user_databases])
def create_users(self):
"""Create users on an existing instance."""
self.test_runner.run_users_create()
@test(depends_on=[create_users])
def show_user(self):
"""Show created users."""
self.test_runner.run_user_show()
@test(depends_on=[create_users],
runs_after=[show_user])
def list_users(self):
"""List the created users."""
self.test_runner.run_users_list()
@test(depends_on=[create_users],
runs_after=[list_users])
def negative_create_user(self):
self.test_runner.run_negative_user_create()
def create_user_with_no_attributes(self):
"""Ensure creating a user with blank specification fails."""
self.test_runner.run_user_create_with_no_attributes()
@test(depends_on=[create_users],
runs_after=[list_users])
def negative_user_attribute_update(self):
self.test_runner.run_negative_user_attribute_update()
runs_after=[create_user_with_no_attributes])
def create_user_with_blank_name(self):
"""Ensure creating a user with blank name fails."""
self.test_runner.run_user_create_with_blank_name()
@test(depends_on=[create_users],
runs_after=[negative_user_attribute_update])
def user_attribute_update(self):
runs_after=[create_user_with_blank_name])
def create_user_with_blank_password(self):
"""Ensure creating a user with blank password fails."""
self.test_runner.run_user_create_with_blank_password()
@test(depends_on=[create_users],
runs_after=[create_user_with_blank_password])
def create_existing_user(self):
"""Ensure creating an existing user fails."""
self.test_runner.run_existing_user_create()
@test(depends_on=[create_users],
runs_after=[create_existing_user])
def update_user_with_no_attributes(self):
"""Ensure updating a user with blank specification fails."""
self.test_runner.run_user_update_with_no_attributes()
@test(depends_on=[create_users],
runs_after=[update_user_with_no_attributes])
def update_user_with_blank_name(self):
"""Ensure updating a user with blank name fails."""
self.test_runner.run_user_update_with_blank_name()
@test(depends_on=[create_users],
runs_after=[update_user_with_blank_name])
def update_user_with_existing_name(self):
"""Ensure updating a user with an existing name fails."""
self.test_runner.run_user_update_with_existing_name()
@test(depends_on=[create_users],
runs_after=[update_user_with_existing_name])
def update_user_attributes(self):
"""Update an existing user."""
self.test_runner.run_user_attribute_update()
@test(depends_on=[create_users],
runs_after=[user_attribute_update])
runs_after=[update_user_attributes])
def delete_user(self):
"""Delete the created users."""
self.test_runner.run_user_delete()
@test
def nonexisting_user_show(self):
@test(runs_after=[delete_user])
def show_nonexisting_user(self):
"""Delete non-existing users."""
self.test_runner.run_nonexisting_user_show()
@test
def nonexisting_user_attribute_update(self):
@test(runs_after=[show_nonexisting_user])
def update_nonexisting_user(self):
"""Ensure updating a non-existing user fails."""
self.test_runner.run_nonexisting_user_update()
@test
def nonexisting_user_delete(self):
@test(runs_after=[update_nonexisting_user])
def delete_nonexisting_user(self):
"""Ensure deleting a non-existing user fails."""
self.test_runner.run_nonexisting_user_delete()
@test
def system_user_create(self):
@test(runs_after=[delete_nonexisting_user])
def create_system_user(self):
"""Ensure creating a system user fails."""
self.test_runner.run_system_user_create()
@test
def system_user_show(self):
@test(runs_after=[create_system_user])
def show_system_user(self):
"""Ensure showing a system user fails."""
self.test_runner.run_system_user_show()
@test
def system_user_attribute_update(self):
@test(runs_after=[show_system_user])
def update_system_user(self):
"""Ensure updating a system user fails."""
self.test_runner.run_system_user_attribute_update()
@test
def system_user_delete(self):
@test(runs_after=[update_system_user])
def delete_system_user(self):
"""Ensure deleting a system user fails."""
self.test_runner.run_system_user_delete()
@test(runs_after=[delete_system_user])
def delete_user_databases(self):
"""Delete the user databases."""
self.database_actions_runner.run_database_delete()
@test(depends_on=[create_initialized_instance],
runs_after=[delete_user_databases])
def wait_for_instances(self):
"""Waiting for all instances to become active."""
self.instance_create_runner.wait_for_created_instances()
@test(depends_on=[wait_for_instances])
def validate_initialized_instance(self):
"""Validate the initialized instance data and properties."""
self.instance_create_runner.run_validate_initialized_instance()
@test(runs_after=[validate_initialized_instance])
def delete_initialized_instance(self):
"""Delete the initialized instance."""
self.instance_create_runner.run_initialized_instance_delete()

View File

@ -0,0 +1,40 @@
# Copyright 2015 Tesora Inc.
# 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.
from trove.tests.scenario.helpers.test_helper import TestHelper
class CassandrabHelper(TestHelper):
def __init__(self, expected_override_name):
super(CassandrabHelper, self).__init__(expected_override_name)
def get_valid_database_definitions(self):
return [{"name": 'db1'}, {"name": 'db2'}]
def get_valid_user_definitions(self):
return [{'name': 'user1', 'password': 'password1',
'databases': []},
{'name': 'user2', 'password': 'password1',
'databases': [{'name': 'db1'}]},
{'name': 'user3', 'password': 'password1',
'databases': [{'name': 'db1'}, {'name': 'db2'}]}]
def get_non_dynamic_group(self):
return {'sstable_preemptive_open_interval_in_mb': 40}
def get_invalid_groups(self):
return [{'sstable_preemptive_open_interval_in_mb': -1},
{'sstable_preemptive_open_interval_in_mb': 'string_value'}]

View File

@ -0,0 +1,41 @@
# Copyright 2015 Tesora Inc.
# 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.
from trove.tests.scenario.helpers.test_helper import TestHelper
class MongodbHelper(TestHelper):
def __init__(self, expected_override_name):
super(MongodbHelper, self).__init__(expected_override_name)
def get_valid_database_definitions(self):
return [{"name": 'db1'}, {"name": 'db2'}]
def get_valid_user_definitions(self):
return [{'name': 'db0.user1', 'password': 'password1',
'databases': []},
{'name': 'db0.user2', 'password': 'password1',
'databases': [{'name': 'db1'}]},
{'name': 'db1.user3', 'password': 'password1',
'databases': [{'name': 'db1'}, {'name': 'db2'}]}]
def get_non_dynamic_group(self):
return {'systemLog.verbosity': 4}
def get_invalid_groups(self):
return [{'net.maxIncomingConnections': -1},
{'storage.mmapv1.nsSize': 4096},
{'storage.journal.enabled': 'string_value'}]

View File

@ -0,0 +1,45 @@
# Copyright 2015 Tesora Inc.
# 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.
from trove.tests.scenario.helpers.test_helper import TestHelper
class MysqlHelper(TestHelper):
def __init__(self, expected_override_name):
super(MysqlHelper, self).__init__(expected_override_name)
def get_valid_database_definitions(self):
return [{'name': 'db1', 'character_set': 'latin2',
'collate': 'latin2_general_ci'},
{'name': 'db2'}]
def get_valid_user_definitions(self):
return [{'name': 'user1', 'password': 'password1', 'databases': [],
'host': '127.0.0.1'},
{'name': 'user2', 'password': 'password1',
'databases': [{'name': 'db1'}], 'host': '0.0.0.0'},
{'name': 'user3', 'password': 'password1',
'databases': [{'name': 'db1'}, {'name': 'db2'}]}]
def get_dynamic_group(self):
return {'key_buffer_size': 10485760,
'join_buffer_size': 10485760}
def get_non_dynamic_group(self):
return {'innodb_buffer_pool_size': 10485760}
def get_invalid_groups(self):
return [{'key_buffer_size': 4}, {"join_buffer_size": 'string_value'}]

View File

@ -153,3 +153,12 @@ class RedisHelper(TestHelper):
TestRunner.assert_equal(expected_value, value,
"Unexpected value '%s' returned from Redis "
"key '%s'" % (value, key))
def get_dynamic_group(self):
return {'hz': 15}
def get_non_dynamic_group(self):
return {'databases': 24}
def get_invalid_groups(self):
return [{'hz': 600}, {'databases': -1}, {'databases': 'string_value'}]

View File

@ -192,3 +192,38 @@ class TestHelper(object):
specific overrides could increase (or decrease) this delay.
"""
sleep(30)
def get_valid_database_definitions(self):
"""Return a list of valid database JSON definitions.
These definitions will be used by tests that create databases.
Return an empty list if the datastore does not support databases.
"""
return list()
def get_valid_user_definitions(self):
"""Return a list of valid user JSON definitions.
These definitions will be used by tests that create users.
Return an empty list if the datastore does not support users.
"""
return list()
def get_dynamic_group(self):
"""Return a definition of a dynamic configuration group.
A dynamic group should contain only properties that do not require
database restart.
Return an empty dict if the datastore does not have any.
"""
return dict()
def get_non_dynamic_group(self):
"""Return a definition of a non-dynamic configuration group.
A non-dynamic group has to include at least one property that requires
database restart.
Return an empty dict if the datastore does not have any.
"""
return dict()
def get_invalid_groups(self):
"""Return a list of configuration groups with invalid values.
"""
return []

View File

@ -60,6 +60,10 @@ class ClusterActionsRunner(TestRunner):
return cluster_id
def run_cluster_communication(self):
# TODO(pmalik): This will need to be generalized
# (using a datastore test_helper) to add and verify data.
# Creating and checking databases like this would not work with
# datastores that do not support them (Redis).
databases = []
databases.append({"name": 'somenewdb'})
cluster = self.auth_client.clusters.get(self.cluster_id)
@ -73,7 +77,7 @@ class ClusterActionsRunner(TestRunner):
for instance in cluster_instances:
databases_after = self.auth_client.databases.list(
cluster_instances[0].id)
asserts.assert_true(len(databases_before) < len(databases_after))
self.assert_true(len(databases_before) < len(databases_after))
def run_cluster_delete(
self, expected_last_instance_state='SHUTDOWN',

View File

@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from proboscis import asserts
from trove.tests.scenario.runners.test_runners import TestRunner
from troveclient.compat import exceptions
@ -28,15 +26,13 @@ class DatabaseActionsRunner(TestRunner):
# more appropriate anyways.
def run_databases_create(self, expected_http_code=202):
databases = [{"name": 'database1'},
{"name": 'database2'},
{"name": 'database3'}]
databases = self.test_helper.get_valid_database_definitions()
self.db_defs = self.assert_databases_create(
self.instance_info.id, databases, expected_http_code)
def assert_databases_create(self, instance_id, serial_databases_def,
expected_http_code):
self.rd_client.databases.create(instance_id, serial_databases_def)
self.auth_client.databases.create(instance_id, serial_databases_def)
self.assert_client_code(expected_http_code)
return serial_databases_def
@ -46,15 +42,15 @@ class DatabaseActionsRunner(TestRunner):
def assert_databases_list(self, instance_id, expected_database_defs,
expected_http_code, limit=2):
full_list = self.rd_client.databases.list(instance_id)
full_list = self.auth_client.databases.list(instance_id)
self.assert_client_code(expected_http_code)
listed_databases = {database.name: database for database in full_list}
asserts.assert_is_none(full_list.next,
"Unexpected pagination in the list.")
self.assert_is_none(full_list.next,
"Unexpected pagination in the list.")
for database_def in expected_database_defs:
database_name = database_def['name']
asserts.assert_true(
self.assert_true(
database_name in listed_databases,
"Database not included in the 'database-list' output: %s" %
database_name)
@ -62,41 +58,49 @@ class DatabaseActionsRunner(TestRunner):
# Check that the system (ignored) databases are not included in the
# output.
system_databases = self.get_system_databases()
asserts.assert_false(
self.assert_false(
any(name in listed_databases for name in system_databases),
"System databases should not be included in the 'database-list' "
"output.")
# Test list pagination.
list_page = self.rd_client.databases.list(instance_id, limit=limit)
list_page = self.auth_client.databases.list(instance_id, limit=limit)
self.assert_client_code(expected_http_code)
asserts.assert_true(len(list_page) <= limit)
asserts.assert_is_not_none(list_page.next, "List page is missing.")
self.assert_true(len(list_page) <= limit)
if len(full_list) > limit:
self.assert_is_not_none(list_page.next, "List page is missing.")
else:
self.assert_is_none(list_page.next, "An extra page in the list.")
marker = list_page.next
self.assert_pagination_match(list_page, full_list, 0, limit)
self.assert_pagination_match(
list_page[-1:], full_list, limit - 1, limit)
if marker:
self.assert_equal(list_page[-1], marker.name,
"Pagination marker should be the last element "
"in the page.")
list_page = self.auth_client.databases.list(
instance_id, marker=marker)
self.assert_client_code(expected_http_code)
self.assert_pagination_match(
list_page, full_list, limit, len(full_list))
list_page = self.rd_client.databases.list(instance_id, marker=marker)
self.assert_client_code(expected_http_code)
self.assert_pagination_match(
list_page, full_list, limit, len(full_list))
def run_negative_database_create(
def run_database_create_with_no_attributes(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
# Test with no attribites.
self.assert_databases_create_failure(
self.instance_info.id, {}, expected_exception, expected_http_code)
# Test with empty database name attribute.
def run_database_create_with_blank_name(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
self.assert_databases_create_failure(
self.instance_info.id, {'name': ''},
expected_exception, expected_http_code)
# Test creating an existing database.
def run_existing_database_create(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
self.assert_databases_create_failure(
self.instance_info.id, self.db_defs[0],
expected_exception, expected_http_code)
@ -105,8 +109,11 @@ class DatabaseActionsRunner(TestRunner):
self, instance_id, serial_databases_def,
expected_exception, expected_http_code):
self.assert_raises(
expected_exception, expected_http_code,
self.rd_client.databases.create, instance_id, serial_databases_def)
expected_exception,
expected_http_code,
self.auth_client.databases.create,
instance_id,
serial_databases_def)
def run_system_database_create(
self, expected_exception=exceptions.BadRequest,
@ -115,13 +122,11 @@ class DatabaseActionsRunner(TestRunner):
# return Forbidden 403 instead. The current error messages are
# confusing (talking about a malformed request).
system_databases = self.get_system_databases()
database_defs = [{'name': name} for name in system_databases]
if system_databases:
for name in system_databases:
database_def = {'name': name, 'password': 'password1',
'databases': []}
self.assert_databases_create_failure(
self.instance_info.id, database_def,
expected_exception, expected_http_code)
self.assert_databases_create_failure(
self.instance_info.id, database_defs,
expected_exception, expected_http_code)
def run_database_delete(self, expected_http_code=202):
for database_def in self.db_defs:
@ -134,12 +139,12 @@ class DatabaseActionsRunner(TestRunner):
instance_id,
database_name,
expected_http_code):
self.rd_client.databases.delete(instance_id, database_name)
self.auth_client.databases.delete(instance_id, database_name)
self.assert_client_code(expected_http_code)
for database in self.rd_client.databases.list(instance_id):
for database in self.auth_client.databases.list(instance_id):
if database.name == database_name:
asserts.fail(
self.fail(
"Database still listed after delete: %s" %
database_name)
@ -166,19 +171,8 @@ class DatabaseActionsRunner(TestRunner):
self, instance_id, database_name,
expected_exception, expected_http_code):
self.assert_raises(expected_exception, expected_http_code,
self.rd_client.databases.delete,
self.auth_client.databases.delete,
instance_id, database_name)
def get_system_databases(self):
return self.get_datastore_config_property('ignore_dbs')
class MysqlDatabaseActionsRunner(DatabaseActionsRunner):
def get_system_databases(self):
# It seems the client does not like this name.
# Does this particular name actually still have to be ignored after
# all the datadir changes?
return [name for name
in self.get_datastore_config_property('ignore_dbs')
if name != '#mysql50#lost+found']

View File

@ -0,0 +1,300 @@
# Copyright 2015 Tesora Inc.
# 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.
import json
from proboscis import SkipTest
from trove.tests.api.instances import CheckInstance, InstanceTestInfo
from trove.tests.config import CONFIG
from trove.tests.scenario.helpers.test_helper import DataType
from trove.tests.scenario.runners.test_runners import TestRunner
class InstanceCreateRunner(TestRunner):
def __init__(self):
super(InstanceCreateRunner, self).__init__()
self.init_inst_id = None
self.init_inst_dbs = None
self.init_inst_users = None
self.init_inst_host = None
self.init_inst_data = None
self.init_config_group_id = None
def run_empty_instance_create(
self, expected_states=['BUILD', 'ACTIVE'], expected_http_code=200):
# TODO(pmalik): Instance create should return 202 Accepted (cast)
# rather than 200 OK (call).
name = self.instance_info.name
flavor = self._get_instance_flavor()
trove_volume_size = CONFIG.get('trove_volume_size', 1)
info = self.assert_instance_create(
name, flavor, trove_volume_size, [], [], None, None,
CONFIG.dbaas_datastore, CONFIG.dbaas_datastore_version,
expected_states, expected_http_code)
# Update the shared instance info.
self.instance_info.databases = info.databases
self.instance_info.users = info.users
self.instance_info.dbaas_datastore = info.dbaas_datastore
self.instance_info.dbaas_datastore_version = (info.
dbaas_datastore_version)
self.instance_info.dbaas_flavor_href = info.dbaas_flavor_href
self.instance_info.volume = info.volume
self.instance_info.id = info.id
def run_initial_configuration_create(self, expected_http_code=200):
dynamic_config = self.test_helper.get_dynamic_group()
non_dynamic_config = self.test_helper.get_non_dynamic_group()
values = dynamic_config or non_dynamic_config
if values:
json_def = json.dumps(values)
result = self.auth_client.configurations.create(
'initial_configuration_for_instance_create',
json_def,
"Configuration group used by instance create tests.",
datastore=self.instance_info.dbaas_datastore,
datastore_version=self.instance_info.dbaas_datastore_version)
self.assert_client_code(expected_http_code)
self.init_config_group_id = result.id
else:
raise SkipTest("No groups defined.")
def run_initialized_instance_create(
self, with_dbs=True, with_users=True, configuration_id=None,
expected_states=['BUILD', 'ACTIVE'], expected_http_code=200):
# TODO(pmalik): Instance create should return 202 Accepted (cast)
# rather than 200 OK (call).
name = self.instance_info.name
flavor = self._get_instance_flavor()
trove_volume_size = CONFIG.get('trove_volume_size', 1)
self.init_inst_dbs = (self.test_helper.get_valid_database_definitions()
if with_dbs else [])
self.init_inst_users = (self.test_helper.get_valid_user_definitions()
if with_users else [])
if configuration_id:
self.init_config_group_id = configuration_id
if (self.init_inst_dbs or self.init_inst_users or
self.init_config_group_id):
info = self.assert_instance_create(
name, flavor, trove_volume_size,
self.init_inst_dbs, self.init_inst_users,
self.init_config_group_id, None,
CONFIG.dbaas_datastore, CONFIG.dbaas_datastore_version,
expected_states, expected_http_code)
self.init_inst_id = info.id
else:
# There is no need to run this test as it's effectively the same as
# the empty instance test.
raise SkipTest("No testable initial properties provided.")
def _get_instance_flavor(self):
if self.EPHEMERAL_SUPPORT:
flavor_name = CONFIG.values.get('instance_eph_flavor_name',
'eph.rd-tiny')
else:
flavor_name = CONFIG.values.get('instance_flavor_name', 'm1.tiny')
return self.get_flavor(flavor_name)
def _get_flavor_href(self, flavor):
return self.auth_client.find_flavor_self_href(flavor)
def assert_instance_create(
self, name, flavor, trove_volume_size,
database_definitions, user_definitions,
configuration_id, root_password, datastore, datastore_version,
expected_states, expected_http_code):
"""This assert method executes a 'create' call and verifies the server
response. It neither waits for the instance to become available
nor it performs any other validations itself.
It has been designed this way to increase test granularity
(other tests may run while the instance is building) and also to allow
its reuse in other runners .
"""
databases = database_definitions
users = [{'name': item['name'], 'password': item['password']}
for item in user_definitions]
instance_info = InstanceTestInfo()
instance_info.name = name
instance_info.databases = databases
instance_info.users = users
instance_info.dbaas_datastore = CONFIG.dbaas_datastore
instance_info.dbaas_datastore_version = CONFIG.dbaas_datastore_version
instance_info.dbaas_flavor_href = self._get_flavor_href(flavor)
if self.VOLUME_SUPPORT:
instance_info.volume = {'size': trove_volume_size}
else:
instance_info.volume = None
self.report.log("Testing create instance: %s"
% {'name': name,
'flavor': flavor.id,
'volume': trove_volume_size,
'databases': databases,
'users': users,
'configuration': configuration_id,
'root password': root_password,
'datastore': datastore,
'datastore version': datastore_version})
instance = self.get_existing_instance()
if instance:
self.report.log("Using an existing instance: %s" % instance.id)
self.assert_equal(expected_states[-1], instance.status,
"Given instance is in a bad state.")
else:
self.report.log("Creating a new instance.")
instance = self.auth_client.instances.create(
instance_info.name,
instance_info.dbaas_flavor_href,
instance_info.volume,
instance_info.databases,
instance_info.users,
configuration=configuration_id,
availability_zone="nova",
datastore=instance_info.dbaas_datastore,
datastore_version=instance_info.dbaas_datastore_version)
self.assert_instance_action(
instance.id, expected_states[0:1], expected_http_code)
instance_info.id = instance.id
with CheckInstance(instance._info) as check:
check.flavor()
check.datastore()
check.links(instance._info['links'])
if self.VOLUME_SUPPORT:
check.volume()
self.assert_equal(trove_volume_size,
instance._info['volume']['size'],
"Unexpected Trove volume size")
self.assert_equal(instance_info.name, instance._info['name'],
"Unexpected instance name")
self.assert_equal(flavor.id,
int(instance._info['flavor']['id']),
"Unexpected instance flavor")
self.assert_equal(instance_info.dbaas_datastore,
instance._info['datastore']['type'],
"Unexpected instance datastore version")
self.assert_equal(instance_info.dbaas_datastore_version,
instance._info['datastore']['version'],
"Unexpected instance datastore version")
self.assert_configuration_group(instance_info.id, configuration_id)
return instance_info
def wait_for_created_instances(self, expected_states=['BUILD', 'ACTIVE']):
instances = [self.instance_info.id]
if self.init_inst_id:
instances.append(self.init_inst_id)
self.assert_all_instance_states(instances, expected_states)
def run_add_initialized_instance_data(self):
self.init_inst_data = DataType.small
self.init_inst_host = self.get_instance_host(self.instance_info.id)
self.test_helper.add_data(self.init_inst_data, self.init_inst_host)
def run_validate_initialized_instance(self):
if self.init_inst_id:
self.assert_instance_properties(
self.init_inst_id, self.init_inst_dbs, self.init_inst_users,
self.init_config_group_id, self.init_inst_data)
def assert_instance_properties(
self, instance_id, expected_dbs_definitions,
expected_user_definitions, expected_config_group_id,
expected_data_type):
if expected_dbs_definitions:
self.assert_database_list(instance_id, expected_dbs_definitions)
else:
self.report.log("No databases to validate for instance: %s"
% instance_id)
if expected_user_definitions:
self.assert_user_list(instance_id, expected_user_definitions)
else:
self.report.log("No users to validate for instance: %s"
% instance_id)
self.assert_configuration_group(instance_id, expected_config_group_id)
if self.init_inst_host:
self.test_helper.verify_data(
expected_data_type, self.init_inst_host)
else:
self.report.log("No data to validate for instance: %s"
% instance_id)
def assert_configuration_group(self, instance_id, expected_group_id):
instance = self.get_instance(instance_id)
if expected_group_id:
self.assert_equal(expected_group_id, instance.configuration['id'],
"Wrong configuration group attached")
else:
self.assert_false(hasattr(instance, 'configuration'),
"No configuration group expected")
def assert_database_list(self, instance_id, expected_databases):
expected_names = self._get_names(expected_databases)
full_list = self.auth_client.databases.list(instance_id)
self.assert_is_none(full_list.next,
"Unexpected pagination in the database list.")
listed_names = [database.name for database in full_list]
self.assert_list_elements_equal(expected_names, listed_names,
"Mismatch in instance databases.")
def _get_names(self, definitions):
return [item['name'] for item in definitions]
def assert_user_list(self, instance_id, expected_users):
expected_names = self._get_names(expected_users)
full_list = self.auth_client.users.list(instance_id)
self.assert_is_none(full_list.next,
"Unexpected pagination in the user list.")
listed_names = [user.name for user in full_list]
self.assert_list_elements_equal(expected_names, listed_names,
"Mismatch in instance users.")
# Verify that user definitions include only created databases.
all_databases = self._get_names(
self.test_helper.get_valid_database_definitions())
for user in expected_users:
self.assert_is_sublist(
self._get_names(user['databases']), all_databases,
"Definition of user '%s' specifies databases not included in "
"the list of initial databases." % user['name'])
def run_initialized_instance_delete(self, expected_states=['SHUTDOWN'],
expected_http_code=202):
if self.init_inst_id:
self.auth_client.instances.delete(self.init_inst_id)
self.assert_client_code(expected_http_code)
self.assert_all_gone(self.init_inst_id, expected_states[-1])
else:
raise SkipTest("Cleanup is not required.")
def run_initial_configuration_delete(self, expected_http_code=202):
if self.init_config_group_id:
self.auth_client.configurations.delete(self.init_config_group_id)
self.assert_client_code(expected_http_code)
else:
raise SkipTest("Cleanup is not required.")

View File

@ -62,13 +62,14 @@ class TestRunner(object):
EPHEMERAL_SUPPORT = not VOLUME_SUPPORT and CONFIG.get('device_path', None)
ROOT_PARTITION = not (VOLUME_SUPPORT or CONFIG.get('device_path', None))
report = CONFIG.get_report()
def __init__(self, sleep_time=10, timeout=1200):
self.def_sleep_time = sleep_time
self.def_timeout = timeout
self.instance_info = instance_info
self.auth_client = create_dbaas_client(self.instance_info.user)
self.unauth_client = None
self.report = CONFIG.get_report()
self._test_helper = None
@classmethod

View File

@ -13,10 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from proboscis import asserts
import urllib
from trove.tests.scenario.runners.test_runners import TestRunner
from troveclient.compat import exceptions
from troveclient.openstack.common.apiclient.exceptions import ValidationError
class UserActionsRunner(TestRunner):
@ -28,18 +29,13 @@ class UserActionsRunner(TestRunner):
# more appropriate anyways.
def run_users_create(self, expected_http_code=202):
users = [{'name': 'nodbguy', 'password': 'password1',
'databases': []},
{'name': 'singledbguy', 'password': 'password1',
'databases': [{'name': 'db1'}]},
{'name': 'multidbguy', 'password': 'password1',
'databases': [{'name': 'db1', 'name': 'db2'}]}]
users = self.test_helper.get_valid_user_definitions()
self.user_defs = self.assert_users_create(
self.instance_info.id, users, expected_http_code)
def assert_users_create(self, instance_id, serial_users_def,
expected_http_code):
self.rd_client.users.create(instance_id, serial_users_def)
self.auth_client.users.create(instance_id, serial_users_def)
self.assert_client_code(expected_http_code)
return serial_users_def
@ -51,16 +47,19 @@ class UserActionsRunner(TestRunner):
def assert_user_show(self, instance_id, expected_user_def,
expected_http_code):
user_name = expected_user_def['name']
queried_user = self.rd_client.users.get(instance_id, user_name, '%')
user_host = expected_user_def.get('host')
queried_user = self.auth_client.users.get(
instance_id, user_name, user_host)
self.assert_client_code(expected_http_code)
self._assert_user_matches(queried_user, expected_user_def)
def _assert_user_matches(self, user, expected_user_def):
user_name = expected_user_def['name']
asserts.assert_equal(user.name, expected_user_def['name'],
"Mismatch of names for user: %s" % user_name)
asserts.assert_equal(user.databases, expected_user_def['databases'],
"Mismatch of databases for user: %s" % user_name)
self.assert_equal(user.name, expected_user_def['name'],
"Mismatch of names for user: %s" % user_name)
self.assert_equal(user.databases, expected_user_def['databases'],
"Mismatch of databases for user: %s" % user_name)
def run_users_list(self, expected_http_code=200):
self.assert_users_list(
@ -68,15 +67,15 @@ class UserActionsRunner(TestRunner):
def assert_users_list(self, instance_id, expected_user_defs,
expected_http_code, limit=2):
full_list = self.rd_client.users.list(instance_id)
full_list = self.auth_client.users.list(instance_id)
self.assert_client_code(expected_http_code)
listed_users = {user.name: user for user in full_list}
asserts.assert_is_none(full_list.next,
"Unexpected pagination in the list.")
self.assert_is_none(full_list.next,
"Unexpected pagination in the list.")
for user_def in expected_user_defs:
user_name = user_def['name']
asserts.assert_true(
self.assert_true(
user_name in listed_users,
"User not included in the 'user-list' output: %s" %
user_name)
@ -84,34 +83,43 @@ class UserActionsRunner(TestRunner):
# Check that the system (ignored) users are not included in the output.
system_users = self.get_system_users()
asserts.assert_false(
self.assert_false(
any(name in listed_users for name in system_users),
"System users should not be included in the 'user-list' output.")
# Test list pagination.
list_page = self.rd_client.users.list(instance_id, limit=limit)
list_page = self.auth_client.users.list(instance_id, limit=limit)
self.assert_client_code(expected_http_code)
asserts.assert_true(len(list_page) <= limit)
asserts.assert_is_not_none(list_page.next, "List page is missing.")
self.assert_true(len(list_page) <= limit)
if len(full_list) > limit:
self.assert_is_not_none(list_page.next, "List page is missing.")
else:
self.assert_is_none(list_page.next, "An extra page in the list.")
marker = list_page.next
self.assert_pagination_match(list_page, full_list, 0, limit)
self.assert_pagination_match(
list_page[-1:], full_list, limit - 1, limit)
if marker:
last_user = list_page[-1]
expected_marker = urllib.quote(
'%s@%s' % (last_user.name, last_user.host))
self.assert_equal(expected_marker, marker,
"Pagination marker should be the last element "
"in the page.")
list_page = self.auth_client.users.list(instance_id, marker=marker)
self.assert_client_code(expected_http_code)
self.assert_pagination_match(
list_page, full_list, limit, len(full_list))
list_page = self.rd_client.users.list(instance_id, marker=marker)
self.assert_client_code(expected_http_code)
self.assert_pagination_match(
list_page, full_list, limit, len(full_list))
def run_negative_user_create(
def run_user_create_with_no_attributes(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
# Test with no attribites.
self.assert_users_create_failure(
self.instance_info.id, {}, expected_exception, expected_http_code)
def run_user_create_with_blank_name(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
# Test with missing user name attribute.
self.assert_users_create_failure(
self.instance_info.id,
@ -124,6 +132,9 @@ class UserActionsRunner(TestRunner):
{'name': '', 'password': 'password1', 'databases': []},
expected_exception, expected_http_code)
def run_user_create_with_blank_password(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
# Test with missing password attribute.
self.assert_users_create_failure(
self.instance_info.id,
@ -136,7 +147,9 @@ class UserActionsRunner(TestRunner):
{'name': 'nodbguy', 'password': 'password1'},
expected_exception, expected_http_code)
# Test creating an existing user.
def run_existing_user_create(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
self.assert_users_create_failure(
self.instance_info.id, self.user_defs[0],
expected_exception, expected_http_code)
@ -149,48 +162,52 @@ class UserActionsRunner(TestRunner):
# confusing (talking about a malformed request).
system_users = self.get_system_users()
if system_users:
for name in system_users:
user_def = {'name': name, 'password': 'password1',
'databases': []}
self.assert_users_create_failure(
self.instance_info.id, user_def,
expected_exception, expected_http_code)
user_defs = [{'name': name, 'password': 'password1',
'databases': []} for name in system_users]
self.assert_users_create_failure(
self.instance_info.id, user_defs,
expected_exception, expected_http_code)
def assert_users_create_failure(
self, instance_id, serial_users_def,
expected_exception, expected_http_code):
self.assert_raises(
expected_exception, expected_http_code,
self.rd_client.users.create, instance_id, serial_users_def)
self.auth_client.users.create, instance_id, serial_users_def)
def run_negative_user_attribute_update(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
# Test some basic invalid attributes on an existing user.
# Test with no attribites.
def run_user_update_with_no_attributes(self):
# Note: this is caught on the client-side.
self.assert_user_attribute_update_failure(
self.instance_info.id, 'nodbguy', {}, Exception, None)
self.instance_info.id, self.user_defs[0],
{}, ValidationError, None)
# Test with empty user name.
def run_user_update_with_blank_name(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
self.assert_user_attribute_update_failure(
self.instance_info.id, 'nodbguy', {'name': ''},
self.instance_info.id, self.user_defs[0], {'name': ''},
expected_exception, expected_http_code)
# Test updating an existing user with a conflicting name.
def run_user_update_with_existing_name(
self, expected_exception=exceptions.BadRequest,
expected_http_code=400):
self.assert_user_attribute_update_failure(
self.instance_info.id, self.user_defs[0]['name'],
{'name': self.user_defs[1]['name']},
self.instance_info.id, self.user_defs[0],
{'name': self.user_defs[0]['name']},
expected_exception, expected_http_code)
def assert_user_attribute_update_failure(
self, instance_id, user_name, update_attribites,
self, instance_id, user_def, update_attribites,
expected_exception, expected_http_code):
user_name, user_host = self._get_user_name_host_pair(user_def)
self.assert_raises(
expected_exception, expected_http_code,
self.rd_client.users.update_attributes, instance_id,
user_name, update_attribites)
self.auth_client.users.update_attributes, instance_id,
user_name, update_attribites, user_host)
def _get_user_name_host_pair(self, user_def):
return user_def['name'], user_def.get('host')
def run_system_user_attribute_update(
self, expected_exception=exceptions.BadRequest,
@ -201,21 +218,23 @@ class UserActionsRunner(TestRunner):
system_users = self.get_system_users()
if system_users:
for name in system_users:
update_attribites = {'name': name, 'password': 'password2'}
user_def = {'name': name, 'password': 'password2'}
self.assert_user_attribute_update_failure(
self.instance_info.id, name, update_attribites,
self.instance_info.id, user_def, user_def,
expected_exception, expected_http_code)
def run_user_attribute_update(self, expected_http_code=202):
update_attribites = {'name': 'dblessguy', 'password': 'password2'}
self.assert_user_attribute_update(
self.instance_info.id, 'nodbguy', update_attribites,
expected_http_code)
self.instance_info.id, self.user_defs[0],
update_attribites, expected_http_code)
def assert_user_attribute_update(self, instance_id, user_name,
def assert_user_attribute_update(self, instance_id, user_def,
update_attribites, expected_http_code):
self.rd_client.users.update_attributes(
instance_id, user_name, update_attribites)
user_name, user_host = self._get_user_name_host_pair(user_def)
self.auth_client.users.update_attributes(
instance_id, user_name, update_attribites, user_host)
self.assert_client_code(expected_http_code)
# Update the stored definitions with the new value.
@ -232,31 +251,36 @@ class UserActionsRunner(TestRunner):
def run_user_delete(self, expected_http_code=202):
for user_def in self.user_defs:
self.assert_user_delete(
self.instance_info.id, user_def['name'], expected_http_code)
self.instance_info.id, user_def, expected_http_code)
def assert_user_delete(self, instance_id, user_name, expected_http_code):
self.rd_client.users.delete(instance_id, user_name)
def assert_user_delete(self, instance_id, user_def, expected_http_code):
user_name, user_host = self._get_user_name_host_pair(user_def)
self.auth_client.users.delete(instance_id, user_name, user_host)
self.assert_client_code(expected_http_code)
self.assert_raises(exceptions.NotFound, 404,
self.rd_client.users.get,
instance_id, user_name, '%')
self.auth_client.users.get,
instance_id, user_name, user_host)
for user in self.rd_client.users.list(instance_id):
for user in self.auth_client.users.list(instance_id):
if user.name == user_name:
asserts.fail("User still listed after delete: %s" % user_name)
self.fail("User still listed after delete: %s" % user_name)
def run_nonexisting_user_show(
self, expected_exception=exceptions.NotFound,
expected_http_code=404):
self.assert_user_show_failure(self.instance_info.id, 'nonexistingusr',
expected_exception, expected_http_code)
self.assert_user_show_failure(
self.instance_info.id, {'name': 'nonexistingusr'},
expected_exception, expected_http_code)
def assert_user_show_failure(self, instance_id, user_name,
def assert_user_show_failure(self, instance_id, user_def,
expected_exception, expected_http_code):
user_name, user_host = self._get_user_name_host_pair(user_def)
self.assert_raises(
expected_exception, expected_http_code,
self.rd_client.users.get, instance_id, user_name, '%')
self.auth_client.users.get, instance_id, user_name, user_host)
def run_system_user_show(
self, expected_exception=exceptions.BadRequest,
@ -268,27 +292,31 @@ class UserActionsRunner(TestRunner):
if system_users:
for name in system_users:
self.assert_user_show_failure(
self.instance_info.id, name,
self.instance_info.id, {'name': name},
expected_exception, expected_http_code)
def run_nonexisting_user_update(self, expected_http_code=404):
# Test valid update on a non-existing user.
user_def = {'name': 'justashadow'}
self.assert_user_attribute_update_failure(
self.instance_info.id, 'nonexistingusr', {'name': 'justashadow'},
self.instance_info.id, user_def, user_def,
exceptions.NotFound, expected_http_code)
def run_nonexisting_user_delete(
self, expected_exception=exceptions.NotFound,
expected_http_code=404):
self.assert_user_delete_failure(self.instance_info.id, 'justashadow',
expected_exception, expected_http_code)
self.assert_user_delete_failure(
self.instance_info.id, {'name': 'justashadow'},
expected_exception, expected_http_code)
def assert_user_delete_failure(
self, instance_id, user_name,
self, instance_id, user_def,
expected_exception, expected_http_code):
user_name, user_host = self._get_user_name_host_pair(user_def)
self.assert_raises(expected_exception, expected_http_code,
self.rd_client.users.delete,
instance_id, user_name)
self.auth_client.users.delete,
instance_id, user_name, user_host)
def run_system_user_delete(
self, expected_exception=exceptions.BadRequest,
@ -300,7 +328,7 @@ class UserActionsRunner(TestRunner):
if system_users:
for name in system_users:
self.assert_user_delete_failure(
self.instance_info.id, name,
self.instance_info.id, {'name': name},
expected_exception, expected_http_code)
def get_system_users(self):