Merge "Remove the unused tenant and user option in cleanup"
diff --git a/.gitignore b/.gitignore
index e96deb1..9292dbb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
AUTHORS
ChangeLog
*.pyc
+__pycache__/
etc/tempest.conf
etc/tempest.conf.sample
etc/logging.conf
diff --git a/README.rst b/README.rst
index fe65dcd..53c7de5 100644
--- a/README.rst
+++ b/README.rst
@@ -1,13 +1,8 @@
Tempest - The OpenStack Integration Test Suite
==============================================
-.. image:: https://img.shields.io/pypi/v/tempest.svg
- :target: https://pypi.python.org/pypi/tempest/
- :alt: Latest Version
-
-.. image:: https://img.shields.io/pypi/dm/tempest.svg
- :target: https://pypi.python.org/pypi/tempest/
- :alt: Downloads
+The documentation for Tempest is officially hosted at:
+http://docs.openstack.org/developer/tempest/
This is a set of integration tests to be run against a live OpenStack
cluster. Tempest has batteries of tests for OpenStack API validation,
@@ -63,12 +58,14 @@
This can be done within a venv, but the assumption for this guide is that
the Tempest cli entry point will be in your shell's PATH.
-#. Installing Tempest will create a /etc/tempest dir which will contain the
- sample config file packaged with Tempest. The contents of /etc/tempest will
- be copied to all local working dirs, so if there is any common configuration
- you'd like to be shared between anyone setting up local Tempest working dirs
- it's recommended that you copy or rename tempest.conf.sample to tempest.conf
- and make those changes to that file in /etc/tempest
+#. Installing Tempest may create a /etc/tempest dir, however if one isn't
+ created you can create one or use ~/.tempest/etc or ~/.config/tempest in
+ place of /etc/tempest. If none of these dirs are created tempest will create
+ ~/.tempest/etc when it's needed. The contents of this dir will always
+ automatically be copied to all etc/ dirs in local workspaces as an initial
+ setup step. So if there is any common configuration you'd like to be shared
+ between local Tempest workspaces it's recommended that you pre-populate it
+ before running ``tempest init``.
#. Setup a local Tempest workspace. This is done by using the tempest init
command::
@@ -88,7 +85,8 @@
config files located in the etc/ subdir created by the ``tempest init``
command. Tempest is expecting a tempest.conf file in etc/ so if only a
sample exists you must rename or copy it to tempest.conf before making
- any changes to it otherwise Tempest will not know how to load it.
+ any changes to it otherwise Tempest will not know how to load it. For
+ details on configuring tempest refer to the :ref:`tempest-configuration`.
#. Once the configuration is done you're now ready to run Tempest. This can
be done using the :ref:`tempest_run` command. This can be done by either
diff --git a/doc/source/test-removal.rst b/doc/source/test-removal.rst
index 6570bb7..79a5846 100644
--- a/doc/source/test-removal.rst
+++ b/doc/source/test-removal.rst
@@ -31,7 +31,7 @@
#. The tests proposed for removal must have equiv. coverage in a different
project's test suite (whether this is another gating test project, or an in
- tree funcitonal test suite) For API tests preferably the other project will
+ tree functional test suite). For API tests preferably the other project will
have a similar source of friction in place to prevent breaking api changes
so that we don't regress and let breaking api changes slip through the
gate.
@@ -62,7 +62,7 @@
SELECT * from tests where test_id like "%test_id%";
(where $test_id is the full test_id, but truncated to the class because of
-setupclass or teardownclass failures)
+setupClass or tearDownClass failures)
You can access the infra mysql subunit2sql db w/ read-only permissions with:
@@ -113,7 +113,7 @@
well ahead of the scheduled meeting. Since the meeting time will be well known
ahead of time anyone who depends on the tests will have ample time beforehand
to outline any concerns on the before the meeting. To give ample time for
-people to respond to removal proposals please add things to the agend by the
+people to respond to removal proposals please add things to the agenda by the
Monday before the meeting.
The other option is to raise the removal on the openstack-dev mailing list.
@@ -163,6 +163,6 @@
anything that lives in tempest which doesn't test one of these projects can be
removed assuming there is equivalent testing elsewhere. Preferably using the
`tempest plugin mechanism`_
-to mantain continuity after migrating the tests out of tempest
+to maintain continuity after migrating the tests out of tempest.
.. _tempest plugin mechanism: http://docs.openstack.org/developer/tempest/plugin.html
diff --git a/releasenotes/notes/service_client_config-8a1d7b4de769c633.yaml b/releasenotes/notes/service_client_config-8a1d7b4de769c633.yaml
new file mode 100644
index 0000000..3e43f9a
--- /dev/null
+++ b/releasenotes/notes/service_client_config-8a1d7b4de769c633.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - A new helper method `service_client_config` has been added
+ to the stable module config.py that returns extracts from
+ configuration into a dictionary the configuration settings
+ relevant for the initisialisation of a service client.
diff --git a/requirements.txt b/requirements.txt
index 84be219..fdcb3d5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,13 +8,12 @@
paramiko>=2.0 # LGPLv2.1+
netaddr!=0.7.16,>=0.7.12 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
-pyOpenSSL>=0.14 # Apache-2.0
oslo.concurrency>=3.8.0 # Apache-2.0
-oslo.config>=3.10.0 # Apache-2.0
+oslo.config>=3.12.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.log>=1.14.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
-oslo.utils>=3.14.0 # Apache-2.0
+oslo.utils>=3.15.0 # Apache-2.0
six>=1.9.0 # MIT
fixtures>=3.0.0 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index fdf55e5..d02f86f 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -16,7 +16,9 @@
import time
from tempest.api.compute import base
+from tempest.common import compute
from tempest.common.utils import net_utils
+from tempest.common import waiters
from tempest import config
from tempest import exceptions
from tempest.lib import exceptions as lib_exc
@@ -48,6 +50,7 @@
cls.networks_client = cls.os.networks_client
cls.subnets_client = cls.os.subnets_client
cls.ports_client = cls.os.ports_client
+ cls.servers_client = cls.servers_client
def wait_for_interface_status(self, server, port_id, status):
"""Waits for an interface to reach a given status."""
@@ -73,6 +76,34 @@
return body
+ # TODO(mriedem): move this into a common waiters utility module
+ def wait_for_port_detach(self, port_id):
+ """Waits for the port's device_id to be unset.
+
+ :param port_id: The id of the port being detached.
+ :returns: The final port dict from the show_port response.
+ """
+ port = self.ports_client.show_port(port_id)['port']
+ device_id = port['device_id']
+ start = int(time.time())
+
+ # NOTE(mriedem): Nova updates the port's device_id to '' rather than
+ # None, but it's not contractual so handle Falsey either way.
+ while device_id:
+ time.sleep(self.build_interval)
+ port = self.ports_client.show_port(port_id)['port']
+ device_id = port['device_id']
+
+ timed_out = int(time.time()) - start >= self.build_timeout
+
+ if device_id and timed_out:
+ message = ('Port %s failed to detach (device_id %s) within '
+ 'the required time (%s s).' %
+ (port_id, device_id, self.build_timeout))
+ raise exceptions.TimeoutException(message)
+
+ return port
+
def _check_interface(self, iface, port_id=None, network_id=None,
fixed_ip=None, mac_addr=None):
self.assertIn('port_state', iface)
@@ -240,3 +271,40 @@
if fixed_ip is not None:
break
self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
+
+ @test.idempotent_id('2f3a0127-95c7-4977-92d2-bc5aec602fb4')
+ def test_reassign_port_between_servers(self):
+ """Tests the following:
+
+ 1. Create a port in Neutron.
+ 2. Create two servers in Nova.
+ 3. Attach the port to the first server.
+ 4. Detach the port from the first server.
+ 5. Attach the port to the second server.
+ 6. Detach the port from the second server.
+ """
+ network = self.get_tenant_network()
+ network_id = network['id']
+ port = self.ports_client.create_port(network_id=network_id)
+ port_id = port['port']['id']
+ self.addCleanup(self.ports_client.delete_port, port_id)
+
+ # create two servers
+ _, servers = compute.create_test_server(
+ self.os, tenant_network=network, wait_until='ACTIVE', min_count=2)
+ # add our cleanups for the servers since we bypassed the base class
+ for server in servers:
+ self.addCleanup(waiters.wait_for_server_termination,
+ self.servers_client, server['id'])
+ self.addCleanup(self.servers_client.delete_server, server['id'])
+
+ for server in servers:
+ # attach the port to the server
+ iface = self.client.create_interface(
+ server['id'], port_id=port_id)['interfaceAttachment']
+ self._check_interface(iface, port_id=port_id)
+
+ # detach the port from the server; this is a cast in the compute
+ # API so we have to poll the port until the device_id is unset.
+ self.client.delete_interface(server['id'], port_id)
+ self.wait_for_port_detach(port_id)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 0d06119..21e7d10 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -110,6 +110,10 @@
servers_client=self.client)
boot_time = linux_client.get_boot_time()
+ # NOTE: This sync is for avoiding the loss of pub key data
+ # in a server
+ linux_client.exec_command("sync")
+
self.client.reboot_server(self.server_id, type=reboot_type)
waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index decccf3..d5ba76c 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -349,7 +349,7 @@
return None
for plugin in CONF.data_processing_feature_enabled.plugins:
- if plugin in DEFAULT_TEMPLATES.keys():
+ if plugin in DEFAULT_TEMPLATES:
break
else:
plugin = ''
diff --git a/tempest/api/identity/admin/v2/test_roles.py b/tempest/api/identity/admin/v2/test_roles.py
index 0924619..380920f 100644
--- a/tempest/api/identity/admin/v2/test_roles.py
+++ b/tempest/api/identity/admin/v2/test_roles.py
@@ -17,6 +17,7 @@
from tempest.api.identity import base
from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
from tempest import test
@@ -25,17 +26,22 @@
@classmethod
def resource_setup(cls):
super(RolesTestJSON, cls).resource_setup()
+ cls.roles = list()
for _ in moves.xrange(5):
role_name = data_utils.rand_name(name='role')
role = cls.roles_client.create_role(name=role_name)['role']
- cls.data.roles.append(role)
+ cls.roles.append(role)
+
+ @classmethod
+ def resource_cleanup(cls):
+ super(RolesTestJSON, cls).resource_cleanup()
+ for role in cls.roles:
+ cls.roles_client.delete_role(role['id'])
def _get_role_params(self):
- self.data.setup_test_user()
- self.data.setup_test_role()
- user = self.get_user_by_name(self.data.user['name'])
- tenant = self.get_tenant_by_name(self.data.tenant['name'])
- role = self.get_role_by_name(self.data.role['name'])
+ user = self.setup_test_user()
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+ role = self.setup_test_role()
return (user, tenant, role)
def assert_role_in_role_list(self, role, roles):
@@ -49,15 +55,17 @@
def test_list_roles(self):
"""Return a list of all roles."""
body = self.roles_client.list_roles()['roles']
- found = [role for role in body if role in self.data.roles]
+ found = [role for role in body if role in self.roles]
self.assertTrue(any(found))
- self.assertEqual(len(found), len(self.data.roles))
+ self.assertEqual(len(found), len(self.roles))
@test.idempotent_id('c62d909d-6c21-48c0-ae40-0a0760e6db5e')
def test_role_create_delete(self):
"""Role should be created, verified, and deleted."""
role_name = data_utils.rand_name(name='role-test')
body = self.roles_client.create_role(name=role_name)['role']
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.roles_client.delete_role, body['id'])
self.assertEqual(role_name, body['name'])
body = self.roles_client.list_roles()['roles']
@@ -73,9 +81,9 @@
@test.idempotent_id('db6870bd-a6ed-43be-a9b1-2f10a5c9994f')
def test_get_role_by_id(self):
"""Get a role by its id."""
- self.data.setup_test_role()
- role_id = self.data.role['id']
- role_name = self.data.role['name']
+ role = self.setup_test_role()
+ role_id = role['id']
+ role_name = role['name']
body = self.roles_client.show_role(role_id)['role']
self.assertEqual(role_id, body['id'])
self.assertEqual(role_name, body['name'])
diff --git a/tempest/api/identity/admin/v2/test_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
index 770bb14..7116913 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -22,11 +22,9 @@
class RolesNegativeTestJSON(base.BaseIdentityV2AdminTest):
def _get_role_params(self):
- self.data.setup_test_user()
- self.data.setup_test_role()
- user = self.get_user_by_name(self.data.user['name'])
- tenant = self.get_tenant_by_name(self.data.tenant['name'])
- role = self.get_role_by_name(self.data.role['name'])
+ user = self.setup_test_user()
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+ role = self.setup_test_role()
return (user, tenant, role)
@test.attr(type=['negative'])
@@ -89,7 +87,7 @@
# Non-administrator user should not be able to delete role
role_name = data_utils.rand_name(name='role')
body = self.roles_client.create_role(name=role_name)['role']
- self.data.roles.append(body)
+ self.addCleanup(self.roles_client.delete_role, body['id'])
role_id = body.get('id')
self.assertRaises(lib_exc.Forbidden,
self.non_admin_roles_client.delete_role, role_id)
@@ -100,7 +98,7 @@
# Request to delete role without a valid token should fail
role_name = data_utils.rand_name(name='role')
body = self.roles_client.create_role(name=role_name)['role']
- self.data.roles.append(body)
+ self.addCleanup(self.roles_client.delete_role, body['id'])
role_id = body.get('id')
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index d1e0217..baa78e9 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -44,7 +44,7 @@
# Non-administrator user should not be able to delete a tenant
tenant_name = data_utils.rand_name(name='tenant')
tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- self.data.tenants.append(tenant)
+ self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.delete_tenant,
tenant['id'])
@@ -55,7 +55,7 @@
# Request to delete a tenant without a valid token should fail
tenant_name = data_utils.rand_name(name='tenant')
tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- self.data.tenants.append(tenant)
+ self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(lib_exc.Unauthorized,
@@ -76,12 +76,9 @@
# Tenant names should be unique
tenant_name = data_utils.rand_name(name='tenant')
body = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- tenant = body
- self.data.tenants.append(tenant)
tenant1_id = body.get('id')
self.addCleanup(self.tenants_client.delete_tenant, tenant1_id)
- self.addCleanup(self.data.tenants.remove, tenant)
self.assertRaises(lib_exc.Conflict, self.tenants_client.create_tenant,
name=tenant_name)
@@ -136,7 +133,7 @@
# Non-administrator user should not be able to update a tenant
tenant_name = data_utils.rand_name(name='tenant')
tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- self.data.tenants.append(tenant)
+ self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.update_tenant,
tenant['id'])
@@ -147,7 +144,7 @@
# Request to update a tenant without a valid token should fail
tenant_name = data_utils.rand_name(name='tenant')
tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- self.data.tenants.append(tenant)
+ self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(lib_exc.Unauthorized,
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index 1aa80df..583fa8e 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -17,6 +17,7 @@
from tempest.api.identity import base
from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
from tempest import test
@@ -30,7 +31,9 @@
tenant_name = data_utils.rand_name(name='tenant-new')
tenant = self.tenants_client.create_tenant(
name=tenant_name)['tenant']
- self.data.tenants.append(tenant)
+ # Add the tenant to the cleanup list
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.tenants_client.delete_tenant, tenant['id'])
tenants.append(tenant)
tenant_ids = map(lambda x: x['id'], tenants)
body = self.tenants_client.list_tenants()['tenants']
@@ -39,7 +42,6 @@
for tenant in tenants:
self.tenants_client.delete_tenant(tenant['id'])
- self.data.tenants.remove(tenant)
body = self.tenants_client.list_tenants()['tenants']
found = [tenant for tenant in body if tenant['id'] in tenant_ids]
@@ -53,7 +55,9 @@
body = self.tenants_client.create_tenant(name=tenant_name,
description=tenant_desc)
tenant = body['tenant']
- self.data.tenants.append(tenant)
+ # Add the tenant to the cleanup list
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.tenants_client.delete_tenant, tenant['id'])
tenant_id = tenant['id']
desc1 = tenant['description']
self.assertEqual(desc1, tenant_desc, 'Description should have '
@@ -63,7 +67,6 @@
self.assertEqual(desc2, tenant_desc, 'Description does not appear'
'to be set')
self.tenants_client.delete_tenant(tenant_id)
- self.data.tenants.remove(tenant)
@test.idempotent_id('670bdddc-1cd7-41c7-b8e2-751cfb67df50')
def test_tenant_create_enabled(self):
@@ -72,7 +75,9 @@
body = self.tenants_client.create_tenant(name=tenant_name,
enabled=True)
tenant = body['tenant']
- self.data.tenants.append(tenant)
+ # Add the tenant to the cleanup list
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.tenants_client.delete_tenant, tenant['id'])
tenant_id = tenant['id']
en1 = tenant['enabled']
self.assertTrue(en1, 'Enable should be True in response')
@@ -80,7 +85,6 @@
en2 = body['enabled']
self.assertTrue(en2, 'Enable should be True in lookup')
self.tenants_client.delete_tenant(tenant_id)
- self.data.tenants.remove(tenant)
@test.idempotent_id('3be22093-b30f-499d-b772-38340e5e16fb')
def test_tenant_create_not_enabled(self):
@@ -89,7 +93,9 @@
body = self.tenants_client.create_tenant(name=tenant_name,
enabled=False)
tenant = body['tenant']
- self.data.tenants.append(tenant)
+ # Add the tenant to the cleanup list
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.tenants_client.delete_tenant, tenant['id'])
tenant_id = tenant['id']
en1 = tenant['enabled']
self.assertEqual('false', str(en1).lower(),
@@ -99,7 +105,6 @@
self.assertEqual('false', str(en2).lower(),
'Enable should be False in lookup')
self.tenants_client.delete_tenant(tenant_id)
- self.data.tenants.remove(tenant)
@test.idempotent_id('781f2266-d128-47f3-8bdb-f70970add238')
def test_tenant_update_name(self):
@@ -107,7 +112,9 @@
t_name1 = data_utils.rand_name(name='tenant')
body = self.tenants_client.create_tenant(name=t_name1)['tenant']
tenant = body
- self.data.tenants.append(tenant)
+ # Add the tenant to the cleanup list
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.tenants_client.delete_tenant, tenant['id'])
t_id = body['id']
resp1_name = body['name']
@@ -125,7 +132,6 @@
self.assertEqual(resp2_name, resp3_name)
self.tenants_client.delete_tenant(t_id)
- self.data.tenants.remove(tenant)
@test.idempotent_id('859fcfe1-3a03-41ef-86f9-b19a47d1cd87')
def test_tenant_update_desc(self):
@@ -135,7 +141,9 @@
body = self.tenants_client.create_tenant(name=t_name,
description=t_desc)
tenant = body['tenant']
- self.data.tenants.append(tenant)
+ # Add the tenant to the cleanup list
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.tenants_client.delete_tenant, tenant['id'])
t_id = tenant['id']
resp1_desc = tenant['description']
@@ -154,7 +162,6 @@
self.assertEqual(resp2_desc, resp3_desc)
self.tenants_client.delete_tenant(t_id)
- self.data.tenants.remove(tenant)
@test.idempotent_id('8fc8981f-f12d-4c66-9972-2bdcf2bc2e1a')
def test_tenant_update_enable(self):
@@ -163,7 +170,9 @@
t_en = False
body = self.tenants_client.create_tenant(name=t_name, enabled=t_en)
tenant = body['tenant']
- self.data.tenants.append(tenant)
+ # Add the tenant to the cleanup list
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.tenants_client.delete_tenant, tenant['id'])
t_id = tenant['id']
resp1_en = tenant['enabled']
@@ -182,4 +191,3 @@
self.assertEqual(resp2_en, resp3_en)
self.tenants_client.delete_tenant(t_id)
- self.data.tenants.remove(tenant)
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index 5cf337b..2f7e941 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -28,13 +28,15 @@
# first:create a tenant
tenant_name = data_utils.rand_name(name='tenant')
tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- self.data.tenants.append(tenant)
+ # Delete the tenant at the end of the test
+ self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
# second:create a user
user = self.users_client.create_user(name=user_name,
password=user_password,
tenantId=tenant['id'],
email='')['user']
- self.data.users.append(user)
+ # Delete the user at the end of the test
+ self.addCleanup(self.users_client.delete_user, user['id'])
# then get a token for the user
body = self.token_client.auth(user_name,
user_password,
@@ -68,23 +70,27 @@
password=user_password,
tenantId=tenant_id,
email=email)['user']
- self.data.users.append(user)
+ # Delete the user at the end of the test
+ self.addCleanup(self.users_client.delete_user, user['id'])
# Create a couple tenants.
tenant1_name = data_utils.rand_name(name='tenant')
tenant1 = self.tenants_client.create_tenant(
name=tenant1_name)['tenant']
- self.data.tenants.append(tenant1)
+ # Delete the tenant at the end of the test
+ self.addCleanup(self.tenants_client.delete_tenant, tenant1['id'])
tenant2_name = data_utils.rand_name(name='tenant')
tenant2 = self.tenants_client.create_tenant(
name=tenant2_name)['tenant']
- self.data.tenants.append(tenant2)
+ # Delete the tenant at the end of the test
+ self.addCleanup(self.tenants_client.delete_tenant, tenant2['id'])
# Create a role
role_name = data_utils.rand_name(name='role')
role = self.roles_client.create_role(name=role_name)['role']
- self.data.roles.append(role)
+ # Delete the role at the end of the test
+ self.addCleanup(self.roles_client.delete_role, role['id'])
# Grant the user the role on the tenants.
self.roles_client.create_user_role_on_project(tenant1['id'],
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index 167cbc7..8e63498 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -19,6 +19,7 @@
from tempest.api.identity import base
from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
from tempest import test
@@ -35,25 +36,27 @@
@test.idempotent_id('2d55a71e-da1d-4b43-9c03-d269fd93d905')
def test_create_user(self):
# Create a user
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
user = self.users_client.create_user(name=self.alt_user,
password=self.alt_password,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email=self.alt_email)['user']
- self.data.users.append(user)
+ # Delete the User at the end of the test
+ self.addCleanup(self.users_client.delete_user, user['id'])
self.assertEqual(self.alt_user, user['name'])
@test.idempotent_id('89d9fdb8-15c2-4304-a429-48715d0af33d')
def test_create_user_with_enabled(self):
# Create a user with enabled : False
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
name = data_utils.rand_name('test_user')
user = self.users_client.create_user(name=name,
password=self.alt_password,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email=self.alt_email,
enabled=False)['user']
- self.data.users.append(user)
+ # Delete the User at the end of the test
+ self.addCleanup(self.users_client.delete_user, user['id'])
self.assertEqual(name, user['name'])
self.assertEqual(False, user['enabled'])
self.assertEqual(self.alt_email, user['email'])
@@ -62,10 +65,10 @@
def test_update_user(self):
# Test case to check if updating of user attributes is successful.
test_user = data_utils.rand_name('test_user')
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
user = self.users_client.create_user(name=test_user,
password=self.alt_password,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email=self.alt_email)['user']
# Delete the User at the end of this method
self.addCleanup(self.users_client.delete_user, user['id'])
@@ -89,76 +92,85 @@
def test_delete_user(self):
# Delete a user
test_user = data_utils.rand_name('test_user')
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
user = self.users_client.create_user(name=test_user,
password=self.alt_password,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email=self.alt_email)['user']
+ # Delete the User at the end of the test
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.users_client.delete_user, user['id'])
self.users_client.delete_user(user['id'])
@test.idempotent_id('aca696c3-d645-4f45-b728-63646045beb1')
def test_user_authentication(self):
# Valid user's token is authenticated
- self.data.setup_test_user()
+ password = data_utils.rand_password()
+ user = self.setup_test_user(password)
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
# Get a token
- self.token_client.auth(self.data.user['name'],
- self.data.user_password,
- self.data.tenant['name'])
+ self.token_client.auth(user['name'],
+ password,
+ tenant['name'])
# Re-auth
- self.token_client.auth(self.data.user['name'],
- self.data.user_password,
- self.data.tenant['name'])
+ self.token_client.auth(user['name'],
+ password,
+ tenant['name'])
@test.idempotent_id('5d1fa498-4c2d-4732-a8fe-2b054598cfdd')
def test_authentication_request_without_token(self):
# Request for token authentication with a valid token in header
- self.data.setup_test_user()
- self.token_client.auth(self.data.user['name'],
- self.data.user_password,
- self.data.tenant['name'])
+ password = data_utils.rand_password()
+ user = self.setup_test_user(password)
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+ self.token_client.auth(user['name'],
+ password,
+ tenant['name'])
# Get the token of the current client
token = self.client.auth_provider.get_token()
# Delete the token from database
self.client.delete_token(token)
# Re-auth
- self.token_client.auth(self.data.user['name'],
- self.data.user_password,
- self.data.tenant['name'])
+ self.token_client.auth(user['name'],
+ password,
+ tenant['name'])
self.client.auth_provider.clear_auth()
@test.idempotent_id('a149c02e-e5e0-4b89-809e-7e8faf33ccda')
def test_get_users(self):
# Get a list of users and find the test user
- self.data.setup_test_user()
+ user = self.setup_test_user()
users = self.users_client.list_users()['users']
self.assertThat([u['name'] for u in users],
- matchers.Contains(self.data.user['name']),
- "Could not find %s" % self.data.user['name'])
+ matchers.Contains(user['name']),
+ "Could not find %s" % user['name'])
@test.idempotent_id('6e317209-383a-4bed-9f10-075b7c82c79a')
def test_list_users_for_tenant(self):
# Return a list of all users for a tenant
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
user_ids = list()
fetched_user_ids = list()
password1 = data_utils.rand_password()
alt_tenant_user1 = data_utils.rand_name('tenant_user1')
user1 = self.users_client.create_user(name=alt_tenant_user1,
password=password1,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email='user1@123')['user']
user_ids.append(user1['id'])
- self.data.users.append(user1)
+ # Delete the User at the end of the test
+ self.addCleanup(self.users_client.delete_user, user1['id'])
password2 = data_utils.rand_password()
alt_tenant_user2 = data_utils.rand_name('tenant_user2')
user2 = self.users_client.create_user(name=alt_tenant_user2,
password=password2,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email='user2@123')['user']
user_ids.append(user2['id'])
- self.data.users.append(user2)
+ # Delete the User at the end of the test
+ self.addCleanup(self.users_client.delete_user, user2['id'])
# List of users for the respective tenant ID
- body = (self.tenants_client.list_tenant_users(self.data.tenant['id'])
+ body = (self.tenants_client.list_tenant_users(tenant['id'])
['users'])
for i in body:
fetched_user_ids.append(i['id'])
@@ -172,11 +184,9 @@
@test.idempotent_id('a8b54974-40e1-41c0-b812-50fc90827971')
def test_list_users_with_roles_for_tenant(self):
# Return list of users on tenant when roles are assigned to users
- self.data.setup_test_user()
- self.data.setup_test_role()
- user = self.get_user_by_name(self.data.user['name'])
- tenant = self.get_tenant_by_name(self.data.tenant['name'])
- role = self.get_role_by_name(self.data.role['name'])
+ user = self.setup_test_user()
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+ role = self.setup_test_role()
# Assigning roles to two users
user_ids = list()
fetched_user_ids = list()
@@ -189,15 +199,15 @@
second_user = self.users_client.create_user(
name=alt_user2,
password=alt_password2,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email='user2@123')['user']
user_ids.append(second_user['id'])
- self.data.users.append(second_user)
+ # Delete the User at the end of the test
+ self.addCleanup(self.users_client.delete_user, second_user['id'])
role = self.roles_client.create_user_role_on_project(
tenant['id'], second_user['id'], role['id'])['role']
# List of users with roles for the respective tenant ID
- body = (self.tenants_client.list_tenant_users(self.data.tenant['id'])
- ['users'])
+ body = (self.tenants_client.list_tenant_users(tenant['id'])['users'])
for i in body:
fetched_user_ids.append(i['id'])
# verifying the user Id in the list
@@ -210,17 +220,18 @@
@test.idempotent_id('1aeb25ac-6ec5-4d8b-97cb-7ac3567a989f')
def test_update_user_password(self):
# Test case to check if updating of user password is successful.
- self.data.setup_test_user()
+ user = self.setup_test_user()
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
# Updating the user with new password
new_pass = data_utils.rand_password()
update_user = self.users_client.update_user_password(
- self.data.user['id'], password=new_pass)['user']
- self.assertEqual(update_user['id'], self.data.user['id'])
+ user['id'], password=new_pass)['user']
+ self.assertEqual(update_user['id'], user['id'])
# NOTE(morganfainberg): Fernet tokens are not subsecond aware and
# Keystone should only be precise to the second. Sleep to ensure
# we are passing the second boundary.
time.sleep(1)
# Validate the updated password through getting a token.
- body = self.token_client.auth(self.data.user['name'], new_pass,
- self.data.tenant['name'])
+ body = self.token_client.auth(user['name'], new_pass,
+ tenant['name'])
self.assertTrue('id' in body['token'])
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
index 78b89fa..597413e 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -32,43 +32,45 @@
@test.idempotent_id('60a1f5fa-5744-4cdf-82bf-60b7de2d29a4')
def test_create_user_by_unauthorized_user(self):
# Non-administrator should not be authorized to create a user
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
self.assertRaises(lib_exc.Forbidden,
self.non_admin_users_client.create_user,
name=self.alt_user, password=self.alt_password,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('d80d0c2f-4514-4d1e-806d-0930dfc5a187')
def test_create_user_with_empty_name(self):
# User with an empty name should not be created
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
name='', password=self.alt_password,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('7704b4f3-3b75-4b82-87cc-931d41c8f780')
def test_create_user_with_name_length_over_255(self):
# Length of user name filed should be restricted to 255 characters
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
name='a' * 256, password=self.alt_password,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('57ae8558-120c-4723-9308-3751474e7ecf')
def test_create_user_with_duplicate_name(self):
# Duplicate user should not be created
- self.data.setup_test_user()
+ password = data_utils.rand_password()
+ user = self.setup_test_user(password)
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
self.assertRaises(lib_exc.Conflict, self.users_client.create_user,
- name=self.data.user['name'],
- password=self.data.user_password,
- tenantId=self.data.tenant['id'],
- email=self.data.user['email'])
+ name=user['name'],
+ password=password,
+ tenantId=tenant['id'],
+ email=user['email'])
@test.attr(type=['negative'])
@test.idempotent_id('0132cc22-7c4f-42e1-9e50-ac6aad31d59a')
@@ -84,7 +86,7 @@
@test.idempotent_id('55bbb103-d1ae-437b-989b-bcdf8175c1f4')
def test_create_user_request_without_a_token(self):
# Request to create a user without a valid token should fail
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
# Get the token of the current client
token = self.client.auth_provider.get_token()
# Delete the token from database
@@ -95,18 +97,18 @@
self.assertRaises(lib_exc.Unauthorized, self.users_client.create_user,
name=self.alt_user, password=self.alt_password,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('23a2f3da-4a1a-41da-abdd-632328a861ad')
def test_create_user_with_enabled_non_bool(self):
# Attempt to create a user with valid enabled para should fail
- self.data.setup_test_tenant()
+ tenant = self.setup_test_tenant()
name = data_utils.rand_name('test_user')
self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
name=name, password=self.alt_password,
- tenantId=self.data.tenant['id'],
+ tenantId=tenant['id'],
email=self.alt_email, enabled=3)
@test.attr(type=['negative'])
@@ -138,7 +140,6 @@
@test.idempotent_id('424868d5-18a7-43e1-8903-a64f95ee3aac')
def test_update_user_by_unauthorized_user(self):
# Non-administrator should not be authorized to update user
- self.data.setup_test_tenant()
self.assertRaises(lib_exc.Forbidden,
self.non_admin_users_client.update_user,
self.alt_user)
@@ -147,10 +148,10 @@
@test.idempotent_id('d45195d5-33ed-41b9-a452-7d0d6a00f6e9')
def test_delete_users_by_unauthorized_user(self):
# Non-administrator user should not be authorized to delete a user
- self.data.setup_test_user()
+ user = self.setup_test_user()
self.assertRaises(lib_exc.Forbidden,
self.non_admin_users_client.delete_user,
- self.data.user['id'])
+ user['id'])
@test.attr(type=['negative'])
@test.idempotent_id('7cc82f7e-9998-4f89-abae-23df36495867')
@@ -179,57 +180,62 @@
@test.idempotent_id('593a4981-f6d4-460a-99a1-57a78bf20829')
def test_authentication_for_disabled_user(self):
# Disabled user's token should not get authenticated
- self.data.setup_test_user()
- self.disable_user(self.data.user['name'])
+ password = data_utils.rand_password()
+ user = self.setup_test_user(password)
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+ self.disable_user(user['name'])
self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
- self.data.user['name'],
- self.data.user_password,
- self.data.tenant['name'])
+ user['name'],
+ password,
+ tenant['name'])
@test.attr(type=['negative'])
@test.idempotent_id('440a7a8d-9328-4b7b-83e0-d717010495e4')
def test_authentication_when_tenant_is_disabled(self):
# User's token for a disabled tenant should not be authenticated
- self.data.setup_test_user()
- self.disable_tenant(self.data.tenant['name'])
+ password = data_utils.rand_password()
+ user = self.setup_test_user(password)
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
+ self.disable_tenant(tenant['name'])
self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
- self.data.user['name'],
- self.data.user_password,
- self.data.tenant['name'])
+ user['name'],
+ password,
+ tenant['name'])
@test.attr(type=['negative'])
@test.idempotent_id('921f1ad6-7907-40b8-853f-637e7ee52178')
def test_authentication_with_invalid_tenant(self):
# User's token for an invalid tenant should not be authenticated
- self.data.setup_test_user()
+ password = data_utils.rand_password()
+ user = self.setup_test_user(password)
self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
- self.data.user['name'],
- self.data.user_password,
+ user['name'],
+ password,
'junktenant1234')
@test.attr(type=['negative'])
@test.idempotent_id('bde9aecd-3b1c-4079-858f-beb5deaa5b5e')
def test_authentication_with_invalid_username(self):
# Non-existent user's token should not get authenticated
- self.data.setup_test_user()
+ password = data_utils.rand_password()
+ user = self.setup_test_user(password)
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
- 'junkuser123', self.data.user_password,
- self.data.tenant['name'])
+ 'junkuser123', password, tenant['name'])
@test.attr(type=['negative'])
@test.idempotent_id('d5308b33-3574-43c3-8d87-1c090c5e1eca')
def test_authentication_with_invalid_password(self):
# User's token with invalid password should not be authenticated
- self.data.setup_test_user()
+ user = self.setup_test_user()
+ tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
- self.data.user['name'], 'junkpass1234',
- self.data.tenant['name'])
+ user['name'], 'junkpass1234', tenant['name'])
@test.attr(type=['negative'])
@test.idempotent_id('284192ce-fb7c-4909-a63b-9a502e0ddd11')
def test_get_users_by_unauthorized_user(self):
# Non-administrator user should not be authorized to get user list
- self.data.setup_test_user()
self.assertRaises(lib_exc.Forbidden,
self.non_admin_users_client.list_users)
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index a3ada21..59fcec6 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -23,14 +23,21 @@
@classmethod
def resource_setup(cls):
super(GroupsV3TestJSON, cls).resource_setup()
- cls.data.setup_test_domain()
+ cls.domain = cls.create_domain()
+
+ @classmethod
+ def resource_cleanup(cls):
+ # Cleanup the domains created in the setup
+ cls.domains_client.update_domain(cls.domain['id'], enabled=False)
+ cls.domains_client.delete_domain(cls.domain['id'])
+ super(GroupsV3TestJSON, cls).resource_cleanup()
@test.idempotent_id('2e80343b-6c81-4ac3-88c7-452f3e9d5129')
def test_group_create_update_get(self):
name = data_utils.rand_name('Group')
description = data_utils.rand_name('Description')
group = self.groups_client.create_group(
- name=name, domain_id=self.data.domain['id'],
+ name=name, domain_id=self.domain['id'],
description=description)['group']
self.addCleanup(self.groups_client.delete_group, group['id'])
self.assertEqual(group['name'], name)
@@ -53,7 +60,7 @@
name = data_utils.rand_name('Group')
old_description = data_utils.rand_name('Description')
group = self.groups_client.create_group(
- name=name, domain_id=self.data.domain['id'],
+ name=name, domain_id=self.domain['id'],
description=old_description)['group']
self.addCleanup(self.groups_client.delete_group, group['id'])
@@ -69,7 +76,7 @@
def test_group_users_add_list_delete(self):
name = data_utils.rand_name('Group')
group = self.groups_client.create_group(
- name=name, domain_id=self.data.domain['id'])['group']
+ name=name, domain_id=self.domain['id'])['group']
self.addCleanup(self.groups_client.delete_group, group['id'])
# add user into group
users = []
@@ -103,7 +110,7 @@
for i in range(2):
name = data_utils.rand_name('Group')
group = self.groups_client.create_group(
- name=name, domain_id=self.data.domain['id'])['group']
+ name=name, domain_id=self.domain['id'])['group']
groups.append(group)
self.addCleanup(self.groups_client.delete_group, group['id'])
self.groups_client.add_group_user(group['id'], user['id'])
@@ -121,7 +128,7 @@
name = data_utils.rand_name('Group')
description = data_utils.rand_name('Description')
group = self.groups_client.create_group(
- name=name, domain_id=self.data.domain['id'],
+ name=name, domain_id=self.domain['id'],
description=description)['group']
self.addCleanup(self.groups_client.delete_group, group['id'])
group_ids.append(group['id'])
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 86f6b12..7d9e41b 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -24,26 +24,38 @@
def resource_setup(cls):
super(ListProjectsTestJSON, cls).resource_setup()
cls.project_ids = list()
- cls.data.setup_test_domain()
+ # Create a domain
+ cls.domain = cls.create_domain()
# Create project with domain
+ cls.projects = list()
cls.p1_name = data_utils.rand_name('project')
cls.p1 = cls.projects_client.create_project(
cls.p1_name, enabled=False,
- domain_id=cls.data.domain['id'])['project']
- cls.data.projects.append(cls.p1)
+ domain_id=cls.domain['id'])['project']
+ cls.projects.append(cls.p1)
cls.project_ids.append(cls.p1['id'])
# Create default project
p2_name = data_utils.rand_name('project')
cls.p2 = cls.projects_client.create_project(p2_name)['project']
- cls.data.projects.append(cls.p2)
+ cls.projects.append(cls.p2)
cls.project_ids.append(cls.p2['id'])
# Create a new project (p3) using p2 as parent project
p3_name = data_utils.rand_name('project')
cls.p3 = cls.projects_client.create_project(
p3_name, parent_id=cls.p2['id'])['project']
- cls.data.projects.append(cls.p3)
+ cls.projects.append(cls.p3)
cls.project_ids.append(cls.p3['id'])
+ @classmethod
+ def resource_cleanup(cls):
+ # Cleanup the projects created during setup in inverse order
+ for project in reversed(cls.projects):
+ cls.projects_client.delete_project(project['id'])
+ # Cleanup the domain created during setup
+ cls.domains_client.update_domain(cls.domain['id'], enabled=False)
+ cls.domains_client.delete_domain(cls.domain['id'])
+ super(ListProjectsTestJSON, cls).resource_cleanup()
+
@test.idempotent_id('1d830662-22ad-427c-8c3e-4ec854b0af44')
def test_list_projects(self):
# List projects
@@ -57,7 +69,7 @@
def test_list_projects_with_domains(self):
# List projects with domain
self._list_projects_with_params(
- {'domain_id': self.data.domain['id']}, 'domain_id')
+ {'domain_id': self.domain['id']}, 'domain_id')
@test.idempotent_id('0fe7a334-675a-4509-b00e-1c4b95d5dae8')
def test_list_projects_with_enabled(self):
diff --git a/tempest/api/identity/admin/v3/test_list_users.py b/tempest/api/identity/admin/v3/test_list_users.py
index 5b27ab1..9691ee8 100644
--- a/tempest/api/identity/admin/v3/test_list_users.py
+++ b/tempest/api/identity/admin/v3/test_list_users.py
@@ -36,24 +36,36 @@
alt_user = data_utils.rand_name('test_user')
alt_password = data_utils.rand_password()
cls.alt_email = alt_user + '@testmail.tm'
- cls.data.setup_test_domain()
+ # Create a domain
+ cls.domain = cls.create_domain()
# Create user with Domain
+ cls.users = list()
u1_name = data_utils.rand_name('test_user')
cls.domain_enabled_user = cls.users_client.create_user(
u1_name, password=alt_password,
- email=cls.alt_email, domain_id=cls.data.domain['id'])['user']
- cls.data.users.append(cls.domain_enabled_user)
+ email=cls.alt_email, domain_id=cls.domain['id'])['user']
+ cls.users.append(cls.domain_enabled_user)
# Create default not enabled user
u2_name = data_utils.rand_name('test_user')
cls.non_domain_enabled_user = cls.users_client.create_user(
u2_name, password=alt_password,
email=cls.alt_email, enabled=False)['user']
- cls.data.users.append(cls.non_domain_enabled_user)
+ cls.users.append(cls.non_domain_enabled_user)
+
+ @classmethod
+ def resource_cleanup(cls):
+ # Cleanup the users created during setup
+ for user in cls.users:
+ cls.users_client.delete_user(user['id'])
+ # Cleanup the domain created during setup
+ cls.domains_client.update_domain(cls.domain['id'], enabled=False)
+ cls.domains_client.delete_domain(cls.domain['id'])
+ super(UsersV3TestJSON, cls).resource_cleanup()
@test.idempotent_id('08f9aabb-dcfe-41d0-8172-82b5fa0bd73d')
def test_list_user_domains(self):
# List users with domain
- params = {'domain_id': self.data.domain['id']}
+ params = {'domain_id': self.domain['id']}
self._list_users_with_params(params, 'domain_id',
self.domain_enabled_user,
self.non_domain_enabled_user)
@@ -79,7 +91,7 @@
# List users
body = self.users_client.list_users()['users']
fetched_ids = [u['id'] for u in body]
- missing_users = [u['id'] for u in self.data.users
+ missing_users = [u['id'] for u in self.users
if u['id'] not in fetched_ids]
self.assertEqual(0, len(missing_users),
"Failed to find user %s in fetched list" %
@@ -88,8 +100,8 @@
@test.idempotent_id('b4baa3ae-ac00-4b4e-9e27-80deaad7771f')
def test_get_user(self):
# Get a user detail
- user = self.users_client.show_user(self.data.users[0]['id'])['user']
- self.assertEqual(self.data.users[0]['id'], user['id'])
- self.assertEqual(self.data.users[0]['name'], user['name'])
+ user = self.users_client.show_user(self.users[0]['id'])['user']
+ self.assertEqual(self.users[0]['id'], user['id'])
+ self.assertEqual(self.users[0]['name'], user['name'])
self.assertEqual(self.alt_email, user['email'])
- self.assertEqual(self.data.domain['id'], user['domain_id'])
+ self.assertEqual(self.domain['id'], user['domain_id'])
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index bee77df..60bb314 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -32,7 +32,7 @@
project_desc = data_utils.rand_name('desc')
project = self.projects_client.create_project(
project_name, description=project_desc)['project']
- self.data.projects.append(project)
+ self.addCleanup(self.projects_client.delete_project, project['id'])
project_id = project['id']
desc1 = project['description']
self.assertEqual(desc1, project_desc, 'Description should have '
@@ -45,25 +45,25 @@
@test.idempotent_id('5f50fe07-8166-430b-a882-3b2ee0abe26f')
def test_project_create_with_domain(self):
# Create project with a domain
- self.data.setup_test_domain()
+ domain = self.setup_test_domain()
project_name = data_utils.rand_name('project')
project = self.projects_client.create_project(
- project_name, domain_id=self.data.domain['id'])['project']
- self.data.projects.append(project)
+ project_name, domain_id=domain['id'])['project']
+ self.addCleanup(self.projects_client.delete_project, project['id'])
project_id = project['id']
self.assertEqual(project_name, project['name'])
- self.assertEqual(self.data.domain['id'], project['domain_id'])
+ self.assertEqual(domain['id'], project['domain_id'])
body = self.projects_client.show_project(project_id)['project']
self.assertEqual(project_name, body['name'])
- self.assertEqual(self.data.domain['id'], body['domain_id'])
+ self.assertEqual(domain['id'], body['domain_id'])
@testtools.skipUnless(CONF.identity_feature_enabled.reseller,
'Reseller not available.')
@test.idempotent_id('1854f9c0-70bc-4d11-a08a-1c789d339e3d')
def test_project_create_with_parent(self):
# Create root project without providing a parent_id
- self.data.setup_test_domain()
- domain_id = self.data.domain['id']
+ domain = self.setup_test_domain()
+ domain_id = domain['id']
root_project_name = data_utils.rand_name('root_project')
root_project = self.projects_client.create_project(
@@ -94,7 +94,7 @@
project_name = data_utils.rand_name('project')
project = self.projects_client.create_project(
project_name, enabled=True)['project']
- self.data.projects.append(project)
+ self.addCleanup(self.projects_client.delete_project, project['id'])
project_id = project['id']
en1 = project['enabled']
self.assertTrue(en1, 'Enable should be True in response')
@@ -108,7 +108,7 @@
project_name = data_utils.rand_name('project')
project = self.projects_client.create_project(
project_name, enabled=False)['project']
- self.data.projects.append(project)
+ self.addCleanup(self.projects_client.delete_project, project['id'])
en1 = project['enabled']
self.assertEqual('false', str(en1).lower(),
'Enable should be False in response')
@@ -122,7 +122,7 @@
# Update name attribute of a project
p_name1 = data_utils.rand_name('project')
project = self.projects_client.create_project(p_name1)['project']
- self.data.projects.append(project)
+ self.addCleanup(self.projects_client.delete_project, project['id'])
resp1_name = project['name']
@@ -146,7 +146,7 @@
p_desc = data_utils.rand_name('desc')
project = self.projects_client.create_project(
p_name, description=p_desc)['project']
- self.data.projects.append(project)
+ self.addCleanup(self.projects_client.delete_project, project['id'])
resp1_desc = project['description']
p_desc2 = data_utils.rand_name('desc2')
@@ -169,7 +169,7 @@
p_en = False
project = self.projects_client.create_project(p_name,
enabled=p_en)['project']
- self.data.projects.append(project)
+ self.addCleanup(self.projects_client.delete_project, project['id'])
resp1_en = project['enabled']
@@ -192,7 +192,7 @@
# Create a Project
p_name = data_utils.rand_name('project')
project = self.projects_client.create_project(p_name)['project']
- self.data.projects.append(project)
+ self.addCleanup(self.projects_client.delete_project, project['id'])
# Create a User
u_name = data_utils.rand_name('user')
diff --git a/tempest/api/identity/admin/v3/test_projects_negative.py b/tempest/api/identity/admin/v3/test_projects_negative.py
index fb4a8cf..e661f42 100644
--- a/tempest/api/identity/admin/v3/test_projects_negative.py
+++ b/tempest/api/identity/admin/v3/test_projects_negative.py
@@ -34,7 +34,7 @@
# Project names should be unique
project_name = data_utils.rand_name('project-dup')
project = self.projects_client.create_project(project_name)['project']
- self.data.projects.append(project)
+ self.addCleanup(self.projects_client.delete_project, project['id'])
self.assertRaises(lib_exc.Conflict,
self.projects_client.create_project, project_name)
@@ -69,7 +69,7 @@
# Non-admin user should not be able to delete a project
project_name = data_utils.rand_name('project')
project = self.projects_client.create_project(project_name)['project']
- self.data.projects.append(project)
+ self.addCleanup(self.projects_client.delete_project, project['id'])
self.assertRaises(
lib_exc.Forbidden, self.non_admin_projects_client.delete_project,
project['id'])
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index 12ef369..2b77023 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -23,10 +23,11 @@
@classmethod
def resource_setup(cls):
super(RolesV3TestJSON, cls).resource_setup()
+ cls.roles = list()
for _ in range(3):
role_name = data_utils.rand_name(name='role')
role = cls.roles_client.create_role(name=role_name)['role']
- cls.data.roles.append(role)
+ cls.roles.append(role)
cls.fetched_role_ids = list()
u_name = data_utils.rand_name('user')
u_desc = '%s description' % u_name
@@ -59,6 +60,8 @@
# before deleting,or else it would result in unauthorized error
cls.domains_client.update_domain(cls.domain['id'], enabled=False)
cls.domains_client.delete_domain(cls.domain['id'])
+ for role in cls.roles:
+ cls.roles_client.delete_role(role['id'])
super(RolesV3TestJSON, cls).resource_cleanup()
def _list_assertions(self, body, fetched_role_ids, role_id):
@@ -189,5 +192,5 @@
def test_list_roles(self):
# Return a list of all roles
body = self.roles_client.list_roles()['roles']
- found = [role for role in body if role in self.data.roles]
- self.assertEqual(len(found), len(self.data.roles))
+ found = [role for role in body if role in self.roles]
+ self.assertEqual(len(found), len(self.roles))
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index 371da9c..f200095 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -149,6 +149,6 @@
@test.idempotent_id('c10dcd90-461d-4b16-8e23-4eb836c00644')
def test_get_user(self):
# Get a user detail
- self.data.setup_test_user()
- user = self.users_client.show_user(self.data.user['id'])['user']
- self.assertEqual(self.data.user['id'], user['id'])
+ user = self.setup_test_user()
+ fetched_user = self.users_client.show_user(user['id'])['user']
+ self.assertEqual(user['id'], fetched_user['id'])
diff --git a/tempest/api/identity/admin/v3/test_users_negative.py b/tempest/api/identity/admin/v3/test_users_negative.py
index 1375db1..71e8bc5 100644
--- a/tempest/api/identity/admin/v3/test_users_negative.py
+++ b/tempest/api/identity/admin/v3/test_users_negative.py
@@ -37,9 +37,10 @@
@test.idempotent_id('b3c9fccc-4134-46f5-b600-1da6fb0a3b1f')
def test_authentication_for_disabled_user(self):
# Attempt to authenticate for disabled user should fail
- self.data.setup_test_user()
- self.disable_user(self.data.user['name'], self.data.user['domain_id'])
+ password = data_utils.rand_password()
+ user = self.setup_test_user(password)
+ self.disable_user(user['name'], user['domain_id'])
self.assertRaises(lib_exc.Unauthorized, self.token.auth,
- username=self.data.user['name'],
- password=self.data.user_password,
+ username=user['name'],
+ password=password,
user_domain_id='default')
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index df39390..ce052e6 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -17,7 +17,6 @@
from tempest.common.utils import data_utils
from tempest import config
-from tempest.lib.common.utils import test_utils
import tempest.test
CONF = config.CONF
@@ -64,6 +63,23 @@
if len(role) > 0:
return role[0]
+ def _create_test_user(self, **kwargs):
+ if kwargs['password'] is None:
+ user_password = data_utils.rand_password()
+ kwargs['password'] = user_password
+ user = self.users_client.create_user(**kwargs)['user']
+ # Delete the user at the end of the test
+ self.addCleanup(self.users_client.delete_user, user['id'])
+ return user
+
+ def setup_test_role(self):
+ """Set up a test role."""
+ role = self.roles_client.create_role(
+ name=data_utils.rand_name('test_role'))['role']
+ # Delete the role at the end of the test
+ self.addCleanup(self.roles_client.delete_role, role['id'])
+ return role
+
class BaseIdentityV2Test(BaseIdentityTest):
@@ -104,14 +120,30 @@
@classmethod
def resource_setup(cls):
super(BaseIdentityV2AdminTest, cls).resource_setup()
- cls.data = DataGeneratorV2(cls.tenants_client, cls.users_client,
- cls.roles_client)
+ cls.projects_client = cls.tenants_client
@classmethod
def resource_cleanup(cls):
- cls.data.teardown_all()
super(BaseIdentityV2AdminTest, cls).resource_cleanup()
+ def setup_test_user(self, password=None):
+ """Set up a test user."""
+ tenant = self.setup_test_tenant()
+ username = data_utils.rand_name('test_user')
+ email = username + '@testmail.tm'
+ user = self._create_test_user(name=username, email=email,
+ tenantId=tenant['id'], password=password)
+ return user
+
+ def setup_test_tenant(self):
+ """Set up a test tenant."""
+ tenant = self.projects_client.create_tenant(
+ name=data_utils.rand_name('test_tenant'),
+ description=data_utils.rand_name('desc'))['tenant']
+ # Delete the tenant at the end of the test
+ self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+ return tenant
+
class BaseIdentityV3Test(BaseIdentityTest):
@@ -160,12 +192,9 @@
@classmethod
def resource_setup(cls):
super(BaseIdentityV3AdminTest, cls).resource_setup()
- cls.data = DataGeneratorV3(cls.projects_client, cls.users_client,
- cls.roles_client, cls.domains_client)
@classmethod
def resource_cleanup(cls):
- cls.data.teardown_all()
super(BaseIdentityV3AdminTest, cls).resource_cleanup()
@classmethod
@@ -173,106 +202,42 @@
user = cls.get_user_by_name(user_name, domain_id)
cls.users_client.update_user(user['id'], user_name, enabled=False)
+ @classmethod
+ def create_domain(cls):
+ """Create a domain."""
+ domain = cls.domains_client.create_domain(
+ name=data_utils.rand_name('test_domain'),
+ description=data_utils.rand_name('desc'))['domain']
+ return domain
+
def delete_domain(self, domain_id):
# NOTE(mpavlase) It is necessary to disable the domain before deleting
# otherwise it raises Forbidden exception
self.domains_client.update_domain(domain_id, enabled=False)
self.domains_client.delete_domain(domain_id)
-
-class BaseDataGenerator(object):
-
- def __init__(self, projects_client, users_client, roles_client,
- domains_client=None):
- self.projects_client = projects_client
- self.users_client = users_client
- self.roles_client = roles_client
- self.domains_client = domains_client
-
- self.user_password = None
- self.user = None
- self.tenant = None
- self.project = None
- self.role = None
- self.domain = None
-
- self.users = []
- self.tenants = []
- self.projects = []
- self.roles = []
- self.domains = []
-
- def _create_test_user(self, **kwargs):
- self.user_password = data_utils.rand_password()
- self.user = self.users_client.create_user(
- password=self.user_password,
- **kwargs)['user']
- self.users.append(self.user)
-
- def setup_test_role(self):
- """Set up a test role."""
- self.role = self.roles_client.create_role(
- name=data_utils.rand_name('test_role'))['role']
- self.roles.append(self.role)
-
- def teardown_all(self):
- for user in self.users:
- test_utils.call_and_ignore_notfound_exc(
- self.users_client.delete_user, user['id'])
- for tenant in self.tenants:
- test_utils.call_and_ignore_notfound_exc(
- self.projects_client.delete_tenant, tenant['id'])
- for project in reversed(self.projects):
- test_utils.call_and_ignore_notfound_exc(
- self.projects_client.delete_project, project['id'])
- for role in self.roles:
- test_utils.call_and_ignore_notfound_exc(
- self.roles_client.delete_role, role['id'])
- for domain in self.domains:
- test_utils.call_and_ignore_notfound_exc(
- self.domains_client.update_domain, domain['id'], enabled=False)
- test_utils.call_and_ignore_notfound_exc(
- self.domains_client.delete_domain, domain['id'])
-
-
-class DataGeneratorV2(BaseDataGenerator):
-
- def setup_test_user(self):
+ def setup_test_user(self, password=None):
"""Set up a test user."""
- self.setup_test_tenant()
+ project = self.setup_test_project()
username = data_utils.rand_name('test_user')
email = username + '@testmail.tm'
- self._create_test_user(name=username, email=email,
- tenantId=self.tenant['id'])
-
- def setup_test_tenant(self):
- """Set up a test tenant."""
- self.tenant = self.projects_client.create_tenant(
- name=data_utils.rand_name('test_tenant'),
- description=data_utils.rand_name('desc'))['tenant']
- self.tenants.append(self.tenant)
-
-
-class DataGeneratorV3(BaseDataGenerator):
-
- def setup_test_user(self):
- """Set up a test user."""
- self.setup_test_project()
- username = data_utils.rand_name('test_user')
- email = username + '@testmail.tm'
- self._create_test_user(user_name=username, email=email,
- project_id=self.project['id'])
+ user = self._create_test_user(user_name=username, email=email,
+ project_id=project['id'],
+ password=password)
+ return user
def setup_test_project(self):
"""Set up a test project."""
- self.project = self.projects_client.create_project(
+ project = self.projects_client.create_project(
name=data_utils.rand_name('test_project'),
description=data_utils.rand_name('desc'))['project']
- self.projects.append(self.project)
+ # Delete the project at the end of the test
+ self.addCleanup(self.projects_client.delete_project, project['id'])
+ return project
def setup_test_domain(self):
"""Set up a test domain."""
- self.domain = self.domains_client.create_domain(
- name=data_utils.rand_name('test_domain'),
- description=data_utils.rand_name('desc'))['domain']
- self.domains.append(self.domain)
+ domain = self.create_domain()
+ # Delete the domain at the end of the test
+ self.addCleanup(self.delete_domain, domain['id'])
+ return domain
diff --git a/tempest/api/orchestration/stacks/test_soft_conf.py b/tempest/api/orchestration/stacks/test_soft_conf.py
index 6a4e2b9..aa0b46a 100644
--- a/tempest/api/orchestration/stacks/test_soft_conf.py
+++ b/tempest/api/orchestration/stacks/test_soft_conf.py
@@ -45,7 +45,7 @@
def _validate_config(self, configuration, api_config):
# Assert all expected keys are present with matching data
- for k in configuration.keys():
+ for k in configuration:
self.assertEqual(configuration[k],
api_config['software_config'][k])
diff --git a/tempest/clients.py b/tempest/clients.py
index ef03e80..fd010f2 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -21,6 +21,7 @@
from tempest import config
from tempest import exceptions
from tempest.lib import auth
+from tempest.lib import exceptions as lib_exc
from tempest.lib.services import compute
from tempest.lib.services import image
from tempest.lib.services import network
@@ -39,15 +40,10 @@
class Manager(service_clients.ServiceClients):
"""Top level manager for OpenStack tempest clients"""
- default_params = {
- 'disable_ssl_certificate_validation':
- CONF.identity.disable_ssl_certificate_validation,
- 'ca_certs': CONF.identity.ca_certificates_file,
- 'trace_requests': CONF.debug.trace_requests
- }
+ default_params = config.service_client_config()
- # NOTE: Tempest uses timeout values of compute API if project specific
- # timeout values don't exist.
+ # TODO(andreaf) This is only used by data_processing and baremetal clients,
+ # and should be removed once they are out of Tempest
default_params_with_timeout_values = {
'build_interval': CONF.compute.build_interval,
'build_timeout': CONF.compute.build_timeout
@@ -65,7 +61,11 @@
_, identity_uri = get_auth_provider_class(credentials)
super(Manager, self).__init__(
credentials=credentials, identity_uri=identity_uri, scope=scope,
- region=CONF.identity.region, **self.default_params)
+ region=CONF.identity.region,
+ client_parameters=self._prepare_configuration())
+ # TODO(andreaf) When clients are initialised without the right
+ # parameters available, the calls below will trigger a KeyError.
+ # We should catch that and raise a better error.
self._set_compute_clients()
self._set_identity_clients()
self._set_volume_clients()
@@ -96,15 +96,38 @@
self.negative_client = negative_rest_client.NegativeRestClient(
self.auth_provider, service, **self.default_params)
+ def _prepare_configuration(self):
+ """Map values from CONF into Manager parameters
+
+ This uses `config.service_client_config` for all services to collect
+ most configuration items needed to init the clients.
+ """
+ # NOTE(andreaf) Configuration items will be passed in future patches
+ # into ClientFactory objects, but for now we update all the
+ # _set_*_client methods to consume them so we can verify that the
+ # configuration collected is correct
+
+ configuration = {}
+
+ # Setup the parameters for all Tempest services.
+ # NOTE(andreaf) Since client.py is an internal module of Tempest,
+ # it doesn't have to consider plugin configuration.
+ for service in service_clients.tempest_modules():
+ try:
+ # NOTE(andreaf) Use the unversioned service name to fetch
+ # the configuration since configuration is not versioned.
+ service_for_config = service.split('.')[0]
+ if service_for_config not in configuration:
+ configuration[service_for_config] = (
+ config.service_client_config(service_for_config))
+ except lib_exc.UnknownServiceClient:
+ LOG.warn(
+ 'Could not load configuration for service %s' % service)
+
+ return configuration
+
def _set_network_clients(self):
- params = {
- 'service': CONF.network.catalog_type,
- 'region': CONF.network.region or CONF.identity.region,
- 'endpoint_type': CONF.network.endpoint_type,
- 'build_interval': CONF.network.build_interval,
- 'build_timeout': CONF.network.build_timeout
- }
- params.update(self.default_params)
+ params = self.parameters['network']
self.network_agents_client = network.AgentsClient(
self.auth_provider, **params)
self.network_extensions_client = network.ExtensionsClient(
@@ -135,20 +158,13 @@
self.auth_provider, **params)
def _set_image_clients(self):
- params = {
- 'service': CONF.image.catalog_type,
- 'region': CONF.image.region or CONF.identity.region,
- 'endpoint_type': CONF.image.endpoint_type,
- 'build_interval': CONF.image.build_interval,
- 'build_timeout': CONF.image.build_timeout
- }
- params.update(self.default_params)
-
if CONF.service_available.glance:
+ params = self.parameters['image']
self.image_client = image.v1.ImagesClient(
self.auth_provider, **params)
self.image_member_client = image.v1.ImageMembersClient(
self.auth_provider, **params)
+
self.image_client_v2 = image.v2.ImagesClient(
self.auth_provider, **params)
self.image_member_client_v2 = image.v2.ImageMembersClient(
@@ -161,14 +177,7 @@
self.auth_provider, **params)
def _set_compute_clients(self):
- params = {
- 'service': CONF.compute.catalog_type,
- 'region': CONF.compute.region or CONF.identity.region,
- 'endpoint_type': CONF.compute.endpoint_type,
- 'build_interval': CONF.compute.build_interval,
- 'build_timeout': CONF.compute.build_timeout
- }
- params.update(self.default_params)
+ params = self.parameters['compute']
self.agents_client = compute.AgentsClient(self.auth_provider, **params)
self.compute_networks_client = compute.NetworksClient(
@@ -234,10 +243,11 @@
# NOTE: The following client needs special timeout values because
# the API is a proxy for the other component.
params_volume = copy.deepcopy(params)
- params_volume.update({
- 'build_interval': CONF.volume.build_interval,
- 'build_timeout': CONF.volume.build_timeout
- })
+ # Optional parameters
+ for _key in ('build_interval', 'build_timeout'):
+ _value = self.parameters['volume'].get(_key)
+ if _value:
+ params_volume[_key] = _value
self.volumes_extensions_client = compute.VolumesClient(
self.auth_provider, **params_volume)
self.compute_versions_client = compute.VersionsClient(
@@ -246,14 +256,10 @@
self.auth_provider, **params_volume)
def _set_identity_clients(self):
- params = {
- 'service': CONF.identity.catalog_type,
- 'region': CONF.identity.region
- }
- params.update(self.default_params_with_timeout_values)
+ params = self.parameters['identity']
# Clients below use the admin endpoint type of Keystone API v2
- params_v2_admin = params.copy()
+ params_v2_admin = copy.copy(params)
params_v2_admin['endpoint_type'] = CONF.identity.v2_admin_endpoint_type
self.endpoints_client = identity.v2.EndpointsClient(self.auth_provider,
**params_v2_admin)
@@ -269,7 +275,7 @@
self.auth_provider, **params_v2_admin)
# Clients below use the public endpoint type of Keystone API v2
- params_v2_public = params.copy()
+ params_v2_public = copy.copy(params)
params_v2_public['endpoint_type'] = (
CONF.identity.v2_public_endpoint_type)
self.identity_public_client = identity.v2.IdentityClient(
@@ -279,8 +285,9 @@
self.users_public_client = identity.v2.UsersClient(
self.auth_provider, **params_v2_public)
- # Clients below use the endpoint type of Keystone API v3
- params_v3 = params.copy()
+ # Clients below use the endpoint type of Keystone API v3, which is set
+ # in endpoint_type
+ params_v3 = copy.copy(params)
params_v3['endpoint_type'] = CONF.identity.v3_endpoint_type
self.domains_client = identity.v3.DomainsClient(self.auth_provider,
**params_v3)
@@ -326,14 +333,8 @@
raise exceptions.InvalidConfiguration(msg)
def _set_volume_clients(self):
- params = {
- 'service': CONF.volume.catalog_type,
- 'region': CONF.volume.region or CONF.identity.region,
- 'endpoint_type': CONF.volume.endpoint_type,
- 'build_interval': CONF.volume.build_interval,
- 'build_timeout': CONF.volume.build_timeout
- }
- params.update(self.default_params)
+ # Mandatory parameters (always defined)
+ params = self.parameters['volume']
self.volume_qos_client = volume.v1.QosSpecsClient(self.auth_provider,
**params)
@@ -381,12 +382,8 @@
volume.v2.AvailabilityZoneClient(self.auth_provider, **params)
def _set_object_storage_clients(self):
- params = {
- 'service': CONF.object_storage.catalog_type,
- 'region': CONF.object_storage.region or CONF.identity.region,
- 'endpoint_type': CONF.object_storage.endpoint_type
- }
- params.update(self.default_params_with_timeout_values)
+ # Mandatory parameters (always defined)
+ params = self.parameters['object-storage']
self.account_client = object_storage.AccountClient(self.auth_provider,
**params)
@@ -404,12 +401,8 @@
def get_auth_provider(credentials, pre_auth=False, scope='project'):
- default_params = {
- 'disable_ssl_certificate_validation':
- CONF.identity.disable_ssl_certificate_validation,
- 'ca_certs': CONF.identity.ca_certificates_file,
- 'trace_requests': CONF.debug.trace_requests
- }
+ # kwargs for auth provider match the common ones used by service clients
+ default_params = config.service_client_config()
if credentials is None:
raise exceptions.InvalidCredentials(
'Credentials must be specified')
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index 80de6f5..af86fe3 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -106,7 +106,7 @@
self._load_json()
def _cleanup(self):
- print ("Begin cleanup")
+ print("Begin cleanup")
is_dry_run = self.options.dry_run
is_preserve = not self.options.delete_tempest_conf_objects
is_save_state = False
@@ -124,7 +124,7 @@
'is_save_state': is_save_state}
tenant_service = cleanup_service.TenantService(admin_mgr, **kwargs)
tenants = tenant_service.list()
- print ("Process %s tenants" % len(tenants))
+ print("Process %s tenants" % len(tenants))
# Loop through list of tenants and clean them up.
for tenant in tenants:
@@ -155,7 +155,7 @@
self._remove_admin_role(tenant_id)
def _clean_tenant(self, tenant):
- print ("Cleaning tenant: %s " % tenant['name'])
+ print("Cleaning tenant: %s " % tenant['name'])
is_dry_run = self.options.dry_run
dry_run_data = self.dry_run_data
is_preserve = not self.options.delete_tempest_conf_objects
@@ -266,7 +266,7 @@
return False
def _init_state(self):
- print ("Initializing saved state.")
+ print("Initializing saved state.")
data = {}
admin_mgr = self.admin_mgr
kwargs = {'data': data,
diff --git a/tempest/cmd/list_plugins.py b/tempest/cmd/list_plugins.py
index 5f6b3e6..36e45a5 100644
--- a/tempest/cmd/list_plugins.py
+++ b/tempest/cmd/list_plugins.py
@@ -19,13 +19,10 @@
"""
from cliff import command
-from oslo_log import log as logging
import prettytable
from tempest.test_discover.plugins import TempestTestPluginManager
-LOG = logging.getLogger(__name__)
-
class TempestListPlugins(command.Command):
def take_action(self, parsed_args):
diff --git a/tempest/cmd/main.py b/tempest/cmd/main.py
index acd97a8..641d11c 100644
--- a/tempest/cmd/main.py
+++ b/tempest/cmd/main.py
@@ -26,7 +26,7 @@
def __init__(self):
super(Main, self).__init__(
description='Tempest cli application',
- version=version.VersionInfo('tempest').version_string(),
+ version=version.VersionInfo('tempest').version_string_with_vcs(),
command_manager=commandmanager.CommandManager('tempest.cm'),
deferred_help=True,
)
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 5270cac..1c0d9c4 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -25,7 +25,7 @@
There are also the **--blacklist_file** and **--whitelist_file** options that
let you pass a filepath to tempest run with the file format being a line
-seperated regex, with '#' used to signify the start of a comment on a line.
+separated regex, with '#' used to signify the start of a comment on a line.
For example::
# Regex file
@@ -88,7 +88,6 @@
from cliff import command
from os_testr import regex_builder
from os_testr import subunit_trace
-from oslo_log import log as logging
from testrepository.commands import run_argv
from tempest.cmd import init
@@ -96,7 +95,6 @@
from tempest import config
-LOG = logging.getLogger(__name__)
CONF = config.CONF
@@ -195,7 +193,7 @@
list_selector = parser.add_mutually_exclusive_group()
list_selector.add_argument('--whitelist_file',
help="Path to a whitelist file, this file "
- "contains a seperate regex on each "
+ "contains a separate regex on each "
"newline.")
list_selector.add_argument('--blacklist_file',
help='Path to a blacklist file, this file '
@@ -205,7 +203,7 @@
parser.add_argument('--list-tests', '-l', action='store_true',
help='List tests',
default=False)
- # exectution args
+ # execution args
parser.add_argument('--concurrency', '-w',
help="The number of workers to use, defaults to "
"the number of cpus")
diff --git a/tempest/cmd/workspace.py b/tempest/cmd/workspace.py
index cc82284..b36cf4e 100644
--- a/tempest/cmd/workspace.py
+++ b/tempest/cmd/workspace.py
@@ -53,13 +53,11 @@
from cliff import command
from oslo_concurrency import lockutils
-from oslo_log import log as logging
import prettytable
import yaml
from tempest import config
-LOG = logging.getLogger(__name__)
CONF = config.CONF
@@ -185,25 +183,35 @@
subparsers = parser.add_subparsers()
- list_parser = subparsers.add_parser('list')
+ list_parser = subparsers.add_parser(
+ 'list', help='Outputs the name and path of all known tempest '
+ 'workspaces')
list_parser.set_defaults(list=True)
- register_parser = subparsers.add_parser('register')
+ register_parser = subparsers.add_parser(
+ 'register', help='Registers a new tempest workspace via a given '
+ '--name and --path')
register_parser.add_argument('--name', required=True)
register_parser.add_argument('--path', required=True)
register_parser.set_defaults(register=True)
- update_parser = subparsers.add_parser('rename')
+ update_parser = subparsers.add_parser(
+ 'rename', help='Renames a tempest workspace from --old-name to '
+ '--new-name')
update_parser.add_argument('--old-name', required=True)
update_parser.add_argument('--new-name', required=True)
update_parser.set_defaults(rename=True)
- move_parser = subparsers.add_parser('move')
+ move_parser = subparsers.add_parser(
+ 'move', help='Changes the path of a given tempest workspace '
+ '--name to --path')
move_parser.add_argument('--name', required=True)
move_parser.add_argument('--path', required=True)
move_parser.set_defaults(move=True)
- remove_parser = subparsers.add_parser('remove')
+ remove_parser = subparsers.add_parser(
+ 'remove', help='Deletes the entry for a given tempest workspace '
+ '--name')
remove_parser.add_argument('--name', required=True)
remove_parser.set_defaults(remove=True)
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 7ebc283..a2edcdc 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -30,7 +30,8 @@
def create_test_server(clients, validatable=False, validation_resources=None,
tenant_network=None, wait_until=None,
volume_backed=False, name=None, flavor=None,
- image_id=None, **kwargs):
+ image_id=None, delete_vol_on_termination=True,
+ **kwargs):
"""Common wrapper utility returning a test server.
This method is a common wrapper returning a test server that can be
@@ -39,11 +40,20 @@
:param clients: Client manager which provides OpenStack Tempest clients.
:param validatable: Whether the server will be pingable or sshable.
:param validation_resources: Resources created for the connection to the
- server. Include a keypair, a security group and an IP.
+ server. Include a keypair, a security group and an IP.
:param tenant_network: Tenant network to be used for creating a server.
:param wait_until: Server status to wait for the server to reach after
- its creation.
+ its creation.
:param volume_backed: Whether the instance is volume backed or not.
+ :param name: Name of the server to be provisioned. If not defined a random
+ string ending with '-instance' will be generated.
+ :param flavor: Flavor of the server to be provisioned. If not defined,
+ CONF.compute.flavor_ref will be used instead.
+ :param image_id: ID of the image to be used to provision the server. If not
+ defined, CONF.compute.image_ref will be used instead.
+ :param delete_vol_on_termination: Controls whether the backing volume
+ should be deleted when the server is deleted. Only applies to volume
+ backed servers.
:returns: a tuple
"""
@@ -106,7 +116,7 @@
'source_type': 'volume',
'destination_type': 'volume',
'boot_index': 0,
- 'delete_on_termination': True}]
+ 'delete_on_termination': delete_vol_on_termination}]
kwargs['block_device_mapping_v2'] = bd_map_v2
# Since this is boot from volume an image does not need
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index bf6c537..1b450ab 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -94,7 +94,7 @@
self.router)
def set_resources(self, **kwargs):
- for key in kwargs.keys():
+ for key in kwargs:
if hasattr(self, key):
setattr(self, key, kwargs[key])
diff --git a/tempest/config.py b/tempest/config.py
index 8ec8b24..0c2b913 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -25,6 +25,7 @@
from oslo_log import log as logging
import testtools
+from tempest.lib import exceptions
from tempest.test_discover import plugins
@@ -371,7 +372,7 @@
default=True,
help='Does the test environment have the nova cert running?'),
cfg.BoolOpt('personality',
- default=True,
+ default=False,
help='Does the test environment support server personality'),
cfg.BoolOpt('attach_encrypted_volume',
default=True,
@@ -1350,3 +1351,84 @@
return f(self, *func_args, **func_kwargs)
return wrapper
return decorator
+
+
+def service_client_config(service_client_name=None):
+ """Return a dict with the parameters to init service clients
+
+ Extracts from CONF the settings specific to the service_client_name and
+ api_version, and formats them as dict ready to be passed to the service
+ clients __init__:
+
+ * `region` (default to identity)
+ * `catalog_type`
+ * `endpoint_type`
+ * `build_timeout` (object-storage and identity default to compute)
+ * `build_interval` (object-storage and identity default to compute)
+
+ The following common settings are always returned, even if
+ `service_client_name` is None:
+
+ * `disable_ssl_certificate_validation`
+ * `ca_certs`
+ * `trace_requests`
+
+ The dict returned by this does not fit a few service clients:
+
+ * The endpoint type is not returned for identity client, since it takes
+ three different values for v2 admin, v2 public and v3
+ * The `ServersClient` from compute accepts an optional
+ `enable_instance_password` parameter, which is not returned.
+ * The `VolumesClient` for both v1 and v2 volume accept an optional
+ `default_volume_size` parameter, which is not returned.
+ * The `TokenClient` and `V3TokenClient` have a very different
+ interface, only auth_url is needed for them.
+
+ :param service_client_name: str Name of the service. Supported values are
+ 'compute', 'identity', 'image', 'network', 'object-storage', 'volume'
+ :return: dictionary of __init__ parameters for the service clients
+ :rtype: dict
+ """
+ _parameters = {
+ 'disable_ssl_certificate_validation':
+ CONF.identity.disable_ssl_certificate_validation,
+ 'ca_certs': CONF.identity.ca_certificates_file,
+ 'trace_requests': CONF.debug.trace_requests
+ }
+
+ if service_client_name is None:
+ return _parameters
+
+ # Get the group of options first, by normalising the service_group_name
+ # Services with a '-' in the name have an '_' in the option group name
+ config_group = service_client_name.replace('-', '_')
+ # NOTE(andreaf) Check if the config group exists. This allows for this
+ # helper to be used for settings from registered plugins as well
+ try:
+ options = getattr(CONF, config_group)
+ except cfg.NoSuchOptError:
+ # Option group not defined
+ raise exceptions.UnknownServiceClient(services=service_client_name)
+ # Set endpoint_type
+ # Identity uses different settings depending on API version, so do not
+ # return the endpoint at all.
+ if service_client_name != 'identity':
+ _parameters['endpoint_type'] = getattr(options, 'endpoint_type')
+ # Set build_*
+ # Object storage and identity groups do not have conf settings for
+ # build_* parameters, and we default to compute in any case
+ for setting in ['build_timeout', 'build_interval']:
+ if not hasattr(options, setting) or not getattr(options, setting):
+ _parameters[setting] = getattr(CONF.compute, setting)
+ else:
+ _parameters[setting] = getattr(options, setting)
+ # Set region
+ # If a service client does not define region or region is not set
+ # default to the identity region
+ if not hasattr(options, 'region') or not getattr(options, 'region'):
+ _parameters['region'] = CONF.identity.region
+ else:
+ _parameters['region'] = getattr(options, 'region')
+ # Set service
+ _parameters['service'] = getattr(options, 'catalog_type')
+ return _parameters
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 3289f04..44497db 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -174,7 +174,16 @@
'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']},
'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'},
'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']},
- 'os-extended-volumes:volumes_attached': {'type': 'array'},
+ 'os-extended-volumes:volumes_attached': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': 'string'}
+ },
+ 'additionalProperties': False,
+ },
+ },
'config_drive': {'type': 'string'}
})
server_detail['properties']['addresses']['patternProperties'][
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 425758f..54a7002 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -665,7 +665,7 @@
msg = ('Cannot have conflicting values for %s and %s' %
(key1, key2))
raise exceptions.InvalidCredentials(msg)
- for key in attr.keys():
+ for key in attr:
if key in self.ATTRIBUTES:
setattr(self, key, attr[key])
else:
diff --git a/tempest/lib/base.py b/tempest/lib/base.py
index 227ac37..f687343 100644
--- a/tempest/lib/base.py
+++ b/tempest/lib/base.py
@@ -13,14 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
import os
import fixtures
import testtools
-LOG = logging.getLogger(__name__)
-
class BaseTestCase(testtools.testcase.WithAttributes, testtools.TestCase):
setUpClassCalled = False
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
index f35b14c..d95aa46 100755
--- a/tempest/lib/cmd/skip_tracker.py
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -103,7 +103,7 @@
def get_results(result_dict):
results = []
- for bug_no in result_dict.keys():
+ for bug_no in result_dict:
for method in result_dict[bug_no]:
results.append((method, bug_no))
return results
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index 2a6a788..5ca78f9 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -225,3 +225,7 @@
message = ("Command '%(command)s', exit status: %(exit_status)d, "
"stderr:\n%(stderr)s\n"
"stdout:\n%(stdout)s")
+
+
+class UnknownServiceClient(TempestException):
+ message = "Service clients named %(services)s are not known"
diff --git a/tempest/lib/services/image/v2/image_members_client.py b/tempest/lib/services/image/v2/image_members_client.py
index 2ae7516..d0ab165 100644
--- a/tempest/lib/services/image/v2/image_members_client.py
+++ b/tempest/lib/services/image/v2/image_members_client.py
@@ -19,6 +19,11 @@
api_version = "v2"
def list_image_members(self, image_id):
+ """List image members.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v2.html#listImageMembers-v2
+ """
url = 'images/%s/members' % image_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
@@ -52,12 +57,22 @@
return rest_client.ResponseBody(resp, body)
def show_image_member(self, image_id, member_id):
+ """Show an image member.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v2.html#showImageMember-v2
+ """
url = 'images/%s/members/%s' % (image_id, member_id)
resp, body = self.get(url)
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, json.loads(body))
def delete_image_member(self, image_id, member_id):
+ """Delete an image member.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v2.html#deleteImageMember-v2
+ """
url = 'images/%s/members/%s' % (image_id, member_id)
resp, _ = self.delete(url)
self.expected_success(204, resp.status)
diff --git a/tempest/lib/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index 71e7c6b..996ce94 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -54,24 +54,44 @@
return rest_client.ResponseBody(resp, body)
def deactivate_image(self, image_id):
+ """Deactivate image.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-image-v2.html#deactivateImage-v2
+ """
url = 'images/%s/actions/deactivate' % image_id
resp, body = self.post(url, None)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
def reactivate_image(self, image_id):
+ """Reactivate image.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-image-v2.html#reactivateImage-v2
+ """
url = 'images/%s/actions/reactivate' % image_id
resp, body = self.post(url, None)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
def delete_image(self, image_id):
+ """Delete image.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-image-v2.html#deleteImage-v2
+ """
url = 'images/%s' % image_id
resp, _ = self.delete(url)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp)
def list_images(self, params=None):
+ """List images.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-image-v2.html#listImages-v2
+ """
url = 'images'
if params:
@@ -83,6 +103,11 @@
return rest_client.ResponseBody(resp, body)
def show_image(self, image_id):
+ """Show image details.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v2.html#showImage-v2
+ """
url = 'images/%s' % image_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
@@ -102,6 +127,11 @@
return 'image'
def store_image_file(self, image_id, data):
+ """Upload binary image data.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v2.html#storeImageFile-v2
+ """
url = 'images/%s/file' % image_id
# We are going to do chunked transfert, so split the input data
@@ -115,18 +145,33 @@
return rest_client.ResponseBody(resp, body)
def show_image_file(self, image_id):
+ """Download binary image data.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v2.html#showImageFile-v2
+ """
url = 'images/%s/file' % image_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
return rest_client.ResponseBodyData(resp, body)
def add_image_tag(self, image_id, tag):
+ """Add an image tag.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v2.html#addImageTag-v2
+ """
url = 'images/%s/tags/%s' % (image_id, tag)
resp, body = self.put(url, body=None)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
def delete_image_tag(self, image_id, tag):
+ """Delete an image tag.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v2.html#deleteImageTag-v2
+ """
url = 'images/%s/tags/%s' % (image_id, tag)
resp, _ = self.delete(url)
self.expected_success(204, resp.status)
diff --git a/tempest/lib/services/image/v2/namespaces_client.py b/tempest/lib/services/image/v2/namespaces_client.py
index 97400e1..5bd096d 100644
--- a/tempest/lib/services/image/v2/namespaces_client.py
+++ b/tempest/lib/services/image/v2/namespaces_client.py
@@ -58,6 +58,11 @@
return rest_client.ResponseBody(resp, body)
def delete_namespace(self, namespace):
+ """Delete a namespace.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v2.html#deleteNamespace-v2
+ """
url = 'metadefs/namespaces/%s' % namespace
resp, _ = self.delete(url)
self.expected_success(204, resp.status)
diff --git a/tempest/lib/services/network/metering_labels_client.py b/tempest/lib/services/network/metering_labels_client.py
old mode 100644
new mode 100755
index 2350ecd..12a5834
--- a/tempest/lib/services/network/metering_labels_client.py
+++ b/tempest/lib/services/network/metering_labels_client.py
@@ -16,18 +16,41 @@
class MeteringLabelsClient(base.BaseNetworkClient):
def create_metering_label(self, **kwargs):
+ """Creates an L3 metering label.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2-ext.html#
+ createMeteringLabel
+ """
uri = '/metering/metering-labels'
post_data = {'metering_label': kwargs}
return self.create_resource(uri, post_data)
def show_metering_label(self, metering_label_id, **fields):
+ """Shows details for a metering label.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2-ext.html#showMeteringLabel
+ """
uri = '/metering/metering-labels/%s' % metering_label_id
return self.show_resource(uri, **fields)
def delete_metering_label(self, metering_label_id):
+ """Deletes an L3 metering label.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2-ext.html#
+ deleteMeteringLabel
+ """
uri = '/metering/metering-labels/%s' % metering_label_id
return self.delete_resource(uri)
def list_metering_labels(self, **filters):
+ """Lists all L3 metering labels that belong to the tenant.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2-ext.html#
+ listMeteringLabels
+ """
uri = '/metering/metering-labels'
return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/networks_client.py b/tempest/lib/services/network/networks_client.py
old mode 100644
new mode 100755
index 24c2ec5..19fa1db
--- a/tempest/lib/services/network/networks_client.py
+++ b/tempest/lib/services/network/networks_client.py
@@ -16,11 +16,21 @@
class NetworksClient(base.BaseNetworkClient):
def create_network(self, **kwargs):
+ """Creates a network.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2.html#createNetwork
+ """
uri = '/networks'
post_data = {'network': kwargs}
return self.create_resource(uri, post_data)
def update_network(self, network_id, **kwargs):
+ """Updates a network.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2.html#updateNetwork
+ """
uri = '/networks/%s' % network_id
post_data = {'network': kwargs}
return self.update_resource(uri, post_data)
diff --git a/tempest/lib/services/network/ports_client.py b/tempest/lib/services/network/ports_client.py
old mode 100644
new mode 100755
index eba11d3..71f1103
--- a/tempest/lib/services/network/ports_client.py
+++ b/tempest/lib/services/network/ports_client.py
@@ -17,24 +17,49 @@
class PortsClient(base.BaseNetworkClient):
def create_port(self, **kwargs):
+ """Creates a port on a network.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2.html#createPort
+ """
uri = '/ports'
post_data = {'port': kwargs}
return self.create_resource(uri, post_data)
def update_port(self, port_id, **kwargs):
+ """Updates a port.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2.html#updatePort
+ """
uri = '/ports/%s' % port_id
post_data = {'port': kwargs}
return self.update_resource(uri, post_data)
def show_port(self, port_id, **fields):
+ """Shows details for a port.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2.html#showPort
+ """
uri = '/ports/%s' % port_id
return self.show_resource(uri, **fields)
def delete_port(self, port_id):
+ """Deletes a port.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2.html#removePort
+ """
uri = '/ports/%s' % port_id
return self.delete_resource(uri)
def list_ports(self, **filters):
+ """Lists ports to which the tenant has access.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2.html#listPorts
+ """
uri = '/ports'
return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/security_groups_client.py b/tempest/lib/services/network/security_groups_client.py
old mode 100644
new mode 100755
index 0e25339..5c89a6f
--- a/tempest/lib/services/network/security_groups_client.py
+++ b/tempest/lib/services/network/security_groups_client.py
@@ -16,23 +16,48 @@
class SecurityGroupsClient(base.BaseNetworkClient):
def create_security_group(self, **kwargs):
+ """Creates an OpenStack Networking security group.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2-ext.html#createSecGroup
+ """
uri = '/security-groups'
post_data = {'security_group': kwargs}
return self.create_resource(uri, post_data)
def update_security_group(self, security_group_id, **kwargs):
+ """Updates a security group.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2-ext.html#updateSecGroup
+ """
uri = '/security-groups/%s' % security_group_id
post_data = {'security_group': kwargs}
return self.update_resource(uri, post_data)
def show_security_group(self, security_group_id, **fields):
+ """Shows details for a security group.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2-ext.html#showSecGroup
+ """
uri = '/security-groups/%s' % security_group_id
return self.show_resource(uri, **fields)
def delete_security_group(self, security_group_id):
+ """Deletes an OpenStack Networking security group.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2-ext.html#deleteSecGroup
+ """
uri = '/security-groups/%s' % security_group_id
return self.delete_resource(uri)
def list_security_groups(self, **filters):
+ """Lists OpenStack Networking security groups.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2-ext.html#listSecGroups
+ """
uri = '/security-groups'
return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/subnets_client.py b/tempest/lib/services/network/subnets_client.py
old mode 100644
new mode 100755
index 63ed13e..9de4a33
--- a/tempest/lib/services/network/subnets_client.py
+++ b/tempest/lib/services/network/subnets_client.py
@@ -16,11 +16,21 @@
class SubnetsClient(base.BaseNetworkClient):
def create_subnet(self, **kwargs):
+ """Creates a subnet on a network.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2.html#createSubnet
+ """
uri = '/subnets'
post_data = {'subnet': kwargs}
return self.create_resource(uri, post_data)
def update_subnet(self, subnet_id, **kwargs):
+ """Updates a subnet.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-networking-v2.html#updateSubnet
+ """
uri = '/subnets/%s' % subnet_id
post_data = {'subnet': kwargs}
return self.update_resource(uri, post_data)
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index cace90b..086b82d 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -74,7 +74,7 @@
self.assertEqual(aggregate_name, aggregate['name'])
self.assertEqual(azone, aggregate['availability_zone'])
self.assertEqual(hosts, aggregate['hosts'])
- for meta_key in metadata.keys():
+ for meta_key in metadata:
self.assertIn(meta_key, aggregate['metadata'])
self.assertEqual(metadata[meta_key],
aggregate['metadata'][meta_key])
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 402a70c..9c48080 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -642,6 +642,8 @@
Nova should unbind the port from the instance on delete if the port was
not created by Nova as part of the boot request.
+
+ We should also be able to boot another server with the same port.
"""
# Setup the network, create a port and boot the server from that port.
self._setup_network_and_servers(boot_with_port=True)
@@ -670,6 +672,17 @@
self.assertEqual('', port['device_id'])
self.assertEqual('', port['device_owner'])
+ # Boot another server with the same port to make sure nothing was
+ # left around that could cause issues.
+ name = data_utils.rand_name('reuse-port')
+ server = self._create_server(name, self.network, port['id'])
+ port_list = self._list_ports(device_id=server['id'],
+ network_id=self.network['id'])
+ self.assertEqual(1, len(port_list),
+ 'There should only be one port created for '
+ 'server %s.' % server['id'])
+ self.assertEqual(port['id'], port_list[0]['id'])
+
@test.requires_ext(service='network', extension='l3_agent_scheduler')
@test.idempotent_id('2e788c46-fb3f-4ac9-8f82-0561555bea73')
@test.services('compute', 'network')
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 446c87a..b58479d 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -81,22 +81,42 @@
'verify metadata on server. '
'%s is empty.' % md_url)
+ def _mount_config_drive(self):
+ cmd_blkid = 'blkid | grep -i config-2'
+ result = self.ssh_client.exec_command(cmd_blkid)
+ dev_name = re.match('([^:]+)', result).group()
+ self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
+
+ def _unmount_config_drive(self):
+ self.ssh_client.exec_command('sudo umount /mnt')
+
def verify_metadata_on_config_drive(self):
if self.run_ssh and CONF.compute_feature_enabled.config_drive:
# Verify metadata on config_drive
- cmd_blkid = 'blkid | grep -i config-2'
- result = self.ssh_client.exec_command(cmd_blkid)
- dev_name = re.match('([^:]+)', result).group()
- self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
+ self._mount_config_drive()
cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
result = self.ssh_client.exec_command(cmd_md)
- self.ssh_client.exec_command('sudo umount /mnt')
+ self._unmount_config_drive()
result = json.loads(result)
self.assertIn('meta', result)
msg = ('Failed while verifying metadata on config_drive on server.'
' Result of command "%s" is NOT "%s".' % (cmd_md, self.md))
self.assertEqual(self.md, result['meta'], msg)
+ def verify_networkdata_on_config_drive(self):
+ if self.run_ssh and CONF.compute_feature_enabled.config_drive:
+ # Verify network data on config_drive
+ self._mount_config_drive()
+ cmd_md = 'sudo cat /mnt/openstack/latest/network_data.json'
+ result = self.ssh_client.exec_command(cmd_md)
+ self._unmount_config_drive()
+ result = json.loads(result)
+ self.assertIn('services', result)
+ self.assertIn('links', result)
+ self.assertIn('networks', result)
+ # TODO(clarkb) construct network_data from known network
+ # instance info and do direct comparison.
+
@test.idempotent_id('7fff3fb3-91d8-4fd0-bd7d-0204f1f180ba')
@test.attr(type='smoke')
@test.services('compute', 'network')
@@ -116,4 +136,5 @@
self.verify_ssh(keypair)
self.verify_metadata()
self.verify_metadata_on_config_drive()
+ self.verify_networkdata_on_config_drive()
self.servers_client.delete_server(self.instance['id'])
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 6d3ecd4..4b9c61c 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest.common import compute
from tempest.common import waiters
from tempest import config
@@ -35,6 +33,12 @@
"""
+ @classmethod
+ def skip_checks(cls):
+ super(TestShelveInstance, cls).skip_checks()
+ if not CONF.compute_feature_enabled.shelve:
+ raise cls.skipException("Shelve is not available.")
+
def _shelve_then_unshelve_server(self, server):
compute.shelve_server(self.servers_client, server['id'],
force_shelve_offload=True)
@@ -83,15 +87,11 @@
self.assertEqual(timestamp, timestamp2)
@test.idempotent_id('1164e700-0af0-4a4c-8792-35909a88743c')
- @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
- 'Shelve is not available.')
@test.services('compute', 'network', 'image')
def test_shelve_instance(self):
self._create_server_then_shelve_and_unshelve()
@test.idempotent_id('c1b6318c-b9da-490b-9c67-9339b627271f')
- @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
- 'Shelve is not available.')
@test.services('compute', 'volume', 'network', 'image')
def test_shelve_volume_backed_instance(self):
self._create_server_then_shelve_and_unshelve(boot_from_volume=True)
diff --git a/tempest/service_clients.py b/tempest/service_clients.py
index 3208c8d..386e621 100644
--- a/tempest/service_clients.py
+++ b/tempest/service_clients.py
@@ -18,6 +18,22 @@
from tempest.lib import exceptions
+def tempest_modules():
+ """List of service client modules available in Tempest.
+
+ Provides a list of service modules available Tempest.
+ """
+ return set(['compute', 'identity.v2', 'identity.v3', 'image.v1',
+ 'image.v2', 'network', 'object-storage', 'volume.v1',
+ 'volume.v2', 'volume.v3'])
+
+
+def available_modules():
+ """List of service client modules available in Tempest and plugins"""
+ # TODO(andreaf) For now this returns only tempest_modules
+ return tempest_modules()
+
+
class ServiceClients(object):
"""Service client provider class
@@ -30,7 +46,8 @@
>>> from tempest import service_clients
>>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
- >>> johndoe_clients = service_clients.ServiceClients(johndoe)
+ >>> johndoe_clients = service_clients.ServiceClients(johndoe,
+ >>> identity_uri)
>>> johndoe_servers = johndoe_clients.servers_client.list_servers()
"""
@@ -40,17 +57,45 @@
# initialises this class using values from tempest CONF object. The wrapper
# class should only be used by tests hosted in Tempest.
- def __init__(self, credentials, identity_uri, region=None,
- scope='project', disable_ssl_certificate_validation=True,
- ca_certs=None, trace_requests=''):
+ def __init__(self, credentials, identity_uri, region=None, scope='project',
+ disable_ssl_certificate_validation=True, ca_certs=None,
+ trace_requests='', client_parameters=None):
"""Service Clients provider
Instantiate a `ServiceClients` object, from a set of credentials and an
identity URI. The identity version is inferred from the credentials
object. Optionally auth scope can be provided.
+
+ A few parameters can be given a value which is applied as default
+ for all service clients: region, dscv, ca_certs, trace_requests.
+
Parameters dscv, ca_certs and trace_requests all apply to the auth
provider as well as any service clients provided by this manager.
+ Any other client parameter must be set via client_parameters.
+ The list of available parameters is defined in the service clients
+ interfaces. For reference, most clients will accept 'region',
+ 'service', 'endpoint_type', 'build_timeout' and 'build_interval', which
+ are all inherited from RestClient.
+
+ The `config` module in Tempest exposes an helper function
+ `service_client_config` that can be used to extract from configuration
+ a dictionary ready to be injected in kwargs.
+
+ Exceptions are:
+ - Token clients for 'identity' have a very different interface
+ - Volume client for 'volume' accepts 'default_volume_size'
+ - Servers client from 'compute' accepts 'enable_instance_password'
+
+ Examples:
+
+ >>> identity_params = config.service_client_config('identity')
+ >>> params = {
+ >>> 'identity': identity_params,
+ >>> 'compute': {'region': 'region2'}}
+ >>> manager = lib_manager.Manager(
+ >>> my_creds, identity_uri, client_parameters=params)
+
:param credentials: An instance of `auth.Credentials`
:param identity_uri: URI of the identity API. This should be a
mandatory parameter, and it will so soon.
@@ -60,12 +105,26 @@
service clients.
:param ca_certs Applies to auth and to all service clients.
:param trace_requests Applies to auth and to all service clients.
+ :param client_parameters Dictionary with parameters for service
+ clients. Keys of the dictionary are the service client service
+ name, as declared in `service_clients.available_modules()` except
+ for the version. Values are dictionaries of parameters that are
+ going to be passed to all clients in the service client module.
+
+ Examples:
+
+ >>> params_service_x = {'param_name': 'param_value'}
+ >>> client_parameters = { 'service_x': params_service_x }
+
+ >>> params_service_y = config.service_client_config('service_y')
+ >>> client_parameters['service_y'] = params_service_y
+
"""
self.credentials = credentials
self.identity_uri = identity_uri
if not identity_uri:
raise exceptions.InvalidCredentials(
- 'Manager requires a non-empty identity_uri.')
+ 'ServiceClients requires a non-empty identity_uri.')
self.region = region
# Check if passed or default credentials are valid
if not self.credentials.is_valid():
@@ -88,3 +147,32 @@
self.credentials, self.identity_uri, scope=scope,
disable_ssl_certificate_validation=self.dscv,
ca_certs=self.ca_certs, trace_requests=self.trace_requests)
+ # Setup some defaults for client parameters of registered services
+ client_parameters = client_parameters or {}
+ self.parameters = {}
+ # Parameters are provided for unversioned services
+ unversioned_services = set(
+ [x.split('.')[0] for x in available_modules()])
+ for service in unversioned_services:
+ self.parameters[service] = self._setup_parameters(
+ client_parameters.pop(service, {}))
+ # Check that no client parameters was supplied for unregistered clients
+ if client_parameters:
+ raise exceptions.UnknownServiceClient(
+ services=list(client_parameters.keys()))
+
+ def _setup_parameters(self, parameters):
+ """Setup default values for client parameters
+
+ Region by default is the region passed as an __init__ parameter.
+ Checks that no parameter for an unknown service is provided.
+ """
+ _parameters = {}
+ # Use region from __init__
+ if self.region:
+ _parameters['region'] = self.region
+ # Update defaults with specified parameters
+ _parameters.update(parameters)
+ # If any parameter is left, parameters for an unknown service were
+ # provided as input. Fail rather than ignore silently.
+ return _parameters
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/services/volume/base/base_snapshots_client.py
index da7bb01..6d3f03b 100644
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/services/volume/base/base_snapshots_client.py
@@ -10,7 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_log import log as logging
from oslo_serialization import jsonutils as json
from six.moves.urllib import parse as urllib
@@ -18,9 +17,6 @@
from tempest.lib import exceptions as lib_exc
-LOG = logging.getLogger(__name__)
-
-
class BaseSnapshotsClient(rest_client.RestClient):
"""Base Client class to send CRUD Volume API requests."""
diff --git a/tempest/stress/actions/unit_test.py b/tempest/stress/actions/unit_test.py
index 3b27885..e016c61 100644
--- a/tempest/stress/actions/unit_test.py
+++ b/tempest/stress/actions/unit_test.py
@@ -80,8 +80,6 @@
try:
self.run_core()
- except Exception as e:
- raise e
finally:
if (CONF.stress.leave_dirty_stack is False
and self.class_setup_per == SetUpClassRunTime.action):
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index 2beaaa9..925d765 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -249,13 +249,13 @@
had_errors = True
sum_runs += process['statistic']['runs']
sum_fails += process['statistic']['fails']
- print ("Process %d (%s): Run %d actions (%d failed)" % (
- process['p_number'],
- process['action'],
- process['statistic']['runs'],
- process['statistic']['fails']))
- print ("Summary:")
- print ("Run %d actions (%d failed)" % (sum_runs, sum_fails))
+ print("Process %d (%s): Run %d actions (%d failed)" % (
+ process['p_number'],
+ process['action'],
+ process['statistic']['runs'],
+ process['statistic']['fails']))
+ print("Summary:")
+ print("Run %d actions (%d failed)" % (sum_runs, sum_fails))
if not had_errors and CONF.stress.full_clean_stack:
LOG.info("cleaning up")
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 65164a0..71a4c81 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -57,3 +57,57 @@
def __init__(self, parse_conf=True, config_path=None):
self._set_attrs()
self.lock_path = cfg.CONF.oslo_concurrency.lock_path
+
+fake_service1_group = cfg.OptGroup(name='fake-service1', title='Fake service1')
+
+FakeService1Group = [
+ cfg.StrOpt('catalog_type', default='fake-service1'),
+ cfg.StrOpt('endpoint_type', default='faketype'),
+ cfg.StrOpt('region', default='fake_region'),
+ cfg.IntOpt('build_timeout', default=99),
+ cfg.IntOpt('build_interval', default=9)]
+
+fake_service2_group = cfg.OptGroup(name='fake-service2', title='Fake service2')
+
+FakeService2Group = [
+ cfg.StrOpt('catalog_type', default='fake-service2'),
+ cfg.StrOpt('endpoint_type', default='faketype')]
+
+
+class ServiceClientsConfigFixture(conf_fixture.Config):
+
+ def __init__(self):
+ cfg.CONF([], default_config_files=[])
+ config._opts.append((fake_service1_group, FakeService1Group))
+ config._opts.append((fake_service2_group, FakeService2Group))
+ config.register_opts()
+ super(ServiceClientsConfigFixture, self).__init__()
+
+ def setUp(self):
+ super(ServiceClientsConfigFixture, self).setUp()
+ # Debug default values
+ self.conf.set_default('trace_requests', 'fake_module', 'debug')
+ # Identity default values
+ self.conf.set_default('disable_ssl_certificate_validation', True,
+ group='identity')
+ self.conf.set_default('ca_certificates_file', '/fake/certificates',
+ group='identity')
+ self.conf.set_default('region', 'fake_region', 'identity')
+ # Identity endpoints
+ self.conf.set_default('v3_endpoint_type', 'fake_v3_uri', 'identity')
+ self.conf.set_default('v2_public_endpoint_type', 'fake_v2_public_uri',
+ 'identity')
+ self.conf.set_default('v2_admin_endpoint_type', 'fake_v2_admin_uri',
+ 'identity')
+ # Compute default values
+ self.conf.set_default('build_interval', 88, group='compute')
+ self.conf.set_default('build_timeout', 8, group='compute')
+
+
+class ServiceClientsFakePrivate(config.TempestConfigPrivate):
+ def __init__(self, parse_conf=True, config_path=None):
+ self._set_attrs()
+ self.fake_service1 = cfg.CONF['fake-service1']
+ self.fake_service2 = cfg.CONF['fake-service2']
+ print('Services registered')
+ self.lock_path = cfg.CONF.oslo_concurrency.lock_path
diff --git a/tempest/tests/lib/fake_auth_provider.py b/tempest/tests/lib/fake_auth_provider.py
index 8095453..fa8ab47 100644
--- a/tempest/tests/lib/fake_auth_provider.py
+++ b/tempest/tests/lib/fake_auth_provider.py
@@ -31,5 +31,5 @@
class FakeCredentials(object):
def __init__(self, creds_dict):
- for key in creds_dict.keys():
+ for key in creds_dict:
setattr(self, key, creds_dict[key])
diff --git a/tempest/tests/lib/test_credentials.py b/tempest/tests/lib/test_credentials.py
index b6f2cf6..c910d6d 100644
--- a/tempest/tests/lib/test_credentials.py
+++ b/tempest/tests/lib/test_credentials.py
@@ -99,7 +99,7 @@
def _test_is_not_valid(self, ignore_key):
creds = self._get_credentials()
- for attr in self.attributes.keys():
+ for attr in self.attributes:
if attr == ignore_key:
continue
temp_attr = getattr(creds, attr)
diff --git a/tempest/tests/test_config.py b/tempest/tests/test_config.py
new file mode 100644
index 0000000..2808a9c
--- /dev/null
+++ b/tempest/tests/test_config.py
@@ -0,0 +1,89 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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 testtools
+
+from tempest import config
+from tempest.lib import exceptions
+from tempest.tests import base
+from tempest.tests import fake_config
+
+
+class TestServiceClientConfig(base.TestCase):
+
+ expected_common_params = set(['disable_ssl_certificate_validation',
+ 'ca_certs', 'trace_requests'])
+ expected_extra_params = set(['service', 'endpoint_type', 'region',
+ 'build_timeout', 'build_interval'])
+
+ def setUp(self):
+ super(TestServiceClientConfig, self).setUp()
+ self.useFixture(fake_config.ServiceClientsConfigFixture())
+ self.patchobject(config, 'CONF',
+ fake_config.ServiceClientsFakePrivate())
+ self.CONF = config.CONF
+
+ def test_service_client_config_no_service(self):
+ params = config.service_client_config()
+ for param_name in self.expected_common_params:
+ self.assertIn(param_name, params)
+ for param_name in self.expected_extra_params:
+ self.assertNotIn(param_name, params)
+ self.assertEqual(
+ self.CONF.identity.disable_ssl_certificate_validation,
+ params['disable_ssl_certificate_validation'])
+ self.assertEqual(self.CONF.identity.ca_certificates_file,
+ params['ca_certs'])
+ self.assertEqual(self.CONF.debug.trace_requests,
+ params['trace_requests'])
+
+ def test_service_client_config_service_all(self):
+ params = config.service_client_config(
+ service_client_name='fake-service1')
+ for param_name in self.expected_common_params:
+ self.assertIn(param_name, params)
+ for param_name in self.expected_extra_params:
+ self.assertIn(param_name, params)
+ self.assertEqual(self.CONF.fake_service1.catalog_type,
+ params['service'])
+ self.assertEqual(self.CONF.fake_service1.endpoint_type,
+ params['endpoint_type'])
+ self.assertEqual(self.CONF.fake_service1.region, params['region'])
+ self.assertEqual(self.CONF.fake_service1.build_timeout,
+ params['build_timeout'])
+ self.assertEqual(self.CONF.fake_service1.build_interval,
+ params['build_interval'])
+
+ def test_service_client_config_service_minimal(self):
+ params = config.service_client_config(
+ service_client_name='fake-service2')
+ for param_name in self.expected_common_params:
+ self.assertIn(param_name, params)
+ for param_name in self.expected_extra_params:
+ self.assertIn(param_name, params)
+ self.assertEqual(self.CONF.fake_service2.catalog_type,
+ params['service'])
+ self.assertEqual(self.CONF.fake_service2.endpoint_type,
+ params['endpoint_type'])
+ self.assertEqual(self.CONF.identity.region, params['region'])
+ self.assertEqual(self.CONF.compute.build_timeout,
+ params['build_timeout'])
+ self.assertEqual(self.CONF.compute.build_interval,
+ params['build_interval'])
+
+ def test_service_client_config_service_unknown(self):
+ unknown_service = 'unknown_service'
+ with testtools.ExpectedException(exceptions.UnknownServiceClient,
+ '.*' + unknown_service + '.*'):
+ config.service_client_config(service_client_name=unknown_service)
diff --git a/tempest/tests/test_service_clients.py b/tempest/tests/test_service_clients.py
index f67781c..a559086 100644
--- a/tempest/tests/test_service_clients.py
+++ b/tempest/tests/test_service_clients.py
@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations under
# the License.
+import fixtures
import testtools
from tempest.lib import auth
@@ -23,7 +24,13 @@
class TestServiceClients(base.TestCase):
- def test__init__creds_v2_uri(self):
+ def setUp(self):
+ super(TestServiceClients, self).setUp()
+ self.useFixture(fixtures.MockPatch(
+ 'tempest.service_clients.tempest_modules',
+ return_value=set(['fake_service1', 'fake_service2'])))
+
+ def test___init___creds_v2_uri(self):
# Verify that no API request is made, since no mock
# is required to run the test successfully
creds = fake_credentials.FakeKeystoneV2Credentials()
@@ -32,7 +39,7 @@
self.assertIsInstance(_manager.auth_provider,
auth.KeystoneV2AuthProvider)
- def test__init__creds_v3_uri(self):
+ def test___init___creds_v3_uri(self):
# Verify that no API request is made, since no mock
# is required to run the test successfully
creds = fake_credentials.FakeKeystoneV3Credentials()
@@ -41,22 +48,79 @@
self.assertIsInstance(_manager.auth_provider,
auth.KeystoneV3AuthProvider)
- def test__init__base_creds_uri(self):
+ def test___init___base_creds_uri(self):
creds = fake_credentials.FakeCredentials()
uri = 'fake_uri'
with testtools.ExpectedException(exceptions.InvalidCredentials):
service_clients.ServiceClients(creds, identity_uri=uri)
- def test__init__invalid_creds_uri(self):
+ def test___init___invalid_creds_uri(self):
creds = fake_credentials.FakeKeystoneV2Credentials()
delattr(creds, 'username')
uri = 'fake_uri'
with testtools.ExpectedException(exceptions.InvalidCredentials):
service_clients.ServiceClients(creds, identity_uri=uri)
- def test__init__creds_uri_none(self):
+ def test___init___creds_uri_none(self):
creds = fake_credentials.FakeKeystoneV2Credentials()
- msg = "Invalid Credentials\nDetails: Manager requires a non-empty"
+ msg = ("Invalid Credentials\nDetails: ServiceClients requires a "
+ "non-empty")
with testtools.ExpectedException(exceptions.InvalidCredentials,
value_re=msg):
service_clients.ServiceClients(creds, None)
+
+ def test___init___creds_uri_params(self):
+ creds = fake_credentials.FakeKeystoneV2Credentials()
+ expeted_params = {'fake_param1': 'fake_value1',
+ 'fake_param2': 'fake_value2'}
+ params = {'fake_service1': expeted_params}
+ uri = 'fake_uri'
+ _manager = service_clients.ServiceClients(creds, identity_uri=uri,
+ client_parameters=params)
+ self.assertIn('fake_service1', _manager.parameters)
+ for _key in expeted_params:
+ self.assertIn(_key, _manager.parameters['fake_service1'].keys())
+ self.assertEqual(expeted_params[_key],
+ _manager.parameters['fake_service1'].get(_key))
+
+ def test___init___creds_uri_params_unknown_services(self):
+ creds = fake_credentials.FakeKeystoneV2Credentials()
+ fake_params = {'fake_param1': 'fake_value1'}
+ params = {'unknown_service1': fake_params,
+ 'unknown_service2': fake_params}
+ uri = 'fake_uri'
+ msg = "(?=.*{0})(?=.*{1})".format(*list(params.keys()))
+ with testtools.ExpectedException(
+ exceptions.UnknownServiceClient, value_re=msg):
+ service_clients.ServiceClients(creds, identity_uri=uri,
+ client_parameters=params)
+
+ def _get_manager(self, init_region='fake_region'):
+ # Get a manager to invoke _setup_parameters on
+ creds = fake_credentials.FakeKeystoneV2Credentials()
+ return service_clients.ServiceClients(creds, identity_uri='fake_uri',
+ region=init_region)
+
+ def test__setup_parameters_none_no_region(self):
+ kwargs = {}
+ _manager = self._get_manager(init_region=None)
+ _params = _manager._setup_parameters(kwargs)
+ self.assertNotIn('region', _params)
+
+ def test__setup_parameters_none(self):
+ kwargs = {}
+ _manager = self._get_manager()
+ _params = _manager._setup_parameters(kwargs)
+ self.assertIn('region', _params)
+ self.assertEqual('fake_region', _params['region'])
+
+ def test__setup_parameters_all(self):
+ expected_params = {'region': 'fake_region1',
+ 'catalog_type': 'fake_service2_mod',
+ 'fake_param1': 'fake_value1',
+ 'fake_param2': 'fake_value2'}
+ _manager = self._get_manager()
+ _params = _manager._setup_parameters(expected_params)
+ for _key in _params.keys():
+ self.assertEqual(expected_params[_key],
+ _params[_key])
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index b554514..55f41a6 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -95,7 +95,7 @@
def get_results(result_dict):
results = []
- for bug_no in result_dict.keys():
+ for bug_no in result_dict:
for method in result_dict[bug_no]:
results.append((method, bug_no))
return results