Merge "Remove hyphen from rand_name calls in scenario tests"
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 15369de..4fe2e9f 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -84,13 +84,11 @@
To enable and use locking test accounts you need do a few things:
- #. Enable the locking test account provider with the
- locking_credentials_provider option in the auth section
#. Create a accounts.yaml file which contains the set of pre-existing
credentials to use for testing. To make sure you don't have a credentials
starvation issue when running in parallel make sure you have at least 2
- times the number of parallel workers you are using to execute tempest
- available in the file.
+ times the number of worker processes you are using to execute tempest
+ available in the file. (if running serially the worker count is 1)
You can check the sample file packaged in tempest for the yaml format
#. Provide tempest with the location of you accounts.yaml file with the
@@ -125,15 +123,3 @@
is that if a test requires specific roles on accounts these tests can not be
run. This is because the config options do not give sufficient flexibility to
describe the roles assigned to a user for running the tests.
-
-You also can use the accounts.yaml file to specify the credentials used for
-testing. This will just allocate them serially so you only need to provide
-a pair of credentials. Do note that all the restrictions associated with
-locking test accounts applies to using the accounts.yaml file this way too,
-except since you can't run in parallel only 2 of each type of credential is
-required to run. However, the limitation on tests which require specific roles
-does not apply here.
-
-The procedure for doing this is very similar to with the locking accounts
-provider just don't set the locking_credentials_provider to true and you
-only should need a single pair of credentials.
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index f5e0cf8..175f0d9 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -94,8 +94,12 @@
#
# Path to the yaml file that contains the list of credentials to use
-# for running tests (string value)
-#test_accounts_file = etc/accounts.yaml
+# for running tests. If used when running in parallel you have to make
+# sure sufficient credentials are provided in the accounts file. For
+# example if no tests with roles are being run it requires at least `2
+# * CONC` distinct accounts configured in the `test_accounts_file`,
+# with CONC == the number of concurrent test processes. (string value)
+#test_accounts_file = <None>
# Allows test cases to create/destroy tenants and users. This option
# requires that OpenStack Identity API admin credentials are known. If
@@ -105,14 +109,6 @@
# Deprecated group/name - [orchestration]/allow_tenant_isolation
#allow_tenant_isolation = true
-# If set to True it enables the Accounts provider, which locks
-# credentials to allow for parallel execution with pre-provisioned
-# accounts. It can only be used to run tests that ensure credentials
-# cleanup happens. It requires at least `2 * CONC` distinct accounts
-# configured in `test_accounts_file`, with CONC == the number of
-# concurrent test processes. (boolean value)
-#locking_credentials_provider = false
-
# Roles to assign to all users created by tempest (list value)
#tempest_roles =
@@ -395,7 +391,8 @@
#block_migration_for_live_migration = false
# Does the test environment block migration support cinder iSCSI
-# volumes (boolean value)
+# volumes. Note, libvirt doesn't support this, see
+# https://bugs.launchpad.net/nova/+bug/1398999 (boolean value)
#block_migrate_cinder_iscsi = false
# Enable VNC console. This configuration value should be same as
@@ -670,7 +667,7 @@
#non_ssh_image_regex = ^.*[Ww]in.*$
# List of user mapped to regex to matching image names. (string value)
-#ssh_user_regex = [["^.*[Cc]irros.*$", "root"]]
+#ssh_user_regex = [["^.*[Cc]irros.*$", "cirros"]]
[messaging]
diff --git a/tempest/api/compute/test_live_block_migration.py b/tempest/api/compute/admin/test_live_migration.py
similarity index 100%
rename from tempest/api/compute/test_live_block_migration.py
rename to tempest/api/compute/admin/test_live_migration.py
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index c6d379c..5af9187 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -96,3 +96,15 @@
self.assertEqual(d_name, domain['name'])
self.assertFalse(domain['enabled'])
self.assertEqual(d_desc, domain['description'])
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('2abf8764-309a-4fa9-bc58-201b799817ad')
+ def test_create_domain_without_description(self):
+ # Create domain only with name
+ d_name = data_utils.rand_name('domain-')
+ domain = self.client.create_domain(d_name)
+ self.addCleanup(self._delete_domain, domain['id'])
+ self.assertIn('id', domain)
+ expected_data = {'name': d_name, 'enabled': True}
+ self.assertIsNone(domain['description'])
+ self.assertDictContainsSubset(expected_data, domain)
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index ad121b0..cf0b5e3 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -18,8 +18,16 @@
from tempest import exceptions
from tempest import test
+AGENT_TYPE = 'L3 agent'
+AGENT_MODES = (
+ 'legacy',
+ 'dvr_snat'
+)
+
class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest):
+ _agent_mode = 'legacy'
+
"""
Tests the following operations in the Neutron API using the REST client for
Neutron:
@@ -46,12 +54,17 @@
body = cls.admin_client.list_agents()
agents = body['agents']
for agent in agents:
- if agent['agent_type'] == 'L3 agent':
+ # TODO(armax): falling back on default _agent_mode can be
+ # dropped as soon as Icehouse is dropped.
+ agent_mode = (
+ agent['configurations'].get('agent_mode', cls._agent_mode))
+ if agent['agent_type'] == AGENT_TYPE and agent_mode in AGENT_MODES:
cls.agent = agent
break
else:
msg = "L3 Agent Scheduler enabled in conf, but L3 Agent not found"
raise exceptions.InvalidConfiguration(msg)
+ cls.router = cls.create_router(data_utils.rand_name('router'))
@test.attr(type='smoke')
@test.idempotent_id('b7ce6e89-e837-4ded-9b78-9ed3c9c6a45a')
@@ -62,22 +75,18 @@
@test.idempotent_id('9464e5e7-8625-49c3-8fd1-89c52be59d66')
def test_add_list_remove_router_on_l3_agent(self):
l3_agent_ids = list()
- name = data_utils.rand_name('router1-')
- router = self.client.create_router(name)
- self.addCleanup(self.client.delete_router, router['router']['id'])
self.admin_client.add_router_to_l3_agent(
self.agent['id'],
- router['router']['id'])
- body = self.admin_client.list_l3_agents_hosting_router(
- router['router']['id'])
+ self.router['id'])
+ body = (
+ self.admin_client.list_l3_agents_hosting_router(self.router['id']))
for agent in body['agents']:
l3_agent_ids.append(agent['id'])
self.assertIn('agent_type', agent)
self.assertEqual('L3 agent', agent['agent_type'])
self.assertIn(self.agent['id'], l3_agent_ids)
- del l3_agent_ids[:]
body = self.admin_client.remove_router_from_l3_agent(
self.agent['id'],
- router['router']['id'])
+ self.router['id'])
# NOTE(afazekas): The deletion not asserted, because neutron
# is not forbidden to reschedule the router to the same agent
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 09a5555..26a31cb 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -263,7 +263,8 @@
@classmethod
def create_router(cls, router_name=None, admin_state_up=False,
- external_network_id=None, enable_snat=None):
+ external_network_id=None, enable_snat=None,
+ **kwargs):
ext_gw_info = {}
if external_network_id:
ext_gw_info['network_id'] = external_network_id
@@ -271,7 +272,7 @@
ext_gw_info['enable_snat'] = enable_snat
body = cls.client.create_router(
router_name, external_gateway_info=ext_gw_info,
- admin_state_up=admin_state_up)
+ admin_state_up=admin_state_up, **kwargs)
router = body['router']
cls.routers.append(router)
return router
diff --git a/tempest/api/network/base_routers.py b/tempest/api/network/base_routers.py
index aa4e200..739e6f9 100644
--- a/tempest/api/network/base_routers.py
+++ b/tempest/api/network/base_routers.py
@@ -21,8 +21,21 @@
# as some router operations, such as enabling or disabling SNAT
# require admin credentials by default
- def _delete_router(self, router_id):
- self.client.delete_router(router_id)
+ def _cleanup_router(self, router):
+ self.delete_router(router)
+ self.routers.remove(router)
+
+ def _create_router(self, name, admin_state_up=False,
+ external_network_id=None, enable_snat=None):
+ # associate a cleanup with created routers to avoid quota limits
+ router = self.create_router(name, admin_state_up,
+ external_network_id, enable_snat)
+ self.addCleanup(self._cleanup_router, router)
+ return router
+
+ def _delete_router(self, router_id, network_client=None):
+ client = network_client or self.client
+ client.delete_router(router_id)
# Asserting that the router is not found in the list
# after deletion
list_body = self.client.list_routers()
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index c6f3849..7c0ab7f 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -44,18 +44,6 @@
if cls._ip_version == 4 else
CONF.network.tenant_network_v6_cidr)
- def _cleanup_router(self, router):
- self.delete_router(router)
- self.routers.remove(router)
-
- def _create_router(self, name, admin_state_up=False,
- external_network_id=None, enable_snat=None):
- # associate a cleanup with created routers to avoid quota limits
- router = self.create_router(name, admin_state_up,
- external_network_id, enable_snat)
- self.addCleanup(self._cleanup_router, router)
- return router
-
@test.attr(type='smoke')
@test.idempotent_id('f64403e2-8483-4b34-8ccd-b09a87bcc68c')
def test_create_show_list_update_delete_router(self):
@@ -363,3 +351,37 @@
class RoutersIpV6Test(RoutersTest):
_ip_version = 6
+
+
+class DvrRoutersTest(base.BaseRouterTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(DvrRoutersTest, cls).skip_checks()
+ if not test.is_extension_enabled('dvr', 'network'):
+ msg = "DVR extension not enabled."
+ raise cls.skipException(msg)
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('141297aa-3424-455d-aa8d-f2d95731e00a')
+ def test_create_distributed_router(self):
+ name = data_utils.rand_name('router')
+ create_body = self.admin_client.create_router(
+ name, distributed=True)
+ self.addCleanup(self._delete_router,
+ create_body['router']['id'],
+ self.admin_client)
+ self.assertTrue(create_body['router']['distributed'])
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('644d7a4a-01a1-4b68-bb8d-0c0042cb1729')
+ def test_convert_centralized_router(self):
+ router = self._create_router(data_utils.rand_name('router'))
+ self.assertNotIn('distributed', router)
+ update_body = self.admin_client.update_router(router['id'],
+ distributed=True)
+ self.assertTrue(update_body['router']['distributed'])
+ show_body = self.admin_client.show_router(router['id'])
+ self.assertTrue(show_body['router']['distributed'])
+ show_body = self.client.show_router(router['id'])
+ self.assertNotIn('distributed', show_body['router'])
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index ae17222..4149be3 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -114,3 +114,28 @@
class RoutersNegativeIpV6Test(RoutersNegativeTest):
_ip_version = 6
+
+
+class DvrRoutersNegativeTest(base.BaseRouterTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(DvrRoutersNegativeTest, cls).skip_checks()
+ if not test.is_extension_enabled('dvr', 'network'):
+ msg = "DVR extension not enabled."
+ raise cls.skipException(msg)
+
+ @classmethod
+ def resource_setup(cls):
+ super(DvrRoutersNegativeTest, cls).resource_setup()
+ cls.router = cls.create_router(data_utils.rand_name('router'))
+ cls.network = cls.create_network()
+ cls.subnet = cls.create_subnet(cls.network)
+
+ @test.attr(type=['negative', 'smoke'])
+ @test.idempotent_id('4990b055-8fc7-48ab-bba7-aa28beaad0b9')
+ def test_router_create_tenant_distributed_returns_forbidden(self):
+ self.assertRaises(lib_exc.Forbidden,
+ self.create_router,
+ data_utils.rand_name('router'),
+ distributed=True)
diff --git a/tempest/api_schema/response/compute/availability_zone.py b/tempest/api_schema/response/compute/availability_zone.py
deleted file mode 100644
index ab3e2ea..0000000
--- a/tempest/api_schema/response/compute/availability_zone.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2014 NEC Corporation. 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.
-
-# NOTE: This is the detail information for "get az detail" API.
-# The information is the same between v2 and v3 APIs.
-detail = {
- 'type': 'object',
- 'patternProperties': {
- # NOTE: Here is for a hostname
- '^[a-zA-Z0-9-_.]+$': {
- 'type': 'object',
- 'patternProperties': {
- # NOTE: Here is for a service name
- '^.*$': {
- 'type': 'object',
- 'properties': {
- 'available': {'type': 'boolean'},
- 'active': {'type': 'boolean'},
- 'updated_at': {'type': ['string', 'null']}
- },
- 'required': ['available', 'active', 'updated_at']
- }
- }
- }
- }
-}
diff --git a/tempest/api_schema/response/compute/v2_1/availability_zone.py b/tempest/api_schema/response/compute/v2_1/availability_zone.py
index e261d3d..5c1224e 100644
--- a/tempest/api_schema/response/compute/v2_1/availability_zone.py
+++ b/tempest/api_schema/response/compute/v2_1/availability_zone.py
@@ -14,8 +14,6 @@
import copy
-from tempest.api_schema.response.compute import availability_zone as common
-
base = {
'status_code': [200],
@@ -47,8 +45,30 @@
}
}
-get_availability_zone_list = copy.deepcopy(base)
+detail = {
+ 'type': 'object',
+ 'patternProperties': {
+ # NOTE: Here is for a hostname
+ '^[a-zA-Z0-9-_.]+$': {
+ 'type': 'object',
+ 'patternProperties': {
+ # NOTE: Here is for a service name
+ '^.*$': {
+ 'type': 'object',
+ 'properties': {
+ 'available': {'type': 'boolean'},
+ 'active': {'type': 'boolean'},
+ 'updated_at': {'type': ['string', 'null']}
+ },
+ 'required': ['available', 'active', 'updated_at']
+ }
+ }
+ }
+ }
+}
-get_availability_zone_list_detail = copy.deepcopy(base)
-get_availability_zone_list_detail['response_body']['properties'][
- 'availabilityZoneInfo']['items']['properties']['hosts'] = common.detail
+list_availability_zone_list = copy.deepcopy(base)
+
+list_availability_zone_list_detail = copy.deepcopy(base)
+list_availability_zone_list_detail['response_body']['properties'][
+ 'availabilityZoneInfo']['items']['properties']['hosts'] = detail
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index e970249..05aaabe 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -992,9 +992,13 @@
add_router_interface(RES['routers'])
create_secgroups(RES['secgroups'])
- create_servers(RES['servers'])
create_volumes(RES['volumes'])
- attach_volumes(RES['volumes'])
+
+ # Only attempt attaching the volumes if servers are defined in the
+ # resourcefile
+ if 'servers' in RES:
+ create_servers(RES['servers'])
+ attach_volumes(RES['volumes'])
def destroy_resources():
diff --git a/tempest/common/accounts.py b/tempest/common/accounts.py
index 8e9a018..6d376d6 100644
--- a/tempest/common/accounts.py
+++ b/tempest/common/accounts.py
@@ -38,7 +38,8 @@
def __init__(self, identity_version=None, name=None):
super(Accounts, self).__init__(identity_version=identity_version,
name=name)
- if os.path.isfile(CONF.auth.test_accounts_file):
+ if (CONF.auth.test_accounts_file and
+ os.path.isfile(CONF.auth.test_accounts_file)):
accounts = read_accounts_yaml(CONF.auth.test_accounts_file)
self.use_default_creds = False
else:
@@ -261,18 +262,14 @@
def _unique_creds(self, cred_arg=None):
"""Verify that the configured credentials are valid and distinct """
- if self.use_default_creds:
- try:
- user = self.get_primary_creds()
- alt_user = self.get_alt_creds()
- return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
- except exceptions.InvalidCredentials as ic:
- msg = "At least one of the configured credentials is " \
- "not valid: %s" % ic.message
- raise exceptions.InvalidConfiguration(msg)
- else:
- # TODO(andreaf) Add a uniqueness check here
- return len(self.hash_dict['creds']) > 1
+ try:
+ user = self.get_primary_creds()
+ alt_user = self.get_alt_creds()
+ return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
+ except exceptions.InvalidCredentials as ic:
+ msg = "At least one of the configured credentials is " \
+ "not valid: %s" % ic.message
+ raise exceptions.InvalidConfiguration(msg)
def is_multi_user(self):
return self._unique_creds('username')
@@ -280,42 +277,20 @@
def is_multi_tenant(self):
return self._unique_creds('tenant_id')
- def get_creds(self, id, roles=None):
- try:
- hashes = self._get_match_hash_list(roles)
- # No need to sort the dict as within the same python process
- # the HASH seed won't change, so subsequent calls to keys()
- # will return the same result
- _hash = hashes[id]
- except IndexError:
- msg = 'Insufficient number of users provided'
- raise exceptions.InvalidConfiguration(msg)
- return self.hash_dict['creds'][_hash]
-
def get_primary_creds(self):
if self.isolated_creds.get('primary'):
return self.isolated_creds.get('primary')
- if not self.use_default_creds:
- creds = self.get_creds(0)
- primary_credential = cred_provider.get_credentials(
- identity_version=self.identity_version, **creds)
- else:
- primary_credential = cred_provider.get_configured_credentials(
- credential_type='user', identity_version=self.identity_version)
+ primary_credential = cred_provider.get_configured_credentials(
+ credential_type='user', identity_version=self.identity_version)
self.isolated_creds['primary'] = primary_credential
return primary_credential
def get_alt_creds(self):
if self.isolated_creds.get('alt'):
return self.isolated_creds.get('alt')
- if not self.use_default_creds:
- creds = self.get_creds(1)
- alt_credential = cred_provider.get_credentials(
- identity_version=self.identity_version, **creds)
- else:
- alt_credential = cred_provider.get_configured_credentials(
- credential_type='alt_user',
- identity_version=self.identity_version)
+ alt_credential = cred_provider.get_configured_credentials(
+ credential_type='alt_user',
+ identity_version=self.identity_version)
self.isolated_creds['alt'] = alt_credential
return alt_credential
@@ -323,35 +298,14 @@
self.isolated_creds = {}
def get_admin_creds(self):
- if not self.use_default_creds:
- return self.get_creds_by_roles([CONF.identity.admin_role])
- else:
- creds = cred_provider.get_configured_credentials(
- "identity_admin", fill_in=False)
- self.isolated_creds['admin'] = creds
- return creds
+ creds = cred_provider.get_configured_credentials(
+ "identity_admin", fill_in=False)
+ self.isolated_creds['admin'] = creds
+ return creds
def get_creds_by_roles(self, roles, force_new=False):
- roles = list(set(roles))
- exist_creds = self.isolated_creds.get(str(roles), None)
- index = 0
- if exist_creds and not force_new:
- return exist_creds
- elif exist_creds and force_new:
- new_index = str(roles) + '-' + str(len(self.isolated_creds))
- self.isolated_creds[new_index] = exist_creds
- # Figure out how many existing creds for this roles set are present
- # use this as the index the returning hash list to ensure separate
- # creds are returned with force_new being True
- for creds_names in self.isolated_creds:
- if str(roles) in creds_names:
- index = index + 1
- if not self.use_default_creds:
- creds = self.get_creds(index, roles=roles)
- role_credential = cred_provider.get_credentials(**creds)
- self.isolated_creds[str(roles)] = role_credential
- else:
- msg = "Default credentials can not be used with specifying "\
- "credentials by roles"
- raise exceptions.InvalidConfiguration(msg)
- return role_credential
+ msg = "Credentials being specified through the config file can not be"\
+ " used with tests that specify using credentials by roles. "\
+ "Either exclude/skip the tests doing this or use either an "\
+ "test_accounts_file or tenant isolation."
+ raise exceptions.InvalidConfiguration(msg)
diff --git a/tempest/common/credentials.py b/tempest/common/credentials.py
index 1ca0128..c34df48 100644
--- a/tempest/common/credentials.py
+++ b/tempest/common/credentials.py
@@ -38,7 +38,8 @@
network_resources=network_resources,
identity_version=identity_version)
else:
- if CONF.auth.locking_credentials_provider:
+ if (CONF.auth.test_accounts_file and
+ os.path.isfile(CONF.auth.test_accounts_file)):
# Most params are not relevant for pre-created accounts
return accounts.Accounts(name=name,
identity_version=identity_version)
@@ -56,7 +57,8 @@
if CONF.auth.allow_tenant_isolation:
return is_admin
# Check whether test accounts file has the admin specified or not
- elif os.path.isfile(CONF.auth.test_accounts_file):
+ elif (CONF.auth.test_accounts_file and
+ os.path.isfile(CONF.auth.test_accounts_file)):
check_accounts = accounts.Accounts(name='check_admin')
if not check_accounts.admin_available():
is_admin = False
diff --git a/tempest/common/fixed_network.py b/tempest/common/fixed_network.py
index b06ddf2..b533898 100644
--- a/tempest/common/fixed_network.py
+++ b/tempest/common/fixed_network.py
@@ -15,6 +15,7 @@
from tempest_lib import exceptions as lib_exc
+from tempest.common import isolated_creds
from tempest import config
from tempest import exceptions
@@ -38,7 +39,7 @@
network = None
# NOTE(andreaf) get_primary_network will always be available once
# bp test-accounts-continued is implemented
- if (CONF.auth.allow_tenant_isolation and
+ if (isinstance(creds_provider, isolated_creds.IsolatedCreds) and
(CONF.service_available.neutron and
not CONF.service_available.ironic)):
network = creds_provider.get_primary_network()
diff --git a/tempest/config.py b/tempest/config.py
index f884baa..3725f58 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -23,6 +23,13 @@
from oslo_log import log as logging
+# TODO(marun) Replace use of oslo_config's global ConfigOpts
+# (cfg.CONF) instance with a local instance (cfg.ConfigOpts()) once
+# the cli tests move to the clients. The cli tests rely on oslo
+# incubator modules that use the global cfg.CONF.
+_CONF = cfg.CONF
+
+
def register_opt_group(conf, opt_group, options):
conf.register_group(opt_group)
for opt in options:
@@ -35,9 +42,14 @@
AuthGroup = [
cfg.StrOpt('test_accounts_file',
- default='etc/accounts.yaml',
help="Path to the yaml file that contains the list of "
- "credentials to use for running tests"),
+ "credentials to use for running tests. If used when "
+ "running in parallel you have to make sure sufficient "
+ "credentials are provided in the accounts file. For "
+ "example if no tests with roles are being run it requires "
+ "at least `2 * CONC` distinct accounts configured in "
+ " the `test_accounts_file`, with CONC == the "
+ "number of concurrent test processes."),
cfg.BoolOpt('allow_tenant_isolation',
default=True,
help="Allows test cases to create/destroy tenants and "
@@ -49,15 +61,6 @@
group='compute'),
cfg.DeprecatedOpt('allow_tenant_isolation',
group='orchestration')]),
- cfg.BoolOpt('locking_credentials_provider',
- default=False,
- help="If set to True it enables the Accounts provider, "
- "which locks credentials to allow for parallel execution "
- "with pre-provisioned accounts. It can only be used to "
- "run tests that ensure credentials cleanup happens. "
- "It requires at least `2 * CONC` distinct accounts "
- "configured in `test_accounts_file`, with CONC == the "
- "number of concurrent test processes."),
cfg.ListOpt('tempest_roles',
help="Roles to assign to all users created by tempest",
default=[]),
@@ -327,7 +330,8 @@
cfg.BoolOpt('block_migrate_cinder_iscsi',
default=False,
help="Does the test environment block migration support "
- "cinder iSCSI volumes"),
+ "cinder iSCSI volumes. Note, libvirt doesn't support this, "
+ "see https://bugs.launchpad.net/nova/+bug/1398999"),
cfg.BoolOpt('vnc_console',
default=False,
help='Enable VNC console. This configuration value should '
@@ -998,7 +1002,7 @@
help="SSH verification in tests is skipped"
"for matching images"),
cfg.StrOpt('ssh_user_regex',
- default="[[\"^.*[Cc]irros.*$\", \"root\"]]",
+ default="[[\"^.*[Cc]irros.*$\", \"cirros\"]]",
help="List of user mapped to regex "
"to matching image names."),
]
@@ -1108,7 +1112,7 @@
def register_opts():
for g, o in _opts:
- register_opt_group(cfg.CONF, g, o)
+ register_opt_group(_CONF, g, o)
def list_opts():
@@ -1132,45 +1136,44 @@
def __getattr__(self, attr):
# Handles config options from the default group
- return getattr(cfg.CONF, attr)
+ return getattr(_CONF, attr)
def _set_attrs(self):
- self.auth = cfg.CONF.auth
- self.compute = cfg.CONF.compute
- self.compute_feature_enabled = cfg.CONF['compute-feature-enabled']
- self.identity = cfg.CONF.identity
- self.identity_feature_enabled = cfg.CONF['identity-feature-enabled']
- self.image = cfg.CONF.image
- self.image_feature_enabled = cfg.CONF['image-feature-enabled']
- self.network = cfg.CONF.network
- self.network_feature_enabled = cfg.CONF['network-feature-enabled']
- self.volume = cfg.CONF.volume
- self.volume_feature_enabled = cfg.CONF['volume-feature-enabled']
- self.object_storage = cfg.CONF['object-storage']
- self.object_storage_feature_enabled = cfg.CONF[
+ self.auth = _CONF.auth
+ self.compute = _CONF.compute
+ self.compute_feature_enabled = _CONF['compute-feature-enabled']
+ self.identity = _CONF.identity
+ self.identity_feature_enabled = _CONF['identity-feature-enabled']
+ self.image = _CONF.image
+ self.image_feature_enabled = _CONF['image-feature-enabled']
+ self.network = _CONF.network
+ self.network_feature_enabled = _CONF['network-feature-enabled']
+ self.volume = _CONF.volume
+ self.volume_feature_enabled = _CONF['volume-feature-enabled']
+ self.object_storage = _CONF['object-storage']
+ self.object_storage_feature_enabled = _CONF[
'object-storage-feature-enabled']
- self.database = cfg.CONF.database
- self.orchestration = cfg.CONF.orchestration
- self.messaging = cfg.CONF.messaging
- self.telemetry = cfg.CONF.telemetry
- self.dashboard = cfg.CONF.dashboard
- self.data_processing = cfg.CONF.data_processing
- self.data_processing_feature_enabled = cfg.CONF[
+ self.database = _CONF.database
+ self.orchestration = _CONF.orchestration
+ self.messaging = _CONF.messaging
+ self.telemetry = _CONF.telemetry
+ self.dashboard = _CONF.dashboard
+ self.data_processing = _CONF.data_processing
+ self.data_processing_feature_enabled = _CONF[
'data_processing-feature-enabled']
- self.boto = cfg.CONF.boto
- self.stress = cfg.CONF.stress
- self.scenario = cfg.CONF.scenario
- self.service_available = cfg.CONF.service_available
- self.debug = cfg.CONF.debug
- self.baremetal = cfg.CONF.baremetal
- self.input_scenario = cfg.CONF['input-scenario']
- self.cli = cfg.CONF.cli
- self.negative = cfg.CONF.negative
- cfg.CONF.set_default('domain_name', self.identity.admin_domain_name,
- group='identity')
- cfg.CONF.set_default('alt_domain_name',
- self.identity.admin_domain_name,
- group='identity')
+ self.boto = _CONF.boto
+ self.stress = _CONF.stress
+ self.scenario = _CONF.scenario
+ self.service_available = _CONF.service_available
+ self.debug = _CONF.debug
+ self.baremetal = _CONF.baremetal
+ self.input_scenario = _CONF['input-scenario']
+ self.cli = _CONF.cli
+ self.negative = _CONF.negative
+ _CONF.set_default('domain_name', self.identity.admin_domain_name,
+ group='identity')
+ _CONF.set_default('alt_domain_name', self.identity.admin_domain_name,
+ group='identity')
def __init__(self, parse_conf=True, config_path=None):
"""Initialize a configuration from a conf directory and conf file."""
@@ -1196,18 +1199,18 @@
# to remove an issue with the config file up to date checker.
if parse_conf:
config_files.append(path)
- logging.register_options(cfg.CONF)
+ logging.register_options(_CONF)
if os.path.isfile(path):
- cfg.CONF([], project='tempest', default_config_files=config_files)
+ _CONF([], project='tempest', default_config_files=config_files)
else:
- cfg.CONF([], project='tempest')
- logging.setup(cfg.CONF, 'tempest')
+ _CONF([], project='tempest')
+ logging.setup(_CONF, 'tempest')
LOG = logging.getLogger('tempest')
LOG.info("Using tempest config file %s" % path)
register_opts()
self._set_attrs()
if parse_conf:
- cfg.CONF.log_opt_values(LOG, std_logging.DEBUG)
+ _CONF.log_opt_values(LOG, std_logging.DEBUG)
class TempestConfigProxy(object):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index ebdebcf..ce640b1 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -680,6 +680,8 @@
def _get_network_by_name(self, network_name):
net = self._list_networks(name=network_name)
+ self.assertNotEqual(len(net), 0,
+ "Unable to get network by name: %s" % network_name)
return net_resources.AttributeDict(net[0])
def create_floating_ip(self, thing, external_network_id=None,
@@ -1049,6 +1051,9 @@
# not (the current baremetal case). Likely can be removed when
# test account mgmt is reworked:
# https://blueprints.launchpad.net/tempest/+spec/test-accounts
+ if not CONF.compute.fixed_network_name:
+ m = 'fixed_network_name must be specified in config'
+ raise exceptions.InvalidConfiguration(m)
network = self._get_network_by_name(
CONF.compute.fixed_network_name)
router = None
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 3e259b0..8fa2df5 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -12,7 +12,6 @@
from oslo_log import log
from tempest_lib.common.utils import data_utils
-from tempest_lib import decorators
from tempest import config
from tempest.scenario import manager
@@ -132,7 +131,6 @@
actual = self._get_content(ssh_client)
self.assertEqual(expected, actual)
- @decorators.skip_because(bug='1373513')
@test.idempotent_id('557cd2c2-4eb8-4dce-98be-f86765ff311b')
@test.services('compute', 'volume', 'image')
def test_volume_boot_pattern(self):
@@ -176,7 +174,6 @@
# NOTE(gfidente): ensure resources are in clean state for
# deletion operations to succeed
self._stop_instances([instance_2nd, instance_from_snapshot])
- self._detach_volumes([volume_origin, volume])
class TestVolumeBootPatternV2(TestVolumeBootPattern):
diff --git a/tempest/services/compute/json/availability_zone_client.py b/tempest/services/compute/json/availability_zone_client.py
index b541a2c..6c50398 100644
--- a/tempest/services/compute/json/availability_zone_client.py
+++ b/tempest/services/compute/json/availability_zone_client.py
@@ -25,14 +25,14 @@
def get_availability_zone_list(self):
resp, body = self.get('os-availability-zone')
body = json.loads(body)
- self.validate_response(schema.get_availability_zone_list, resp, body)
+ self.validate_response(schema.list_availability_zone_list, resp, body)
return service_client.ResponseBodyList(resp,
body['availabilityZoneInfo'])
def get_availability_zone_list_detail(self):
resp, body = self.get('os-availability-zone/detail')
body = json.loads(body)
- self.validate_response(schema.get_availability_zone_list_detail, resp,
+ self.validate_response(schema.list_availability_zone_list_detail, resp,
body)
return service_client.ResponseBodyList(resp,
body['availabilityZoneInfo'])
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index e84d627..62e60d6 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -23,7 +23,7 @@
from tempest_lib.common.utils import data_utils
from tempest import clients
-from tempest.common import cred_provider
+from tempest.common import isolated_creds
from tempest.common import ssh
from tempest import config
from tempest import exceptions
@@ -149,15 +149,22 @@
username = data_utils.rand_name("stress_user")
tenant_name = data_utils.rand_name("stress_tenant")
password = "pass"
- identity_client = admin_manager.identity_client
- tenant = identity_client.create_tenant(name=tenant_name)
- identity_client.create_user(username,
- password,
- tenant['id'],
- "email")
- creds = cred_provider.get_credentials(username=username,
- password=password,
- tenant_name=tenant_name)
+ if CONF.identity.auth_version == 'v2':
+ identity_client = admin_manager.identity_client
+ else:
+ identity_client = admin_manager.identity_v3_client
+ credentials_client = isolated_creds.get_creds_client(
+ identity_client)
+ project = credentials_client.create_project(
+ name=tenant_name, description=tenant_name)
+ user = credentials_client.create_user(username, password,
+ project['id'], "email")
+ # Add roles specified in config file
+ for conf_role in CONF.auth.tempest_roles:
+ credentials_client.assign_user_role(user, project,
+ conf_role)
+ creds = credentials_client.get_credentials(user, project,
+ password)
manager = clients.Manager(credentials=creds)
test_obj = importutils.import_class(test['action'])
diff --git a/tempest/tests/common/test_accounts.py b/tempest/tests/common/test_accounts.py
index 2a98a06..6371e49 100644
--- a/tempest/tests/common/test_accounts.py
+++ b/tempest/tests/common/test_accounts.py
@@ -67,7 +67,7 @@
self.useFixture(mockpatch.Patch(
'tempest.common.accounts.read_accounts_yaml',
return_value=self.test_accounts))
- cfg.CONF.set_default('test_accounts_file', '', group='auth')
+ cfg.CONF.set_default('test_accounts_file', 'fake_path', group='auth')
self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
def _get_hash_list(self, accounts_list):
@@ -297,12 +297,8 @@
cfg.CONF.set_default('test_accounts_file', '', group='auth')
self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
- def test_get_creds(self):
+ def test_get_creds_roles_nonlocking_invalid(self):
test_accounts_class = accounts.NotLockingAccounts('v2', 'test_name')
- for i in xrange(len(self.test_accounts)):
- creds = test_accounts_class.get_creds(i)
- msg = "Empty credentials returned for ID %s" % str(i)
- self.assertIsNotNone(creds, msg)
self.assertRaises(exceptions.InvalidConfiguration,
- test_accounts_class.get_creds,
- id=len(self.test_accounts))
+ test_accounts_class.get_creds_by_roles,
+ ['fake_role'])
diff --git a/tempest/tests/common/test_admin_available.py b/tempest/tests/common/test_admin_available.py
new file mode 100644
index 0000000..4e3aa4c
--- /dev/null
+++ b/tempest/tests/common/test_admin_available.py
@@ -0,0 +1,104 @@
+# Copyright 2015 Red Hat, Inc.
+#
+# 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 oslo.config import cfg
+from oslotest import mockpatch
+
+from tempest.common import credentials
+from tempest import config
+from tempest.tests import base
+from tempest.tests import fake_config
+
+
+class TestAdminAvailable(base.TestCase):
+
+ def setUp(self):
+ super(TestAdminAvailable, self).setUp()
+ self.useFixture(fake_config.ConfigFixture())
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+
+ def run_test(self, tenant_isolation, use_accounts_file, admin_creds):
+
+ cfg.CONF.set_default('allow_tenant_isolation',
+ tenant_isolation, group='auth')
+ if use_accounts_file:
+ accounts = [{'username': 'u1',
+ 'tenant_name': 't1',
+ 'password': 'p'},
+ {'username': 'u2',
+ 'tenant_name': 't2',
+ 'password': 'p'}]
+ if admin_creds == 'role':
+ accounts.append({'username': 'admin',
+ 'tenant_name': 'admin',
+ 'password': 'p',
+ 'roles': ['admin']})
+ elif admin_creds == 'type':
+ accounts.append({'username': 'admin',
+ 'tenant_name': 'admin',
+ 'password': 'p',
+ 'types': ['admin']})
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.accounts.read_accounts_yaml',
+ return_value=accounts))
+ cfg.CONF.set_default('test_accounts_file',
+ use_accounts_file, group='auth')
+ self.useFixture(mockpatch.Patch('os.path.isfile',
+ return_value=True))
+ else:
+ self.useFixture(mockpatch.Patch('os.path.isfile',
+ return_value=False))
+ if admin_creds:
+ (u, t, p) = ('u', 't', 'p')
+ else:
+ (u, t, p) = (None, None, None)
+
+ cfg.CONF.set_default('admin_username', u, group='identity')
+ cfg.CONF.set_default('admin_tenant_name', t, group='identity')
+ cfg.CONF.set_default('admin_password', p, group='identity')
+
+ expected = admin_creds is not None or tenant_isolation
+ observed = credentials.is_admin_available()
+ self.assertEqual(expected, observed)
+
+ # Tenant isolation implies admin so only one test case for True
+ def test__tenant_isolation__accounts_file__no_admin(self):
+ self.run_test(tenant_isolation=True,
+ use_accounts_file=True,
+ admin_creds=None)
+
+ def test__no_tenant_isolation__accounts_file__no_admin(self):
+ self.run_test(tenant_isolation=False,
+ use_accounts_file=True,
+ admin_creds=None)
+
+ def test__no_tenant_isolation__accounts_file__admin_role(self):
+ self.run_test(tenant_isolation=False,
+ use_accounts_file=True,
+ admin_creds='role')
+
+ def test__no_tenant_isolation__accounts_file__admin_type(self):
+ self.run_test(tenant_isolation=False,
+ use_accounts_file=True,
+ admin_creds='type')
+
+ def test__no_tenant_isolation__no_accounts_file__no_admin(self):
+ self.run_test(tenant_isolation=False,
+ use_accounts_file=False,
+ admin_creds=None)
+
+ def test__no_tenant_isolation__no_accounts_file__admin(self):
+ self.run_test(tenant_isolation=False,
+ use_accounts_file=False,
+ admin_creds='role')
diff --git a/tools/check_uuid.py b/tools/check_uuid.py
index 34effe4..e21c3d8 100755
--- a/tools/check_uuid.py
+++ b/tools/check_uuid.py
@@ -275,7 +275,7 @@
if not test_uuid:
return
if test_uuid in uuids:
- error_str = "%s:%s\n uuid %s collision: %s<->%s\n%s:%s\n" % (
+ error_str = "%s:%s\n uuid %s collision: %s<->%s\n%s:%s" % (
tests[module_name]['source_path'],
tests[module_name]['tests'][test_name].lineno,
test_uuid,
@@ -285,6 +285,8 @@
uuids[test_uuid]['test_node'].lineno,
)
print(error_str)
+ print("cannot automatically resolve the collision, please "
+ "manually remove the duplicate value on the new test.")
return True
else:
uuids[test_uuid] = {