Merge "Make identity v3 users_client use **kwargs"
diff --git a/doc/source/index.rst b/doc/source/index.rst
index f1ede06..6abe9dc 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -50,7 +50,6 @@
account_generator
cleanup
- javelin
subunit_describe_calls
workspace
run
diff --git a/doc/source/javelin.rst b/doc/source/javelin.rst
deleted file mode 100644
index 01090ca..0000000
--- a/doc/source/javelin.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-----------------------------------------------------------
-Javelin2 - How to check that resources survived an upgrade
-----------------------------------------------------------
-
-.. automodule:: tempest.cmd.javelin
diff --git a/doc/source/library.rst b/doc/source/library.rst
index 6a2fb83..29248d1 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -67,3 +67,4 @@
library/utils
library/api_microversion_testing
library/auth
+ library/clients
diff --git a/doc/source/library/clients.rst b/doc/source/library/clients.rst
new file mode 100644
index 0000000..086cfc9
--- /dev/null
+++ b/doc/source/library/clients.rst
@@ -0,0 +1,24 @@
+.. _clients:
+
+Service Clients Usage
+=====================
+
+Tests make requests against APIs using service clients. Service clients are
+specializations of the ``RestClient`` class. The service clients that cover the
+APIs exposed by a service should be grouped in a service clients module.
+A service clients module is python module where all service clients are
+defined. If major API versions are available, submodules should be defined,
+one for each version.
+
+The ``ClientsFactory`` class helps initializing all clients of a specific
+service client module from a set of shared parameters.
+
+The ``ServiceClients`` class provides a convenient way to get access to all
+available service clients initialized with a provided set of credentials.
+
+------------------
+The clients module
+------------------
+
+.. automodule:: tempest.lib.services.clients
+ :members:
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 9640469..d34023f 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -111,8 +111,9 @@
class MyPlugin(plugins.TempestPlugin):
-Then you need to ensure you locally define all of the methods in the abstract
-class, you can refer to the api doc below for a reference of what that entails.
+Then you need to ensure you locally define all of the mandatory methods in the
+abstract class, you can refer to the api doc below for a reference of what that
+entails.
Abstract Plugin Class
---------------------
@@ -164,6 +165,142 @@
CONF object from tempest. This enables the plugin to add options to both
existing sections and also create new configuration sections for new options.
+Service Clients
+---------------
+
+If a plugin defines a service client, it is beneficial for it to implement the
+``get_service_clients`` method in the plugin class. All service clients which
+are exposed via this interface will be automatically configured and be
+available in any instance of the service clients class, defined in
+``tempest.lib.services.clients.ServiceClients``. In case multiple plugins are
+installed, all service clients from all plugins will be registered, making it
+easy to write tests which rely on multiple APIs whose service clients are in
+different plugins.
+
+Example implementation of ``get_service_clients``::
+
+ def get_service_clients(self):
+ # Example implementation with two service clients
+ my_service1_config = config.service_client_config('my_service')
+ params_my_service1 = {
+ 'name': 'my_service_v1',
+ 'service_version': 'my_service.v1',
+ 'module_path': 'plugin_tempest_tests.services.my_service.v1',
+ 'client_names': ['API1Client', 'API2Client'],
+ }
+ params_my_service1.update(my_service_config)
+ my_service2_config = config.service_client_config('my_service')
+ params_my_service2 = {
+ 'name': 'my_service_v2',
+ 'service_version': 'my_service.v2',
+ 'module_path': 'plugin_tempest_tests.services.my_service.v2',
+ 'client_names': ['API1Client', 'API2Client'],
+ }
+ params_my_service2.update(my_service2_config)
+ return [params_my_service1, params_my_service2]
+
+Parameters:
+
+* **name**: Name of the attribute used to access the ``ClientsFactory`` from
+ the ``ServiceClients`` instance. See example below.
+* **service_version**: Tempest enforces a single implementation for each
+ service client. Available service clients are held in a ``ClientsRegistry``
+ singleton, and registered with ``service_version``, which means that
+ ``service_version`` must be unique and it should represent the service API
+ and version implemented by the service client.
+* **module_path**: Relative to the service client module, from the root of the
+ plugin.
+* **client_names**: Name of the classes that implement service clients in the
+ service clients module.
+
+Example usage of the the service clients in tests::
+
+ # my_creds is instance of tempest.lib.auth.Credentials
+ # identity_uri is v2 or v3 depending on the configuration
+ from tempest.lib.services import clients
+
+ my_clients = clients.ServiceClients(my_creds, identity_uri)
+ my_service1_api1_client = my_clients.my_service_v1.API1Client()
+ my_service2_api1_client = my_clients.my_service_v2.API1Client(my_args='any')
+
+Automatic configuration and registration of service clients imposes some extra
+constraints on the structure of the configuration options exposed by the
+plugin.
+
+First ``service_version`` should be in the format `service_config[.version]`.
+The `.version` part is optional, and should only be used if there are multiple
+versions of the same API available. The `service_config` must match the name of
+a configuration options group defined by the plugin. Different versions of one
+API must share the same configuration group.
+
+Second the configuration options group `service_config` must contain the
+following options:
+
+* `catalog_type`: corresponds to `service` in the catalog
+* `endpoint_type`
+
+The following options will be honoured if defined, but they are not mandatory,
+as they do not necessarily apply to all service clients.
+
+* `region`: default to identity.region
+* `build_timeout` : default to compute.build_timeout
+* `build_interval`: default to compute.build_interval
+
+Third the service client classes should inherit from ``RestClient``, should
+accept generic keyword arguments, and should pass those arguments to the
+``__init__`` method of ``RestClient``. Extra arguments can be added. For
+instance::
+
+ class MyAPIClient(rest_client.RestClient):
+
+ def __init__(self, auth_provider, service, region,
+ my_arg, my_arg2=True, **kwargs):
+ super(MyAPIClient, self).__init__(
+ auth_provider, service, region, **kwargs)
+ self.my_arg = my_arg
+ self.my_args2 = my_arg
+
+Finally the service client should be structured in a python module, so that all
+service client classes are importable from it. Each major API version should
+have its own module.
+
+The following folder and module structure is recommended for a single major
+API version::
+
+ plugin_dir/
+ services/
+ __init__.py
+ client_api_1.py
+ client_api_2.py
+
+The content of __init__.py module should be::
+
+ from client_api_1.py import API1Client
+ from client_api_2.py import API2Client
+
+ __all__ = ['API1Client', 'API2Client']
+
+The following folder and module structure is recommended for multiple major
+API version::
+
+ plugin_dir/
+ services/
+ v1/
+ __init__.py
+ client_api_1.py
+ client_api_2.py
+ v2/
+ __init__.py
+ client_api_1.py
+ client_api_2.py
+
+The content each of __init__.py module under vN should be::
+
+ from client_api_1.py import API1Client
+ from client_api_2.py import API2Client
+
+ __all__ = ['API1Client', 'API2Client']
+
Using Plugins
=============
diff --git a/etc/javelin-resources.yaml.sample b/etc/javelin-resources.yaml.sample
deleted file mode 100644
index 1565686..0000000
--- a/etc/javelin-resources.yaml.sample
+++ /dev/null
@@ -1,63 +0,0 @@
-tenants:
- - javelin
- - discuss
-
-users:
- - name: javelin
- pass: gungnir
- tenant: javelin
- - name: javelin2
- pass: gungnir2
- tenant: discuss
-
-secgroups:
- - name: secgroup1
- owner: javelin
- description: SecurityGroup1
- rules:
- - 'icmp -1 -1 0.0.0.0/0'
- - 'tcp 22 22 0.0.0.0/0'
- - name: secgroup2
- owner: javelin2
- description: SecurityGroup2
- rules:
- - 'tcp 80 80 0.0.0.0/0'
-
-images:
- - name: cirros1
- owner: javelin
- imgdir: images
- file: cirros.img
- container_format: bare
- disk_format: qcow2
- - name: cirros2
- owner: javelin2
- imgdir: files/images/cirros-0.3.2-x86_64-uec
- file: cirros-0.3.2-x86_64-blank.img
- container_format: ami
- disk_format: ami
- aki: cirros-0.3.2-x86_64-vmlinuz
- ari: cirros-0.3.2-x86_64-initrd
-
-networks:
- - name: network1
- owner: javelin
- - name: network2
- owner: javelin2
-
-subnets:
- - name: net1-subnet1
- range: 10.1.0.0/24
- network: network1
- owner: javelin
- - name: net2-subnet2
- range: 192.168.1.0/24
- network: network2
- owner: javelin2
-
-objects:
- - container: container1
- name: object1
- owner: javelin
- file: /etc/hosts
- swift_role: Member
diff --git a/releasenotes/notes/add-httptimeout-in-restclient-ax78061900e3f3d7.yaml b/releasenotes/notes/add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
new file mode 100644
index 0000000..a360f8e
--- /dev/null
+++ b/releasenotes/notes/add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - RestClient now supports setting timeout in urllib3.poolmanager.
+ Clients will use CONF.service_clients.http_timeout for timeout
+ value to wait for http request to response.
+ - KeystoneAuthProvider will accept http_timeout and will use it in
+ get_credentials.
diff --git a/releasenotes/notes/clients_module-16f3025f515bf9ec.yaml b/releasenotes/notes/clients_module-16f3025f515bf9ec.yaml
new file mode 100644
index 0000000..53741da
--- /dev/null
+++ b/releasenotes/notes/clients_module-16f3025f515bf9ec.yaml
@@ -0,0 +1,18 @@
+---
+features:
+ - The Tempest plugin interface contains a new optional method, which allows
+ plugins to declare and automatically register any service client defined
+ in the plugin.
+ - tempest.lib exposes a new stable interface, the clients module and
+ ServiceClients class, which provides a convinient way for plugin tests to
+ access service clients defined in Tempest as well as service clients
+ defined in all loaded plugins.
+ The new ServiceClients class only exposes for now the service clients
+ which are in tempest.lib, i.e. compute, network and image. The remaing
+ service clients (identity, volume and object-storage) will be added in
+ future updates.
+deprecations:
+ - The new clients module provides a stable alternative to tempest classes
+ manager.Manager and clients.Manager. manager.Manager only exists now
+ to smoothen the transition of plugins to the new interface, but it will
+ be removed shortly without further notice.
diff --git a/releasenotes/notes/nova_cert_default-90eb7c1e3cde624a.yaml b/releasenotes/notes/nova_cert_default-90eb7c1e3cde624a.yaml
new file mode 100644
index 0000000..cfe97c5
--- /dev/null
+++ b/releasenotes/notes/nova_cert_default-90eb7c1e3cde624a.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+
+ - The ``nova_cert`` option default is changed to ``False``. The nova
+ certification management APIs were a hold over from ec2, and are
+ not used by any other parts of nova. They are deprecated for
+ removal in nova after the newton release. This makes false a more
+ sensible default going forward.
\ No newline at end of file
diff --git a/releasenotes/notes/remove-javelin-276f62d04f7e4a1d.yaml b/releasenotes/notes/remove-javelin-276f62d04f7e4a1d.yaml
new file mode 100644
index 0000000..8e893b8
--- /dev/null
+++ b/releasenotes/notes/remove-javelin-276f62d04f7e4a1d.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+ - The previously deprecated Javelin utility has been removed from Tempest.
+ As an alternative Ansible can be used to construct similar yaml workflows
+ to what Javelin used to provide.
diff --git a/releasenotes/notes/volume-clients-as-library-9a3444dd63c134b3.yaml b/releasenotes/notes/volume-clients-as-library-9a3444dd63c134b3.yaml
new file mode 100644
index 0000000..6a4fc2b
--- /dev/null
+++ b/releasenotes/notes/volume-clients-as-library-9a3444dd63c134b3.yaml
@@ -0,0 +1,16 @@
+---
+features:
+ - |
+ Define volume service clients as libraries
+ The following volume service clients are defined as library interface,
+ so the other projects can use these modules as stable libraries
+ without any maintenance changes.
+
+ * availability_zone_client(v1)
+ * availability_zone_client(v2)
+ * extensions_client(v1)
+ * extensions_client(v2)
+ * hosts_client(v1)
+ * hosts_client(v2)
+ * services_client(v1)
+ * services_client(v2)
diff --git a/requirements.txt b/requirements.txt
index 058ea00..d698cda 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,11 +9,11 @@
netaddr!=0.7.16,>=0.7.12 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
oslo.concurrency>=3.8.0 # Apache-2.0
-oslo.config>=3.12.0 # Apache-2.0
+oslo.config>=3.14.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.15.0 # Apache-2.0
+oslo.utils>=3.16.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/setup.cfg b/setup.cfg
index 2a3000d..91ede20 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -27,7 +27,6 @@
[entry_points]
console_scripts =
verify-tempest-config = tempest.cmd.verify_tempest_config:main
- javelin2 = tempest.cmd.javelin:main
run-tempest-stress = tempest.cmd.run_stress:main
tempest-cleanup = tempest.cmd.cleanup:main
tempest-account-generator = tempest.cmd.account_generator:main
diff --git a/tempest/api/baremetal/admin/base.py b/tempest/api/baremetal/admin/base.py
index f7891dd..2d3f190 100644
--- a/tempest/api/baremetal/admin/base.py
+++ b/tempest/api/baremetal/admin/base.py
@@ -96,7 +96,7 @@
@classmethod
@creates('chassis')
- def create_chassis(cls, description=None, expect_errors=False):
+ def create_chassis(cls, description=None):
"""Wrapper utility for creating test chassis.
:param description: A description of the chassis. if not supplied,
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 150e8af..3754637 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -49,6 +49,9 @@
name=snapshot_name,
wait_until='SAVING')
self.client.delete_image(image['id'])
+ msg = ('The image with ID {image_id} failed to be deleted'
+ .format(image_id=image['id']))
+ self.assertTrue(self.client.is_resource_deleted(image['id']), msg)
@test.idempotent_id('aaacd1d0-55a2-4ce8-818a-b5439df8adc9')
def test_create_image_from_stopped_server(self):
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 05c23ee..a5c303c 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -84,19 +84,21 @@
self.volume['id'], 'available')
if shelve_server:
- # NOTE(andreaf) If we are going to shelve a server, we should
- # check first whether the server is ssh-able. Otherwise we won't
- # be able to distinguish failures introduced by shelve from
- # pre-existing ones. Also it's good to wait for cloud-init to be
- # done and sshd server to be running before shelving to avoid
- # breaking the VM
- linux_client = remote_client.RemoteClient(
- self.get_server_ip(self.server),
- self.image_ssh_user,
- self.admin_pass,
- self.validation_resources['keypair']['private_key'])
- linux_client.validate_authentication()
- # If validation went ok, shelve the server
+ if CONF.validation.run_validation:
+ # NOTE(andreaf) If we are going to shelve a server, we should
+ # check first whether the server is ssh-able. Otherwise we
+ # won't be able to distinguish failures introduced by shelve
+ # from pre-existing ones. Also it's good to wait for cloud-init
+ # to be done and sshd server to be running before shelving to
+ # avoid breaking the VM
+ linux_client = remote_client.RemoteClient(
+ self.get_server_ip(self.server),
+ self.image_ssh_user,
+ self.admin_pass,
+ self.validation_resources['keypair']['private_key'])
+ linux_client.validate_authentication()
+
+ # If validation went ok, or it was skipped, shelve the server
compute.shelve_server(self.servers_client, self.server['id'])
# Attach the volume to the server
@@ -110,8 +112,6 @@
self.addCleanup(self._detach, self.server['id'], self.volume['id'])
@test.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
- @testtools.skipUnless(CONF.validation.run_validation,
- 'SSH required for this test')
def test_attach_detach_volume(self):
# Stop and Start a server with an attached volume, ensuring that
# the volume remains attached.
@@ -125,16 +125,17 @@
waiters.wait_for_server_status(self.servers_client, self.server['id'],
'ACTIVE')
- linux_client = remote_client.RemoteClient(
- self.get_server_ip(self.server),
- self.image_ssh_user,
- self.admin_pass,
- self.validation_resources['keypair']['private_key'],
- server=self.server,
- servers_client=self.servers_client)
+ if CONF.validation.run_validation:
+ linux_client = remote_client.RemoteClient(
+ self.get_server_ip(self.server),
+ self.image_ssh_user,
+ self.admin_pass,
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.servers_client)
- partitions = linux_client.get_partitions()
- self.assertIn(self.device, partitions)
+ partitions = linux_client.get_partitions()
+ self.assertIn(self.device, partitions)
self._detach(self.server['id'], self.volume['id'])
self.attachment = None
@@ -146,16 +147,17 @@
waiters.wait_for_server_status(self.servers_client, self.server['id'],
'ACTIVE')
- linux_client = remote_client.RemoteClient(
- self.get_server_ip(self.server),
- self.image_ssh_user,
- self.admin_pass,
- self.validation_resources['keypair']['private_key'],
- server=self.server,
- servers_client=self.servers_client)
+ if CONF.validation.run_validation:
+ linux_client = remote_client.RemoteClient(
+ self.get_server_ip(self.server),
+ self.image_ssh_user,
+ self.admin_pass,
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.servers_client)
- partitions = linux_client.get_partitions()
- self.assertNotIn(self.device, partitions)
+ partitions = linux_client.get_partitions()
+ self.assertNotIn(self.device, partitions)
@test.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
def test_list_get_volume_attachments(self):
@@ -192,23 +194,22 @@
waiters.wait_for_server_status(self.servers_client,
self.server['id'],
'ACTIVE')
- linux_client = remote_client.RemoteClient(
- self.get_server_ip(self.server['id']),
- self.image_ssh_user,
- self.admin_pass,
- self.validation_resources['keypair']['private_key'],
- server=self.server,
- servers_client=self.servers_client)
+ if CONF.validation.run_validation:
+ linux_client = remote_client.RemoteClient(
+ self.get_server_ip(self.server['id']),
+ self.image_ssh_user,
+ self.admin_pass,
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.servers_client)
- command = 'grep [vs]d /proc/partitions | wc -l'
- nb_partitions = linux_client.exec_command(command).strip()
- self.assertEqual(number_of_partition, nb_partitions)
+ command = 'grep [vs]d /proc/partitions | wc -l'
+ nb_partitions = linux_client.exec_command(command).strip()
+ self.assertEqual(number_of_partition, nb_partitions)
@test.idempotent_id('13a940b6-3474-4c3c-b03f-29b89112bfee')
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
- @testtools.skipUnless(CONF.validation.run_validation,
- 'SSH required for this test')
def test_attach_volume_shelved_or_offload_server(self):
self._create_and_attach(shelve_server=True)
@@ -228,8 +229,6 @@
@test.idempotent_id('b54e86dd-a070-49c4-9c07-59ae6dae15aa')
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
- @testtools.skipUnless(CONF.validation.run_validation,
- 'SSH required for this test')
def test_detach_volume_shelved_or_offload_server(self):
self._create_and_attach(shelve_server=True)
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 97d9eed..85026af 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -15,6 +15,7 @@
from tempest.common import custom_matchers
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
import tempest.test
@@ -57,16 +58,41 @@
cls.container_client.auth_provider.clear_auth()
cls.account_client.auth_provider.clear_auth()
+ cls.containers = []
+
@classmethod
- def delete_containers(cls, containers, container_client=None,
+ def create_container(cls):
+ # wrapper that returns a test container
+ container_name = data_utils.rand_name(name='TestContainer')
+ cls.container_client.create_container(container_name)
+ cls.containers.append(container_name)
+
+ return container_name
+
+ @classmethod
+ def create_object(cls, container_name, object_name=None,
+ data=None, metadata=None):
+ # wrapper that returns a test object
+ if object_name is None:
+ object_name = data_utils.rand_name(name='TestObject')
+ if data is None:
+ data = data_utils.arbitrary_string()
+ cls.object_client.create_object(container_name,
+ object_name,
+ data,
+ metadata=metadata)
+
+ return object_name, data
+
+ @classmethod
+ def delete_containers(cls, container_client=None,
object_client=None):
- """Remove given containers and all objects in them.
+ """Remove containers and all objects in them.
The containers should be visible from the container_client given.
Will not throw any error if the containers don't exist.
Will not check that object and container deletions succeed.
- :param containers: list of container names to remove
:param container_client: if None, use cls.container_client, this means
that the default testing user will be used (see 'username' in
'etc/tempest.conf')
@@ -76,7 +102,7 @@
container_client = cls.container_client
if object_client is None:
object_client = cls.object_client
- for cont in containers:
+ for cont in cls.containers:
try:
objlist = container_client.list_all_container_objects(cont)
# delete every object in the container
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index da4c80c..7292ee9 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -27,7 +27,7 @@
self.containers = []
def tearDown(self):
- self.delete_containers(self.containers)
+ self.delete_containers()
super(BulkTest, self).tearDown()
def _create_archive(self):
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 0f6a330..fcbd6eb 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -34,8 +34,7 @@
@classmethod
def resource_setup(cls):
super(AccountQuotasTest, cls).resource_setup()
- cls.container_name = data_utils.rand_name(name="TestContainer")
- cls.container_client.create_container(cls.container_name)
+ cls.container_name = cls.create_container()
# Retrieve a ResellerAdmin auth data and use it to set a quota
# on the client's account
@@ -73,8 +72,7 @@
@classmethod
def resource_cleanup(cls):
- if hasattr(cls, "container_name"):
- cls.delete_containers([cls.container_name])
+ cls.delete_containers()
super(AccountQuotasTest, cls).resource_cleanup()
@test.attr(type="smoke")
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 546bb06..0eec387 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -36,8 +36,7 @@
@classmethod
def resource_setup(cls):
super(AccountQuotasNegativeTest, cls).resource_setup()
- cls.container_name = data_utils.rand_name(name="TestContainer")
- cls.container_client.create_container(cls.container_name)
+ cls.container_name = cls.create_container()
# Retrieve a ResellerAdmin auth data and use it to set a quota
# on the client's account
@@ -74,8 +73,7 @@
@classmethod
def resource_cleanup(cls):
- if hasattr(cls, "container_name"):
- cls.delete_containers([cls.container_name])
+ cls.delete_containers()
super(AccountQuotasNegativeTest, cls).resource_cleanup()
@test.attr(type=["negative"])
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 5983c1f..723b870 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -50,7 +50,7 @@
@classmethod
def resource_cleanup(cls):
- cls.delete_containers(cls.containers)
+ cls.delete_containers()
super(AccountTest, cls).resource_cleanup()
@test.attr(type='smoke')
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index c1b6711..ffdd1de 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -39,11 +39,10 @@
def setUp(self):
super(ObjectTestACLs, self).setUp()
- self.container_name = data_utils.rand_name(name='TestContainer')
- self.container_client.create_container(self.container_name)
+ self.container_name = self.create_container()
def tearDown(self):
- self.delete_containers([self.container_name])
+ self.delete_containers()
super(ObjectTestACLs, self).tearDown()
@test.idempotent_id('a3270f3f-7640-4944-8448-c7ea783ea5b6')
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index 01e5389..c26e49b 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -38,8 +38,7 @@
Maximum object count of the container.
"""
super(ContainerQuotasTest, self).setUp()
- self.container_name = data_utils.rand_name(name="TestContainer")
- self.container_client.create_container(self.container_name)
+ self.container_name = self.create_container()
metadata = {"quota-bytes": str(QUOTA_BYTES),
"quota-count": str(QUOTA_COUNT), }
self.container_client.update_container_metadata(
@@ -47,7 +46,7 @@
def tearDown(self):
"""Cleans the container of any object after each test."""
- self.delete_containers([self.container_name])
+ self.delete_containers()
super(ContainerQuotasTest, self).tearDown()
@test.idempotent_id('9a0fb034-86af-4df0-86fa-f8bd7db21ae0')
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 9d043e5..296d8ee 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -21,31 +21,11 @@
class ContainerTest(base.BaseObjectTest):
def setUp(self):
super(ContainerTest, self).setUp()
- self.containers = []
def tearDown(self):
- self.delete_containers(self.containers)
+ self.delete_containers()
super(ContainerTest, self).tearDown()
- def _create_container(self):
- # setup container
- container_name = data_utils.rand_name(name='TestContainer')
- self.container_client.create_container(container_name)
- self.containers.append(container_name)
-
- return container_name
-
- def _create_object(self, container_name, object_name=None):
- # setup object
- if object_name is None:
- object_name = data_utils.rand_name(name='TestObject')
- data = data_utils.arbitrary_string()
- self.object_client.create_object(container_name,
- object_name,
- data)
-
- return object_name
-
@test.attr(type='smoke')
@test.idempotent_id('92139d73-7819-4db1-85f8-3f2f22a8d91f')
def test_create_container(self):
@@ -140,7 +120,7 @@
@test.idempotent_id('95d3a249-b702-4082-a2c4-14bb860cf06a')
def test_delete_container(self):
# create a container
- container_name = self._create_container()
+ container_name = self.create_container()
# delete container, success asserted within
resp, _ = self.container_client.delete_container(container_name)
self.assertHeaders(resp, 'Container', 'DELETE')
@@ -150,8 +130,8 @@
@test.idempotent_id('312ff6bd-5290-497f-bda1-7c5fec6697ab')
def test_list_container_contents(self):
# get container contents list
- container_name = self._create_container()
- object_name = self._create_object(container_name)
+ container_name = self.create_container()
+ object_name, _ = self.create_object(container_name)
resp, object_list = self.container_client.list_container_contents(
container_name)
@@ -161,7 +141,7 @@
@test.idempotent_id('4646ac2d-9bfb-4c7d-a3c5-0f527402b3df')
def test_list_container_contents_with_no_object(self):
# get empty container contents list
- container_name = self._create_container()
+ container_name = self.create_container()
resp, object_list = self.container_client.list_container_contents(
container_name)
@@ -171,9 +151,9 @@
@test.idempotent_id('fe323a32-57b9-4704-a996-2e68f83b09bc')
def test_list_container_contents_with_delimiter(self):
# get container contents list using delimiter param
- container_name = self._create_container()
+ container_name = self.create_container()
object_name = data_utils.rand_name(name='TestObject/')
- self._create_object(container_name, object_name)
+ self.create_object(container_name, object_name)
params = {'delimiter': '/'}
resp, object_list = self.container_client.list_container_contents(
@@ -185,8 +165,8 @@
@test.idempotent_id('55b4fa5c-e12e-4ca9-8fcf-a79afe118522')
def test_list_container_contents_with_end_marker(self):
# get container contents list using end_marker param
- container_name = self._create_container()
- object_name = self._create_object(container_name)
+ container_name = self.create_container()
+ object_name, _ = self.create_object(container_name)
params = {'end_marker': 'ZzzzObject1234567890'}
resp, object_list = self.container_client.list_container_contents(
@@ -198,8 +178,8 @@
@test.idempotent_id('196f5034-6ab0-4032-9da9-a937bbb9fba9')
def test_list_container_contents_with_format_json(self):
# get container contents list using format_json param
- container_name = self._create_container()
- self._create_object(container_name)
+ container_name = self.create_container()
+ self.create_object(container_name)
params = {'format': 'json'}
resp, object_list = self.container_client.list_container_contents(
@@ -217,8 +197,8 @@
@test.idempotent_id('655a53ca-4d15-408c-a377-f4c6dbd0a1fa')
def test_list_container_contents_with_format_xml(self):
# get container contents list using format_xml param
- container_name = self._create_container()
- self._create_object(container_name)
+ container_name = self.create_container()
+ self.create_object(container_name)
params = {'format': 'xml'}
resp, object_list = self.container_client.list_container_contents(
@@ -241,8 +221,8 @@
@test.idempotent_id('297ec38b-2b61-4ff4-bcd1-7fa055e97b61')
def test_list_container_contents_with_limit(self):
# get container contents list using limit param
- container_name = self._create_container()
- object_name = self._create_object(container_name)
+ container_name = self.create_container()
+ object_name, _ = self.create_object(container_name)
params = {'limit': data_utils.rand_int_id(1, 10000)}
resp, object_list = self.container_client.list_container_contents(
@@ -254,8 +234,8 @@
@test.idempotent_id('c31ddc63-2a58-4f6b-b25c-94d2937e6867')
def test_list_container_contents_with_marker(self):
# get container contents list using marker param
- container_name = self._create_container()
- object_name = self._create_object(container_name)
+ container_name = self.create_container()
+ object_name, _ = self.create_object(container_name)
params = {'marker': 'AaaaObject1234567890'}
resp, object_list = self.container_client.list_container_contents(
@@ -267,9 +247,9 @@
@test.idempotent_id('58ca6cc9-6af0-408d-aaec-2a6a7b2f0df9')
def test_list_container_contents_with_path(self):
# get container contents list using path param
- container_name = self._create_container()
+ container_name = self.create_container()
object_name = data_utils.rand_name(name='Swift/TestObject')
- self._create_object(container_name, object_name)
+ self.create_object(container_name, object_name)
params = {'path': 'Swift'}
resp, object_list = self.container_client.list_container_contents(
@@ -281,8 +261,8 @@
@test.idempotent_id('77e742c7-caf2-4ec9-8aa4-f7d509a3344c')
def test_list_container_contents_with_prefix(self):
# get container contents list using prefix param
- container_name = self._create_container()
- object_name = self._create_object(container_name)
+ container_name = self.create_container()
+ object_name, _ = self.create_object(container_name)
prefix_key = object_name[0:8]
params = {'prefix': prefix_key}
@@ -296,7 +276,7 @@
@test.idempotent_id('96e68f0e-19ec-4aa2-86f3-adc6a45e14dd')
def test_list_container_metadata(self):
# List container metadata
- container_name = self._create_container()
+ container_name = self.create_container()
metadata = {'name': 'Pictures'}
self.container_client.update_container_metadata(
@@ -312,7 +292,7 @@
@test.idempotent_id('a2faf936-6b13-4f8d-92a2-c2278355821e')
def test_list_no_container_metadata(self):
# HEAD container without metadata
- container_name = self._create_container()
+ container_name = self.create_container()
resp, _ = self.container_client.list_container_metadata(
container_name)
@@ -345,7 +325,7 @@
@test.idempotent_id('2ae5f295-4bf1-4e04-bfad-21e54b62cec5')
def test_update_container_metadata_with_create_metadata(self):
# update container metadata using add metadata
- container_name = self._create_container()
+ container_name = self.create_container()
metadata = {'test-container-meta1': 'Meta1'}
resp, _ = self.container_client.update_container_metadata(
@@ -380,7 +360,7 @@
@test.idempotent_id('31f40a5f-6a52-4314-8794-cd89baed3040')
def test_update_container_metadata_with_create_metadata_key(self):
# update container metadata with a blank value of metadata
- container_name = self._create_container()
+ container_name = self.create_container()
metadata = {'test-container-meta1': ''}
resp, _ = self.container_client.update_container_metadata(
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index 5b3ce79..47ef0d3 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -24,18 +24,14 @@
@classmethod
def resource_setup(cls):
super(StaticWebTest, cls).resource_setup()
- cls.container_name = data_utils.rand_name(name="TestContainer")
# This header should be posted on the container before every test
cls.headers_public_read_acl = {'Read': '.r:*,.rlistings'}
# Create test container and create one object in it
- cls.container_client.create_container(cls.container_name)
- cls.object_name = data_utils.rand_name(name="TestObject")
- cls.object_data = data_utils.arbitrary_string()
- cls.object_client.create_object(cls.container_name,
- cls.object_name,
- cls.object_data)
+ cls.container_name = cls.create_container()
+ cls.object_name, cls.object_data = cls.create_object(
+ cls.container_name)
cls.container_client.update_container_metadata(
cls.container_name,
@@ -44,8 +40,7 @@
@classmethod
def resource_cleanup(cls):
- if hasattr(cls, "container_name"):
- cls.delete_containers([cls.container_name])
+ cls.delete_containers()
super(StaticWebTest, cls).resource_cleanup()
@test.idempotent_id('c1f055ab-621d-4a6a-831f-846fcb578b8b')
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 2a5cec6..e10b900 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -80,7 +80,7 @@
@classmethod
def resource_cleanup(cls):
for client in cls.clients.values():
- cls.delete_containers(cls.containers, client[0], client[1])
+ cls.delete_containers(client[0], client[1])
super(ContainerSyncTest, cls).resource_cleanup()
def _test_container_synchronization(self, make_headers):
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 9db8bde..11acb31 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -16,7 +16,6 @@
import time
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -25,19 +24,17 @@
@classmethod
def resource_setup(cls):
super(ObjectExpiryTest, cls).resource_setup()
- cls.container_name = data_utils.rand_name(name='TestContainer')
- cls.container_client.create_container(cls.container_name)
+ cls.container_name = cls.create_container()
def setUp(self):
super(ObjectExpiryTest, self).setUp()
# create object
- self.object_name = data_utils.rand_name(name='TestObject')
- resp, _ = self.object_client.create_object(self.container_name,
- self.object_name, '')
+ self.object_name, _ = self.create_object(
+ self.container_name)
@classmethod
def resource_cleanup(cls):
- cls.delete_containers([cls.container_name])
+ cls.delete_containers()
super(ObjectExpiryTest, cls).resource_cleanup()
def _test_object_expiry(self, metadata):
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index 356f560..102ec2f 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -31,12 +31,9 @@
@classmethod
def resource_setup(cls):
super(ObjectFormPostTest, cls).resource_setup()
- cls.container_name = data_utils.rand_name(name='TestContainer')
+ cls.container_name = cls.create_container()
cls.object_name = data_utils.rand_name(name='ObjectTemp')
- cls.container_client.create_container(cls.container_name)
- cls.containers = [cls.container_name]
-
cls.key = 'Meta'
cls.metadata = {'Temp-URL-Key': cls.key}
cls.account_client.create_account_metadata(metadata=cls.metadata)
@@ -56,7 +53,7 @@
@classmethod
def resource_cleanup(cls):
cls.account_client.delete_account_metadata(metadata=cls.metadata)
- cls.delete_containers(cls.containers)
+ cls.delete_containers()
super(ObjectFormPostTest, cls).resource_cleanup()
def get_multipart_form(self, expires=600):
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index cb13271..8ff5d82 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -32,12 +32,9 @@
@classmethod
def resource_setup(cls):
super(ObjectFormPostNegativeTest, cls).resource_setup()
- cls.container_name = data_utils.rand_name(name='TestContainer')
+ cls.container_name = cls.create_container()
cls.object_name = data_utils.rand_name(name='ObjectTemp')
- cls.container_client.create_container(cls.container_name)
- cls.containers = [cls.container_name]
-
cls.key = 'Meta'
cls.metadata = {'Temp-URL-Key': cls.key}
cls.account_client.create_account_metadata(metadata=cls.metadata)
@@ -57,7 +54,7 @@
@classmethod
def resource_cleanup(cls):
cls.account_client.delete_account_metadata(metadata=cls.metadata)
- cls.delete_containers(cls.containers)
+ cls.delete_containers()
super(ObjectFormPostNegativeTest, cls).resource_cleanup()
def get_multipart_form(self, expires=600):
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index a88e4f4..a707ebb 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -35,24 +35,13 @@
@classmethod
def resource_setup(cls):
super(ObjectTest, cls).resource_setup()
- cls.container_name = data_utils.rand_name(name='TestContainer')
- cls.container_client.create_container(cls.container_name)
- cls.containers = [cls.container_name]
+ cls.container_name = cls.create_container()
@classmethod
def resource_cleanup(cls):
- cls.delete_containers(cls.containers)
+ cls.delete_containers()
super(ObjectTest, cls).resource_cleanup()
- def _create_object(self, metadata=None):
- # setup object
- object_name = data_utils.rand_name(name='TestObject')
- data = data_utils.arbitrary_string()
- self.object_client.create_object(self.container_name,
- object_name, data, metadata=metadata)
-
- return object_name, data
-
def _upload_segments(self):
# create object
object_name = data_utils.rand_name(name='LObject')
@@ -335,7 +324,7 @@
@test.idempotent_id('7a94c25d-66e6-434c-9c38-97d4e2c29945')
def test_update_object_metadata(self):
# update object metadata
- object_name, data = self._create_object()
+ object_name, _ = self.create_object(self.container_name)
metadata = {'X-Object-Meta-test-meta': 'Meta'}
resp, _ = self.object_client.update_object_metadata(
@@ -431,8 +420,8 @@
@test.idempotent_id('0dbbe89c-6811-4d84-a2df-eca2bdd40c0e')
def test_update_object_metadata_with_x_object_metakey(self):
- # update object metadata with a blenk value of metadata
- object_name, data = self._create_object()
+ # update object metadata with a blank value of metadata
+ object_name, _ = self.create_object(self.container_name)
update_metadata = {'X-Object-Meta-test-meta': ''}
resp, _ = self.object_client.update_object_metadata(
@@ -494,7 +483,7 @@
@test.idempotent_id('170fb90e-f5c3-4b1f-ae1b-a18810821172')
def test_list_no_object_metadata(self):
# get empty list of object metadata
- object_name, data = self._create_object()
+ object_name, _ = self.create_object(self.container_name)
resp, _ = self.object_client.list_object_metadata(
self.container_name,
@@ -548,7 +537,7 @@
# retrieve object's data (in response body)
# create object
- object_name, data = self._create_object()
+ object_name, data = self.create_object(self.container_name)
# get object
resp, body = self.object_client.get_object(self.container_name,
object_name)
@@ -701,7 +690,7 @@
@test.idempotent_id('0aa1201c-10aa-467a-bee7-63cbdd463152')
def test_get_object_with_if_unmodified_since(self):
# get object with if_unmodified_since
- object_name, data = self._create_object()
+ object_name, data = self.create_object(self.container_name)
time_now = time.time()
http_date = time.ctime(time_now + 86400)
@@ -716,7 +705,7 @@
@test.idempotent_id('94587078-475f-48f9-a40f-389c246e31cd')
def test_get_object_with_x_newest(self):
# get object with x_newest
- object_name, data = self._create_object()
+ object_name, data = self.create_object(self.container_name)
list_metadata = {'X-Newest': 'true'}
resp, body = self.object_client.get_object(
@@ -757,7 +746,7 @@
# change the content type of an existing object
# create object
- object_name, data = self._create_object()
+ object_name, _ = self.create_object(self.container_name)
# get the old content type
resp_tmp, _ = self.object_client.list_object_metadata(
self.container_name, object_name)
@@ -843,7 +832,8 @@
def test_copy_object_with_x_fresh_metadata(self):
# create source object
metadata = {'x-object-meta-src': 'src_value'}
- src_object_name, data = self._create_object(metadata)
+ src_object_name, data = self.create_object(self.container_name,
+ metadata=metadata)
# copy source object with x_fresh_metadata header
metadata = {'X-Fresh-Metadata': 'true'}
@@ -863,7 +853,8 @@
def test_copy_object_with_x_object_metakey(self):
# create source object
metadata = {'x-object-meta-src': 'src_value'}
- src_obj_name, data = self._create_object(metadata)
+ src_obj_name, data = self.create_object(self.container_name,
+ metadata=metadata)
# copy source object to destination with x-object-meta-key
metadata = {'x-object-meta-test': ''}
@@ -885,7 +876,8 @@
def test_copy_object_with_x_object_meta(self):
# create source object
metadata = {'x-object-meta-src': 'src_value'}
- src_obj_name, data = self._create_object(metadata)
+ src_obj_name, data = self.create_object(self.container_name,
+ metadata=metadata)
# copy source object to destination with object metadata
metadata = {'x-object-meta-test': 'value'}
@@ -951,7 +943,7 @@
# Make a conditional request for an object using the If-None-Match
# header, it should get downloaded only if the local file is different,
# otherwise the response code should be 304 Not Modified
- object_name, data = self._create_object()
+ object_name, data = self.create_object(self.container_name)
# local copy is identical, no download
md5 = hashlib.md5(data).hexdigest()
headers = {'If-None-Match': md5}
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 867159b..e00bbab 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -30,8 +30,7 @@
def setUp(self):
super(ObjectSloTest, self).setUp()
- self.container_name = data_utils.rand_name(name='TestContainer')
- self.container_client.create_container(self.container_name)
+ self.container_name = self.create_container()
self.objects = []
def tearDown(self):
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 3d28f6e..c2d3b69 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -32,9 +32,7 @@
def resource_setup(cls):
super(ObjectTempUrlTest, cls).resource_setup()
# create a container
- cls.container_name = data_utils.rand_name(name='TestContainer')
- cls.container_client.create_container(cls.container_name)
- cls.containers = [cls.container_name]
+ cls.container_name = cls.create_container()
# update account metadata
cls.key = 'Meta'
@@ -44,11 +42,7 @@
cls.account_client.create_account_metadata(metadata=metadata)
# create an object
- cls.object_name = data_utils.rand_name(name='ObjectTemp')
- cls.content = data_utils.arbitrary_string(size=len(cls.object_name),
- base_text=cls.object_name)
- cls.object_client.create_object(cls.container_name,
- cls.object_name, cls.content)
+ cls.object_name, cls.content = cls.create_object(cls.container_name)
@classmethod
def resource_cleanup(cls):
@@ -56,7 +50,7 @@
cls.account_client.delete_account_metadata(
metadata=metadata)
- cls.delete_containers(cls.containers)
+ cls.delete_containers()
super(ObjectTempUrlTest, cls).resource_cleanup()
diff --git a/tempest/api/object_storage/test_object_temp_url_negative.py b/tempest/api/object_storage/test_object_temp_url_negative.py
index 38fe697..577f3bd 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -33,9 +33,7 @@
def resource_setup(cls):
super(ObjectTempUrlNegativeTest, cls).resource_setup()
- cls.container_name = data_utils.rand_name(name='TestContainer')
- cls.container_client.create_container(cls.container_name)
- cls.containers = [cls.container_name]
+ cls.container_name = cls.create_container()
# update account metadata
cls.key = 'Meta'
@@ -49,7 +47,7 @@
resp, _ = cls.account_client.delete_account_metadata(
metadata=cls.metadata)
- cls.delete_containers(cls.containers)
+ cls.delete_containers()
super(ObjectTempUrlNegativeTest, cls).resource_cleanup()
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index 24ec3f5..3f6623b 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -31,7 +31,7 @@
@classmethod
def resource_cleanup(cls):
- cls.delete_containers(cls.containers)
+ cls.delete_containers()
super(ContainerTest, cls).resource_cleanup()
def assertContainer(self, container, count, byte, versioned):
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index ba17d9c..fe105e8 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -73,8 +73,9 @@
@test.idempotent_id('18c51ae9-cb03-48fc-b234-14a19374dbed')
def test_show_quota_usage(self):
- quota_usage = self.admin_quotas_client.show_quota_usage(
- self.os_adm.credentials.tenant_id)['quota_set']
+ quota_usage = self.admin_quotas_client.show_quota_set(
+ self.os_adm.credentials.tenant_id,
+ params={'usage': True})['quota_set']
for key in QUOTA_KEYS:
self.assertIn(key, quota_usage)
for usage_key in QUOTA_USAGE_KEYS:
@@ -82,15 +83,15 @@
@test.idempotent_id('ae8b6091-48ad-4bfa-a188-bbf5cc02115f')
def test_quota_usage(self):
- quota_usage = self.admin_quotas_client.show_quota_usage(
- self.demo_tenant_id)['quota_set']
+ quota_usage = self.admin_quotas_client.show_quota_set(
+ self.demo_tenant_id, params={'usage': True})['quota_set']
volume = self.create_volume()
self.addCleanup(self.delete_volume,
self.admin_volume_client, volume['id'])
- new_quota_usage = self.admin_quotas_client.show_quota_usage(
- self.demo_tenant_id)['quota_set']
+ new_quota_usage = self.admin_quotas_client.show_quota_set(
+ self.demo_tenant_id, params={'usage': True})['quota_set']
self.assertEqual(quota_usage['volumes']['in_use'] + 1,
new_quota_usage['volumes']['in_use'])
@@ -128,11 +129,11 @@
self.admin_volume_client, volume['id'])
# List of tenants quota usage pre-transfer
- primary_quota = self.admin_quotas_client.show_quota_usage(
- self.demo_tenant_id)['quota_set']
+ primary_quota = self.admin_quotas_client.show_quota_set(
+ self.demo_tenant_id, params={'usage': True})['quota_set']
- alt_quota = self.admin_quotas_client.show_quota_usage(
- self.alt_client.tenant_id)['quota_set']
+ alt_quota = self.admin_quotas_client.show_quota_set(
+ self.alt_client.tenant_id, params={'usage': True})['quota_set']
# Creates a volume transfer
transfer = self.volumes_client.create_volume_transfer(
@@ -149,11 +150,11 @@
self.alt_client, volume['id'], 'available')
# List of tenants quota usage post transfer
- new_primary_quota = self.admin_quotas_client.show_quota_usage(
- self.demo_tenant_id)['quota_set']
+ new_primary_quota = self.admin_quotas_client.show_quota_set(
+ self.demo_tenant_id, params={'usage': True})['quota_set']
- new_alt_quota = self.admin_quotas_client.show_quota_usage(
- self.alt_client.tenant_id)['quota_set']
+ new_alt_quota = self.admin_quotas_client.show_quota_set(
+ self.alt_client.tenant_id, params={'usage': True})['quota_set']
# Verify tenants quota usage was updated
self.assertEqual(primary_quota['volumes']['in_use'] -
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 27f6ccb..8eae085 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -88,18 +88,21 @@
# Create/get volume type.
body = {}
name = data_utils.rand_name("volume-type")
+ description = data_utils.rand_name("volume-type-description")
proto = CONF.volume.storage_protocol
vendor = CONF.volume.vendor_name
extra_specs = {"storage_protocol": proto,
"vendor_name": vendor}
- body = self.create_volume_type(
- name=name,
- extra_specs=extra_specs)
+ body = self.create_volume_type(description=description, name=name,
+ extra_specs=extra_specs)
self.assertIn('id', body)
self.assertIn('name', body)
- self.assertEqual(body['name'], name,
+ self.assertEqual(name, body['name'],
"The created volume_type name is not equal "
"to the requested name")
+ self.assertEqual(description, body['description'],
+ "The created volume_type_description name is "
+ "not equal to the requested name")
self.assertTrue(body['id'] is not None,
"Field volume_type id is empty or not found.")
fetched_volume_type = self.admin_volume_types_client.show_volume_type(
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index b144c7c..ff28b50 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -43,7 +43,7 @@
def _delete_backup(self, backup_id):
self.admin_backups_client.delete_backup(backup_id)
- self.admin_backups_client.wait_for_backup_deletion(backup_id)
+ self.admin_backups_client.wait_for_resource_deletion(backup_id)
def _decode_url(self, backup_url):
return json.loads(base64.decodestring(backup_url))
@@ -59,44 +59,6 @@
backup.update(changes)
return self._encode_backup(backup)
- @test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
- def test_volume_backup_create_get_detailed_list_restore_delete(self):
- # Create backup
- backup_name = data_utils.rand_name('Backup')
- create_backup = self.admin_backups_client.create_backup
- backup = create_backup(volume_id=self.volume['id'],
- name=backup_name)['backup']
- self.addCleanup(self.admin_backups_client.delete_backup,
- backup['id'])
- self.assertEqual(backup_name, backup['name'])
- waiters.wait_for_volume_status(self.admin_volume_client,
- self.volume['id'], 'available')
- self.admin_backups_client.wait_for_backup_status(backup['id'],
- 'available')
-
- # Get a given backup
- backup = self.admin_backups_client.show_backup(backup['id'])['backup']
- self.assertEqual(backup_name, backup['name'])
-
- # Get all backups with detail
- backups = self.admin_backups_client.list_backups(
- detail=True)['backups']
- self.assertIn((backup['name'], backup['id']),
- [(m['name'], m['id']) for m in backups])
-
- # Restore backup
- restore = self.admin_backups_client.restore_backup(
- backup['id'])['restore']
-
- # Delete backup
- self.addCleanup(self.admin_volume_client.delete_volume,
- restore['volume_id'])
- self.assertEqual(backup['id'], restore['backup_id'])
- self.admin_backups_client.wait_for_backup_status(backup['id'],
- 'available')
- waiters.wait_for_volume_status(self.admin_volume_client,
- restore['volume_id'], 'available')
-
@test.idempotent_id('a99c54a1-dd80-4724-8a13-13bf58d4068d')
def test_volume_backup_export_import(self):
"""Test backup export import functionality.
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 87146db..d83c308 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -36,6 +36,44 @@
cls.volume = cls.create_volume()
+ @test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
+ def test_volume_backup_create_get_detailed_list_restore_delete(self):
+ # Create backup
+ backup_name = data_utils.rand_name('Backup')
+ create_backup = self.backups_client.create_backup
+ backup = create_backup(volume_id=self.volume['id'],
+ name=backup_name)['backup']
+ self.addCleanup(self.backups_client.delete_backup,
+ backup['id'])
+ self.assertEqual(backup_name, backup['name'])
+ waiters.wait_for_volume_status(self.volumes_client,
+ self.volume['id'], 'available')
+ self.backups_client.wait_for_backup_status(backup['id'],
+ 'available')
+
+ # Get a given backup
+ backup = self.backups_client.show_backup(backup['id'])['backup']
+ self.assertEqual(backup_name, backup['name'])
+
+ # Get all backups with detail
+ backups = self.backups_client.list_backups(
+ detail=True)['backups']
+ self.assertIn((backup['name'], backup['id']),
+ [(m['name'], m['id']) for m in backups])
+
+ # Restore backup
+ restore = self.backups_client.restore_backup(
+ backup['id'])['restore']
+
+ # Delete backup
+ self.addCleanup(self.volumes_client.delete_volume,
+ restore['volume_id'])
+ self.assertEqual(backup['id'], restore['backup_id'])
+ self.backups_client.wait_for_backup_status(backup['id'],
+ 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ restore['volume_id'], 'available')
+
@test.idempotent_id('07af8f6d-80af-44c9-a5dc-c8427b1b62e6')
@test.services('compute')
def test_backup_create_attached_volume(self):
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 5117e6c..c501ffc 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -42,22 +42,13 @@
super(VolumesV2ListTestJSON, cls).resource_setup()
# Create 3 test volumes
- cls.volume_list = []
cls.volume_id_list = []
cls.metadata = {'Type': 'work'}
for i in range(3):
volume = cls.create_volume(metadata=cls.metadata)
volume = cls.client.show_volume(volume['id'])['volume']
- cls.volume_list.append(volume)
cls.volume_id_list.append(volume['id'])
- @classmethod
- def resource_cleanup(cls):
- # Delete the created volumes
- for volid in cls.volume_id_list:
- cls.delete_volume(cls.client, volid)
- super(VolumesV2ListTestJSON, cls).resource_cleanup()
-
@test.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
def test_volume_list_details_with_multiple_params(self):
# List volumes detail using combined condition
@@ -174,9 +165,9 @@
# If cannot follow make sure it's because we have finished
else:
- self.assertListEqual([], remaining or [],
- 'No more pages reported, but still '
- 'missing ids %s' % remaining)
+ self.assertEqual([], remaining or [],
+ 'No more pages reported, but still '
+ 'missing ids %s' % remaining)
break
@test.idempotent_id('e9138a2c-f67b-4796-8efa-635c196d01de')
diff --git a/tempest/clients.py b/tempest/clients.py
index e070637..e14f6f8 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -14,15 +14,13 @@
# under the License.
import copy
-
from oslo_log import log as logging
-
from tempest.common import negative_rest_client
from tempest import config
from tempest import exceptions
from tempest.lib import auth
from tempest.lib import exceptions as lib_exc
-from tempest import service_clients
+from tempest.lib.services import clients
from tempest.services import baremetal
from tempest.services import data_processing
from tempest.services import identity
@@ -34,7 +32,7 @@
LOG = logging.getLogger(__name__)
-class Manager(service_clients.ServiceClients):
+class Manager(clients.ServiceClients):
"""Top level manager for OpenStack tempest clients"""
default_params = config.service_client_config()
@@ -99,17 +97,16 @@
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
+ # NOTE(andreaf) Once all service clients in Tempest are migrated
+ # to tempest.lib, their configuration will be picked up from the
+ # registry, and this method will become redundant.
configuration = {}
- # Setup the parameters for all Tempest services.
+ # Setup the parameters for all Tempest services which are not in lib.
# 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():
+ for service in clients._tempest_internal_modules():
try:
# NOTE(andreaf) Use the unversioned service name to fetch
# the configuration since configuration is not versioned.
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 426571f..9758061 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -349,7 +349,8 @@
LOG.exception("Delete Volume Quotas exception.")
def dry_run(self):
- quotas = self.client.show_quota_usage(self.tenant_id)['quota_set']
+ quotas = self.client.show_quota_set(
+ self.tenant_id, params={'usage': True})['quota_set']
self.data['volume_quotas'] = quotas
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index e3788ab..bf7289a 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -89,18 +89,28 @@
with open(testr_conf_path, 'w+') as testr_conf_file:
testr_conf_file.write(testr_conf)
- def update_local_conf(self, conf_path, lock_dir, log_dir):
+ def get_configparser(self, conf_path):
config_parse = moves.configparser.SafeConfigParser()
config_parse.optionxform = str
- with open(conf_path, 'a+') as conf_file:
- # Set local lock_dir in tempest conf
- if not config_parse.has_section('oslo_concurrency'):
- config_parse.add_section('oslo_concurrency')
- config_parse.set('oslo_concurrency', 'lock_path', lock_dir)
- # Set local log_dir in tempest conf
- config_parse.set('DEFAULT', 'log_dir', log_dir)
- # Set default log filename to tempest.log
- config_parse.set('DEFAULT', 'log_file', 'tempest.log')
+ # get any existing values if a config file already exists
+ if os.path.isfile(conf_path):
+ # use read() for Python 2 and 3 compatibility
+ config_parse.read(conf_path)
+ return config_parse
+
+ def update_local_conf(self, conf_path, lock_dir, log_dir):
+ config_parse = self.get_configparser(conf_path)
+ # Set local lock_dir in tempest conf
+ if not config_parse.has_section('oslo_concurrency'):
+ config_parse.add_section('oslo_concurrency')
+ config_parse.set('oslo_concurrency', 'lock_path', lock_dir)
+ # Set local log_dir in tempest conf
+ config_parse.set('DEFAULT', 'log_dir', log_dir)
+ # Set default log filename to tempest.log
+ config_parse.set('DEFAULT', 'log_file', 'tempest.log')
+
+ # write out a new file with the updated configurations
+ with open(conf_path, 'w+') as conf_file:
config_parse.write(conf_file)
def copy_config(self, etc_dir, config_dir):
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
deleted file mode 100755
index a9e5167..0000000
--- a/tempest/cmd/javelin.py
+++ /dev/null
@@ -1,1131 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-"""Javelin is a tool for creating, verifying, and deleting a small set of
-resources in a declarative way.
-
-Javelin is meant to be used as a way to validate quickly that resources can
-survive an upgrade process.
-
-Authentication
---------------
-
-Javelin will be creating (and removing) users and tenants so it needs the admin
-credentials of your cloud to operate properly. The corresponding info can be
-given the usual way, either through CLI options or environment variables.
-
-You're probably familiar with these, but just in case::
-
- +----------+------------------+----------------------+
- | Param | CLI | Environment Variable |
- +----------+------------------+----------------------+
- | Username | --os-username | OS_USERNAME |
- | Password | --os-password | OS_PASSWORD |
- | Tenant | --os-tenant-name | OS_TENANT_NAME |
- +----------+------------------+----------------------+
-
-
-Runtime Arguments
------------------
-
-**-m/--mode**: (Required) Has to be one of 'check', 'create' or 'destroy'. It
-indicates which actions javelin is going to perform.
-
-**-r/--resources**: (Required) The path to a YAML file describing the resources
-used by Javelin.
-
-**-d/--devstack-base**: (Required) The path to the devstack repo used to
-retrieve artefacts (like images) that will be referenced in the resource files.
-
-**-c/--config-file**: (Optional) The path to a valid Tempest config file
-describing your cloud. Javelin may use this to determine if certain services
-are enabled and modify its behavior accordingly.
-
-
-Resource file
--------------
-
-The resource file is a valid YAML file describing the resources that will be
-created, checked and destroyed by javelin. Here's a canonical example of a
-resource file::
-
- tenants:
- - javelin
- - discuss
-
- users:
- - name: javelin
- pass: gungnir
- tenant: javelin
- - name: javelin2
- pass: gungnir2
- tenant: discuss
-
- # resources that we want to create
- images:
- - name: javelin_cirros
- owner: javelin
- file: cirros-0.3.2-x86_64-blank.img
- disk_format: ami
- container_format: ami
- aki: cirros-0.3.2-x86_64-vmlinuz
- ari: cirros-0.3.2-x86_64-initrd
-
- servers:
- - name: peltast
- owner: javelin
- flavor: m1.small
- image: javelin_cirros
- floating_ip_pool: public
- - name: hoplite
- owner: javelin
- flavor: m1.medium
- image: javelin_cirros
-
-
-An important piece of the resource definition is the *owner* field, which is
-the user (that we've created) that is the owner of that resource. All
-operations on that resource will happen as that regular user to ensure that
-admin level access does not mask issues.
-
-The check phase will act like a unit test, using well known assert methods to
-verify that the correct resources exist.
-
-"""
-
-import argparse
-import collections
-import datetime
-import os
-import sys
-import unittest
-
-import netaddr
-from oslo_log import log as logging
-import six
-import yaml
-
-from tempest.common import identity
-from tempest.common import waiters
-from tempest import config
-from tempest.lib import auth
-from tempest.lib import exceptions as lib_exc
-from tempest.lib.services.compute import flavors_client
-from tempest.lib.services.compute import floating_ips_client
-from tempest.lib.services.compute import security_group_rules_client
-from tempest.lib.services.compute import security_groups_client
-from tempest.lib.services.compute import servers_client
-from tempest.lib.services.identity.v2 import roles_client
-from tempest.lib.services.identity.v2 import tenants_client
-from tempest.lib.services.identity.v2 import users_client
-from tempest.lib.services.image.v2 import images_client
-from tempest.lib.services.network import networks_client
-from tempest.lib.services.network import ports_client
-from tempest.lib.services.network import routers_client
-from tempest.lib.services.network import subnets_client
-from tempest.services.identity.v2.json import identity_client
-from tempest.services.object_storage import container_client
-from tempest.services.object_storage import object_client
-from tempest.services.volume.v1.json import volumes_client
-
-CONF = config.CONF
-OPTS = {}
-USERS = {}
-RES = collections.defaultdict(list)
-
-LOG = None
-
-JAVELIN_START = datetime.datetime.utcnow()
-
-
-class OSClient(object):
- _creds = None
- identity = None
- servers = None
-
- def __init__(self, user, pw, tenant):
- 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_with_timeout_values = {
- 'build_interval': CONF.compute.build_interval,
- 'build_timeout': CONF.compute.build_timeout
- }
- default_params_with_timeout_values.update(default_params)
-
- compute_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
- }
- compute_params.update(default_params)
-
- object_storage_params = {
- 'service': CONF.object_storage.catalog_type,
- 'region': CONF.object_storage.region or CONF.identity.region,
- 'endpoint_type': CONF.object_storage.endpoint_type
- }
- object_storage_params.update(default_params)
-
- _creds = auth.KeystoneV2Credentials(
- username=user,
- password=pw,
- tenant_name=tenant)
- auth_provider_params = {
- 'disable_ssl_certificate_validation':
- CONF.identity.disable_ssl_certificate_validation,
- 'ca_certs': CONF.identity.ca_certificates_file,
- 'trace_requests': CONF.debug.trace_requests
- }
- _auth = auth.KeystoneV2AuthProvider(
- _creds, CONF.identity.uri, **auth_provider_params)
- self.identity = identity_client.IdentityClient(
- _auth,
- CONF.identity.catalog_type,
- CONF.identity.region,
- endpoint_type='adminURL',
- **default_params_with_timeout_values)
- self.tenants = tenants_client.TenantsClient(
- _auth,
- CONF.identity.catalog_type,
- CONF.identity.region,
- endpoint_type='adminURL',
- **default_params_with_timeout_values)
- self.roles = roles_client.RolesClient(
- _auth,
- CONF.identity.catalog_type,
- CONF.identity.region,
- endpoint_type='adminURL',
- **default_params_with_timeout_values)
- self.users = users_client.UsersClient(
- _auth,
- CONF.identity.catalog_type,
- CONF.identity.region,
- endpoint_type='adminURL',
- **default_params_with_timeout_values)
- self.servers = servers_client.ServersClient(_auth,
- **compute_params)
- self.flavors = flavors_client.FlavorsClient(_auth,
- **compute_params)
- self.floating_ips = floating_ips_client.FloatingIPsClient(
- _auth, **compute_params)
- self.secgroups = security_groups_client.SecurityGroupsClient(
- _auth, **compute_params)
- self.secrules = security_group_rules_client.SecurityGroupRulesClient(
- _auth, **compute_params)
- self.objects = object_client.ObjectClient(_auth,
- **object_storage_params)
- self.containers = container_client.ContainerClient(
- _auth, **object_storage_params)
- self.images = images_client.ImagesClient(
- _auth,
- CONF.image.catalog_type,
- 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,
- **default_params)
- self.volumes = volumes_client.VolumesClient(
- _auth,
- CONF.volume.catalog_type,
- 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,
- **default_params)
- self.networks = networks_client.NetworksClient(
- _auth,
- CONF.network.catalog_type,
- 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,
- **default_params)
- self.ports = ports_client.PortsClient(
- _auth,
- CONF.network.catalog_type,
- 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,
- **default_params)
- self.routers = routers_client.RoutersClient(
- _auth,
- CONF.network.catalog_type,
- 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,
- **default_params)
- self.subnets = subnets_client.SubnetsClient(
- _auth,
- CONF.network.catalog_type,
- 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,
- **default_params)
-
-
-def load_resources(fname):
- """Load the expected resources from a yaml file."""
- return yaml.load(open(fname, 'r'))
-
-
-def keystone_admin():
- return OSClient(OPTS.os_username, OPTS.os_password, OPTS.os_tenant_name)
-
-
-def client_for_user(name):
- LOG.debug("Entering client_for_user")
- if name in USERS:
- user = USERS[name]
- LOG.debug("Created client for user %s" % user)
- return OSClient(user['name'], user['pass'], user['tenant'])
- else:
- LOG.error("%s not found in USERS: %s" % (name, USERS))
-
-
-###################
-#
-# TENANTS
-#
-###################
-
-
-def create_tenants(tenants):
- """Create tenants from resource definition.
-
- Don't create the tenants if they already exist.
- """
- admin = keystone_admin()
- body = admin.tenants.list_tenants()['tenants']
- existing = [x['name'] for x in body]
- for tenant in tenants:
- if tenant not in existing:
- admin.tenants.create_tenant(name=tenant)['tenant']
- else:
- LOG.warning("Tenant '%s' already exists in this environment"
- % tenant)
-
-
-def destroy_tenants(tenants):
- admin = keystone_admin()
- for tenant in tenants:
- tenant_id = identity.get_tenant_by_name(admin.tenant, tenant)['id']
- admin.tenants.delete_tenant(tenant_id)
-
-##############
-#
-# USERS
-#
-##############
-
-
-def _users_for_tenant(users, tenant):
- u_for_t = []
- for user in users:
- for n in user:
- if user[n]['tenant'] == tenant:
- u_for_t.append(user[n])
- return u_for_t
-
-
-def _tenants_from_users(users):
- tenants = set()
- for user in users:
- for n in user:
- tenants.add(user[n]['tenant'])
- return tenants
-
-
-def _assign_swift_role(user, swift_role):
- admin = keystone_admin()
- roles = admin.roles.list_roles()
- role = next(r for r in roles if r['name'] == swift_role)
- LOG.debug(USERS[user])
- try:
- admin.roles.create_user_role_on_project(
- USERS[user]['tenant_id'],
- USERS[user]['id'],
- role['id'])
- except lib_exc.Conflict:
- # don't care if it's already assigned
- pass
-
-
-def create_users(users):
- """Create tenants from resource definition.
-
- Don't create the tenants if they already exist.
- """
- global USERS
- LOG.info("Creating users")
- admin = keystone_admin()
- for u in users:
- try:
- tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
- except lib_exc.NotFound:
- LOG.error("Tenant: %s - not found" % u['tenant'])
- continue
- try:
- identity.get_user_by_username(admin.tenants,
- tenant['id'], u['name'])
- LOG.warning("User '%s' already exists in this environment"
- % u['name'])
- except lib_exc.NotFound:
- admin.users.create_user(
- name=u['name'], password=u['pass'],
- tenantId=tenant['id'],
- email="%s@%s" % (u['name'], tenant['id']),
- enabled=True)
-
-
-def destroy_users(users):
- admin = keystone_admin()
- for user in users:
- tenant_id = identity.get_tenant_by_name(admin.tenants,
- user['tenant'])['id']
- user_id = identity.get_user_by_username(admin.tenants,
- tenant_id, user['name'])['id']
- admin.users.delete_user(user_id)
-
-
-def collect_users(users):
- global USERS
- LOG.info("Collecting users")
- admin = keystone_admin()
- for u in users:
- tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
- u['tenant_id'] = tenant['id']
- USERS[u['name']] = u
- body = identity.get_user_by_username(admin.tenants,
- tenant['id'], u['name'])
- USERS[u['name']]['id'] = body['id']
-
-
-class JavelinCheck(unittest.TestCase):
- def __init__(self, users, resources):
- super(JavelinCheck, self).__init__()
- self.users = users
- self.res = resources
-
- def runTest(self, *args):
- pass
-
- def _ping_ip(self, ip_addr, count, namespace=None):
- if namespace is None:
- ping_cmd = "ping -c1 " + ip_addr
- else:
- ping_cmd = "sudo ip netns exec %s ping -c1 %s" % (namespace,
- ip_addr)
- for current in range(count):
- return_code = os.system(ping_cmd)
- if return_code is 0:
- break
- self.assertNotEqual(current, count - 1,
- "Server is not pingable at %s" % ip_addr)
-
- def check(self):
- self.check_users()
- self.check_objects()
- self.check_servers()
- self.check_volumes()
- self.check_secgroups()
-
- # validate neutron is enabled and ironic disabled:
- # Tenant network isolation is not supported when using ironic.
- # "admin" has set up a neutron flat network environment within a shared
- # fixed network for all tenants to use.
- # In this case, network/subnet/router creation can be skipped and the
- # server booted the same as nova network.
- if (CONF.service_available.neutron and
- not CONF.baremetal.driver_enabled):
- self.check_networking()
-
- def check_users(self):
- """Check that the users we expect to exist, do.
-
- We don't use the resource list for this because we need to validate
- that things like tenantId didn't drift across versions.
- """
- LOG.info("checking users")
- for name, user in six.iteritems(self.users):
- client = keystone_admin()
- found = client.users.show_user(user['id'])['user']
- self.assertEqual(found['name'], user['name'])
- self.assertEqual(found['tenantId'], user['tenant_id'])
-
- # also ensure we can auth with that user, and do something
- # on the cloud. We don't care about the results except that it
- # remains authorized.
- client = client_for_user(user['name'])
- client.servers.list_servers()
-
- def check_objects(self):
- """Check that the objects created are still there."""
- if not self.res.get('objects'):
- return
- LOG.info("checking objects")
- for obj in self.res['objects']:
- client = client_for_user(obj['owner'])
- r, contents = client.objects.get_object(
- obj['container'], obj['name'])
- source = _file_contents(obj['file'])
- self.assertEqual(contents, source)
-
- def check_servers(self):
- """Check that the servers are still up and running."""
- if not self.res.get('servers'):
- return
- LOG.info("checking servers")
- for server in self.res['servers']:
- client = client_for_user(server['owner'])
- found = _get_server_by_name(client, server['name'])
- self.assertIsNotNone(
- found,
- "Couldn't find expected server %s" % server['name'])
-
- found = client.servers.show_server(found['id'])['server']
- # validate neutron is enabled and ironic disabled:
- if (CONF.service_available.neutron and
- not CONF.baremetal.driver_enabled):
- _floating_is_alive = False
- for network_name, body in found['addresses'].items():
- for addr in body:
- ip = addr['addr']
- # Use floating IP, fixed IP or other type to
- # reach the server.
- # This is useful in multi-node environment.
- if CONF.validation.connect_method == 'floating':
- if addr.get('OS-EXT-IPS:type',
- 'floating') == 'floating':
- self._ping_ip(ip, 60)
- _floating_is_alive = True
- elif CONF.validation.connect_method == 'fixed':
- if addr.get('OS-EXT-IPS:type',
- 'fixed') == 'fixed':
- namespace = _get_router_namespace(client,
- network_name)
- self._ping_ip(ip, 60, namespace)
- else:
- self._ping_ip(ip, 60)
- # If CONF.validation.connect_method is floating, validate
- # that the floating IP is attached to the server and the
- # the server is pingable.
- if CONF.validation.connect_method == 'floating':
- self.assertTrue(_floating_is_alive,
- "Server %s has no floating IP." %
- server['name'])
- else:
- addr = found['addresses']['private'][0]['addr']
- self._ping_ip(addr, 60)
-
- def check_secgroups(self):
- """Check that the security groups still exist."""
- LOG.info("Checking security groups")
- for secgroup in self.res['secgroups']:
- client = client_for_user(secgroup['owner'])
- found = _get_resource_by_name(client.secgroups, 'security_groups',
- secgroup['name'])
- self.assertIsNotNone(
- found,
- "Couldn't find expected secgroup %s" % secgroup['name'])
-
- def check_volumes(self):
- """Check that the volumes are still there and attached."""
- if not self.res.get('volumes'):
- return
- LOG.info("checking volumes")
- for volume in self.res['volumes']:
- client = client_for_user(volume['owner'])
- vol_body = _get_volume_by_name(client, volume['name'])
- self.assertIsNotNone(
- vol_body,
- "Couldn't find expected volume %s" % volume['name'])
-
- # Verify that a volume's attachment retrieved
- server_id = _get_server_by_name(client, volume['server'])['id']
- attachment = client.volumes.get_attachment_from_volume(vol_body)
- self.assertEqual(vol_body['id'], attachment['volume_id'])
- self.assertEqual(server_id, attachment['server_id'])
-
- def check_networking(self):
- """Check that the networks are still there."""
- for res_type in ('networks', 'subnets', 'routers'):
- for res in self.res[res_type]:
- client = client_for_user(res['owner'])
- found = _get_resource_by_name(client.networks, res_type,
- res['name'])
- self.assertIsNotNone(
- found,
- "Couldn't find expected resource %s" % res['name'])
-
-
-#######################
-#
-# OBJECTS
-#
-#######################
-
-
-def _file_contents(fname):
- with open(fname, 'r') as f:
- return f.read()
-
-
-def create_objects(objects):
- if not objects:
- return
- LOG.info("Creating objects")
- for obj in objects:
- LOG.debug("Object %s" % obj)
- swift_role = obj.get('swift_role', 'Member')
- _assign_swift_role(obj['owner'], swift_role)
- client = client_for_user(obj['owner'])
- client.containers.create_container(obj['container'])
- client.objects.create_object(
- obj['container'], obj['name'],
- _file_contents(obj['file']))
-
-
-def destroy_objects(objects):
- for obj in objects:
- client = client_for_user(obj['owner'])
- r, body = client.objects.delete_object(obj['container'], obj['name'])
- if not (200 <= int(r['status']) < 299):
- raise ValueError("unable to destroy object: [%s] %s" % (r, body))
-
-
-#######################
-#
-# IMAGES
-#
-#######################
-
-
-def _resolve_image(image, imgtype):
- name = image[imgtype]
- fname = os.path.join(OPTS.devstack_base, image['imgdir'], name)
- return name, fname
-
-
-def _get_image_by_name(client, name):
- body = client.images.list_images()
- for image in body:
- if name == image['name']:
- return image
- return None
-
-
-def create_images(images):
- if not images:
- return
- LOG.info("Creating images")
- for image in images:
- client = client_for_user(image['owner'])
-
- # DEPRECATED: 'format' was used for ami images
- # Use 'disk_format' and 'container_format' instead
- if 'format' in image:
- LOG.warning("Deprecated: 'format' is deprecated for images "
- "description. Please use 'disk_format' and 'container_"
- "format' instead.")
- image['disk_format'] = image['format']
- image['container_format'] = image['format']
-
- # only upload a new image if the name isn't there
- if _get_image_by_name(client, image['name']):
- LOG.info("Image '%s' already exists" % image['name'])
- continue
-
- # special handling for 3 part image
- extras = {}
- if image['disk_format'] == 'ami':
- name, fname = _resolve_image(image, 'aki')
- aki = client.images.create_image(
- 'javelin_' + name, 'aki', 'aki')
- client.images.store_image_file(aki.get('id'), open(fname, 'r'))
- extras['kernel_id'] = aki.get('id')
-
- name, fname = _resolve_image(image, 'ari')
- ari = client.images.create_image(
- 'javelin_' + name, 'ari', 'ari')
- client.images.store_image_file(ari.get('id'), open(fname, 'r'))
- extras['ramdisk_id'] = ari.get('id')
-
- _, fname = _resolve_image(image, 'file')
- body = client.images.create_image(
- image['name'], image['container_format'],
- image['disk_format'], **extras)
- image_id = body.get('id')
- client.images.store_image_file(image_id, open(fname, 'r'))
-
-
-def destroy_images(images):
- if not images:
- return
- LOG.info("Destroying images")
- for image in images:
- client = client_for_user(image['owner'])
-
- response = _get_image_by_name(client, image['name'])
- if not response:
- LOG.info("Image '%s' does not exist" % image['name'])
- continue
- client.images.delete_image(response['id'])
-
-
-#######################
-#
-# NETWORKS
-#
-#######################
-
-def _get_router_namespace(client, network):
- network_id = _get_resource_by_name(client.networks,
- 'networks', network)['id']
- n_body = client.routers.list_routers()
- for router in n_body['routers']:
- router_id = router['id']
- r_body = client.ports.list_ports(device_id=router_id)
- for port in r_body['ports']:
- if port['network_id'] == network_id:
- return "qrouter-%s" % router_id
-
-
-def _get_resource_by_name(client, resource, name):
- get_resources = getattr(client, 'list_%s' % resource)
- if get_resources is None:
- raise AttributeError("client doesn't have method list_%s" % resource)
- # Until all tempest client methods are changed to return only one value,
- # we cannot assume they all have the same signature so we need to discard
- # the unused response first value it two values are being returned.
- body = get_resources()
- if isinstance(body, tuple):
- body = body[1]
- if isinstance(body, dict):
- body = body[resource]
- for res in body:
- if name == res['name']:
- return res
- raise ValueError('%s not found in %s resources' % (name, resource))
-
-
-def create_networks(networks):
- LOG.info("Creating networks")
- for network in networks:
- client = client_for_user(network['owner'])
-
- # only create a network if the name isn't here
- body = client.networks.list_networks()
- if any(item['name'] == network['name'] for item in body['networks']):
- LOG.warning("Duplicated network name: %s" % network['name'])
- continue
-
- client.networks.create_network(name=network['name'])
-
-
-def destroy_networks(networks):
- LOG.info("Destroying subnets")
- for network in networks:
- client = client_for_user(network['owner'])
- network_id = _get_resource_by_name(client.networks, 'networks',
- network['name'])['id']
- client.networks.delete_network(network_id)
-
-
-def create_subnets(subnets):
- LOG.info("Creating subnets")
- for subnet in subnets:
- client = client_for_user(subnet['owner'])
-
- network = _get_resource_by_name(client.networks, 'networks',
- subnet['network'])
- ip_version = netaddr.IPNetwork(subnet['range']).version
- # ensure we don't overlap with another subnet in the network
- try:
- client.networks.create_subnet(network_id=network['id'],
- cidr=subnet['range'],
- name=subnet['name'],
- ip_version=ip_version)
- except lib_exc.BadRequest as e:
- is_overlapping_cidr = 'overlaps with another subnet' in str(e)
- if not is_overlapping_cidr:
- raise
-
-
-def destroy_subnets(subnets):
- LOG.info("Destroying subnets")
- for subnet in subnets:
- client = client_for_user(subnet['owner'])
- subnet_id = _get_resource_by_name(client.subnets,
- 'subnets', subnet['name'])['id']
- client.subnets.delete_subnet(subnet_id)
-
-
-def create_routers(routers):
- LOG.info("Creating routers")
- for router in routers:
- client = client_for_user(router['owner'])
-
- # only create a router if the name isn't here
- body = client.routers.list_routers()
- if any(item['name'] == router['name'] for item in body['routers']):
- LOG.warning("Duplicated router name: %s" % router['name'])
- continue
-
- client.networks.create_router(name=router['name'])
-
-
-def destroy_routers(routers):
- LOG.info("Destroying routers")
- for router in routers:
- client = client_for_user(router['owner'])
- router_id = _get_resource_by_name(client.networks,
- 'routers', router['name'])['id']
- for subnet in router['subnet']:
- subnet_id = _get_resource_by_name(client.networks,
- 'subnets', subnet)['id']
- client.routers.remove_router_interface(router_id,
- subnet_id=subnet_id)
- client.routers.delete_router(router_id)
-
-
-def add_router_interface(routers):
- for router in routers:
- client = client_for_user(router['owner'])
- router_id = _get_resource_by_name(client.networks,
- 'routers', router['name'])['id']
-
- for subnet in router['subnet']:
- subnet_id = _get_resource_by_name(client.networks,
- 'subnets', subnet)['id']
- # connect routers to their subnets
- client.routers.add_router_interface(router_id,
- subnet_id=subnet_id)
- # connect routers to external network if set to "gateway"
- if router['gateway']:
- if CONF.network.public_network_id:
- ext_net = CONF.network.public_network_id
- client.routers.update_router(
- router_id, set_enable_snat=True,
- external_gateway_info={"network_id": ext_net})
- else:
- raise ValueError('public_network_id is not configured.')
-
-
-#######################
-#
-# SERVERS
-#
-#######################
-
-def _get_server_by_name(client, name):
- body = client.servers.list_servers()
- for server in body['servers']:
- if name == server['name']:
- return server
- return None
-
-
-def _get_flavor_by_name(client, name):
- body = client.flavors.list_flavors()['flavors']
- for flavor in body:
- if name == flavor['name']:
- return flavor
- return None
-
-
-def create_servers(servers):
- if not servers:
- return
- LOG.info("Creating servers")
- for server in servers:
- client = client_for_user(server['owner'])
-
- if _get_server_by_name(client, server['name']):
- LOG.info("Server '%s' already exists" % server['name'])
- continue
-
- image_id = _get_image_by_name(client, server['image'])['id']
- flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
- # validate neutron is enabled and ironic disabled
- kwargs = dict()
- if (CONF.service_available.neutron and
- not CONF.baremetal.driver_enabled and server.get('networks')):
- get_net_id = lambda x: (_get_resource_by_name(
- client.networks, 'networks', x)['id'])
- kwargs['networks'] = [{'uuid': get_net_id(network)}
- for network in server['networks']]
- body = client.servers.create_server(
- name=server['name'], imageRef=image_id, flavorRef=flavor_id,
- **kwargs)['server']
- server_id = body['id']
- client.servers.wait_for_server_status(server_id, 'ACTIVE')
- # create security group(s) after server spawning
- for secgroup in server['secgroups']:
- client.servers.add_security_group(server_id, name=secgroup)
- if CONF.validation.connect_method == 'floating':
- floating_ip_pool = server.get('floating_ip_pool')
- floating_ip = client.floating_ips.create_floating_ip(
- pool_name=floating_ip_pool)['floating_ip']
- client.floating_ips.associate_floating_ip_to_server(
- floating_ip['ip'], server_id)
-
-
-def destroy_servers(servers):
- if not servers:
- return
- LOG.info("Destroying servers")
- for server in servers:
- client = client_for_user(server['owner'])
-
- response = _get_server_by_name(client, server['name'])
- if not response:
- LOG.info("Server '%s' does not exist" % server['name'])
- continue
-
- # TODO(EmilienM): disassociate floating IP from server and release it.
- client.servers.delete_server(response['id'])
- waiters.wait_for_server_termination(client.servers, response['id'],
- ignore_error=True)
-
-
-def create_secgroups(secgroups):
- LOG.info("Creating security groups")
- for secgroup in secgroups:
- client = client_for_user(secgroup['owner'])
-
- # only create a security group if the name isn't here
- # i.e. a security group may be used by another server
- # only create a router if the name isn't here
- body = client.secgroups.list_security_groups()['security_groups']
- if any(item['name'] == secgroup['name'] for item in body):
- LOG.warning("Security group '%s' already exists" %
- secgroup['name'])
- continue
-
- body = client.secgroups.create_security_group(
- name=secgroup['name'],
- description=secgroup['description'])['security_group']
- secgroup_id = body['id']
- # for each security group, create the rules
- for rule in secgroup['rules']:
- ip_proto, from_port, to_port, cidr = rule.split()
- client.secrules.create_security_group_rule(
- parent_group_id=secgroup_id, ip_protocol=ip_proto,
- from_port=from_port, to_port=to_port, cidr=cidr)
-
-
-def destroy_secgroups(secgroups):
- LOG.info("Destroying security groups")
- for secgroup in secgroups:
- client = client_for_user(secgroup['owner'])
- sg_id = _get_resource_by_name(client.secgroups,
- 'security_groups',
- secgroup['name'])
- # sg rules are deleted automatically
- client.secgroups.delete_security_group(sg_id['id'])
-
-
-#######################
-#
-# VOLUMES
-#
-#######################
-
-def _get_volume_by_name(client, name):
- body = client.volumes.list_volumes()['volumes']
- for volume in body:
- if name == volume['display_name']:
- return volume
- return None
-
-
-def create_volumes(volumes):
- if not volumes:
- return
- LOG.info("Creating volumes")
- for volume in volumes:
- client = client_for_user(volume['owner'])
-
- # only create a volume if the name isn't here
- if _get_volume_by_name(client, volume['name']):
- LOG.info("volume '%s' already exists" % volume['name'])
- continue
-
- size = volume['gb']
- v_name = volume['name']
- body = client.volumes.create_volume(size=size,
- display_name=v_name)['volume']
- waiters.wait_for_volume_status(client.volumes, body['id'], 'available')
-
-
-def destroy_volumes(volumes):
- for volume in volumes:
- client = client_for_user(volume['owner'])
- volume_id = _get_volume_by_name(client, volume['name'])['id']
- client.volumes.detach_volume(volume_id)
- client.volumes.delete_volume(volume_id)
-
-
-def attach_volumes(volumes):
- for volume in volumes:
- client = client_for_user(volume['owner'])
- server_id = _get_server_by_name(client, volume['server'])['id']
- volume_id = _get_volume_by_name(client, volume['name'])['id']
- device = volume['device']
- client.volumes.attach_volume(volume_id,
- instance_uuid=server_id,
- mountpoint=device)
-
-
-#######################
-#
-# MAIN LOGIC
-#
-#######################
-
-def create_resources():
- LOG.info("Creating Resources")
- # first create keystone level resources, and we need to be admin
- # for this.
- create_tenants(RES['tenants'])
- create_users(RES['users'])
- collect_users(RES['users'])
-
- # next create resources in a well known order
- create_objects(RES['objects'])
- create_images(RES['images'])
-
- # validate neutron is enabled and ironic is disabled
- if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
- create_networks(RES['networks'])
- create_subnets(RES['subnets'])
- create_routers(RES['routers'])
- add_router_interface(RES['routers'])
-
- create_secgroups(RES['secgroups'])
- create_volumes(RES['volumes'])
-
- # Only attempt attaching the volumes if servers are defined in the
- # resource file
- if 'servers' in RES:
- create_servers(RES['servers'])
- attach_volumes(RES['volumes'])
-
-
-def destroy_resources():
- LOG.info("Destroying Resources")
- # Destroy in inverse order of create
- destroy_servers(RES['servers'])
- destroy_images(RES['images'])
- destroy_objects(RES['objects'])
- destroy_volumes(RES['volumes'])
- if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
- destroy_routers(RES['routers'])
- destroy_subnets(RES['subnets'])
- destroy_networks(RES['networks'])
- destroy_secgroups(RES['secgroups'])
- destroy_users(RES['users'])
- destroy_tenants(RES['tenants'])
- LOG.warning("Destroy mode incomplete")
-
-
-def get_options():
- global OPTS
- parser = argparse.ArgumentParser(
- description='Create and validate a fixed set of OpenStack resources')
- parser.add_argument('-m', '--mode',
- metavar='<create|check|destroy>',
- required=True,
- help=('One of (create, check, destroy)'))
- parser.add_argument('-r', '--resources',
- required=True,
- metavar='resourcefile.yaml',
- help='Resources definition yaml file')
-
- parser.add_argument(
- '-d', '--devstack-base',
- required=True,
- metavar='/opt/stack/old',
- help='Devstack base directory for retrieving artifacts')
- parser.add_argument(
- '-c', '--config-file',
- metavar='/etc/tempest.conf',
- help='path to javelin2(tempest) config file')
-
- # auth bits, letting us also just source the devstack openrc
- parser.add_argument('--os-username',
- metavar='<auth-user-name>',
- default=os.environ.get('OS_USERNAME'),
- help=('Defaults to env[OS_USERNAME].'))
- parser.add_argument('--os-password',
- metavar='<auth-password>',
- default=os.environ.get('OS_PASSWORD'),
- help=('Defaults to env[OS_PASSWORD].'))
- parser.add_argument('--os-tenant-name',
- metavar='<auth-tenant-name>',
- default=os.environ.get('OS_TENANT_NAME'),
- help=('Defaults to env[OS_TENANT_NAME].'))
-
- OPTS = parser.parse_args()
- if OPTS.mode not in ('create', 'check', 'destroy'):
- print("ERROR: Unknown mode -m %s\n" % OPTS.mode)
- parser.print_help()
- sys.exit(1)
- if OPTS.config_file:
- config.CONF.set_config_path(OPTS.config_file)
-
-
-def setup_logging():
- global LOG
- logging.setup(CONF, __name__)
- LOG = logging.getLogger(__name__)
-
-
-def main():
- print("Javelin is deprecated and will be removed from Tempest in the "
- "future.")
- global RES
- get_options()
- setup_logging()
- RES.update(load_resources(OPTS.resources))
-
- if OPTS.mode == 'create':
- create_resources()
- # Make sure the resources we just created actually work
- checker = JavelinCheck(USERS, RES)
- checker.check()
- elif OPTS.mode == 'check':
- collect_users(RES['users'])
- checker = JavelinCheck(USERS, RES)
- checker.check()
- elif OPTS.mode == 'destroy':
- collect_users(RES['users'])
- destroy_resources()
- else:
- LOG.error('Unknown mode %s' % OPTS.mode)
- return 1
- LOG.info('javelin2 successfully finished')
- return 0
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/tempest/cmd/resources.yaml b/tempest/cmd/resources.yaml
deleted file mode 100644
index 5c62ee3..0000000
--- a/tempest/cmd/resources.yaml
+++ /dev/null
@@ -1,95 +0,0 @@
-# This is a yaml description for the most basic definitions
-# of what should exist across the resource boundary. Perhaps
-# one day this will grow into a Heat resource template, but as
-# Heat isn't a known working element in the upgrades, we do
-# this much simpler thing for now.
-
-tenants:
- - javelin
- - discuss
-
-users:
- - name: javelin
- pass: gungnir
- tenant: javelin
- - name: javelin2
- pass: gungnir2
- tenant: discuss
-
-secgroups:
- - name: angon
- owner: javelin
- description: angon
- rules:
- - 'icmp -1 -1 0.0.0.0/0'
- - 'tcp 22 22 0.0.0.0/0'
- - name: baobab
- owner: javelin
- description: baobab
- rules:
- - 'tcp 80 80 0.0.0.0/0'
-
-# resources that we want to create
-images:
- - name: javelin_cirros
- owner: javelin
- imgdir: files/images/cirros-0.3.2-x86_64-uec
- file: cirros-0.3.2-x86_64-blank.img
- format: ami
- aki: cirros-0.3.2-x86_64-vmlinuz
- ari: cirros-0.3.2-x86_64-initrd
-volumes:
- - name: assegai
- server: peltast
- owner: javelin
- gb: 1
- device: /dev/vdb
- - name: pifpouf
- server: hoplite
- owner: javelin
- gb: 2
- device: /dev/vdb
-networks:
- - name: world1
- owner: javelin
- - name: world2
- owner: javelin
-subnets:
- - name: subnet1
- range: 10.1.0.0/24
- network: world1
- owner: javelin
- - name: subnet2
- range: 192.168.1.0/24
- network: world2
- owner: javelin
-routers:
- - name: connector
- owner: javelin
- gateway: true
- subnet:
- - subnet1
- - subnet2
-servers:
- - name: peltast
- owner: javelin
- flavor: m1.small
- image: javelin_cirros
- networks:
- - world1
- secgroups:
- - angon
- - baobab
- - name: hoplite
- owner: javelin
- flavor: m1.medium
- image: javelin_cirros
- networks:
- - world2
- secgroups:
- - angon
-objects:
- - container: jc1
- name: javelin1
- owner: javelin
- file: /etc/hosts
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 77b88f9..9947f2a 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -147,6 +147,10 @@
contains_version('v2.', versions)):
print_and_or_update('api_v2', 'volume-feature-enabled',
not CONF.volume_feature_enabled.api_v2, update)
+ if (CONF.volume_feature_enabled.api_v3 !=
+ contains_version('v3.', versions)):
+ print_and_or_update('api_v3', 'volume-feature-enabled',
+ not CONF.volume_feature_enabled.api_v3, update)
def verify_api_versions(os, service, update):
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 073481c..9d307ee 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -53,9 +53,7 @@
return
# NOTE(afazekas): The instance is in "ready for action state"
# when no task in progress
- # NOTE(afazekas): Converted to string because of the XML
- # responses
- if str(task_state) == "None":
+ if task_state is None:
# without state api extension 3 sec usually enough
time.sleep(CONF.compute.ready_wait)
return
diff --git a/tempest/config.py b/tempest/config.py
index 0c2b913..3fd20ab 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -26,6 +26,7 @@
import testtools
from tempest.lib import exceptions
+from tempest.lib.services import clients
from tempest.test_discover import plugins
@@ -173,6 +174,16 @@
"a domain scoped token to use admin APIs")
]
+service_clients_group = cfg.OptGroup(name='service-clients',
+ title="Service Clients Options")
+
+ServiceClientsGroup = [
+ cfg.IntOpt('http_timeout',
+ default=60,
+ help='Timeout in seconds to wait for the http request to '
+ 'return'),
+]
+
identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
title='Enabled Identity Features')
@@ -369,8 +380,9 @@
help='Does the test environment support creating snapshot '
'images of running instances?'),
cfg.BoolOpt('nova_cert',
- default=True,
- help='Does the test environment have the nova cert running?'),
+ default=False,
+ help='Does the test environment have the nova cert running?',
+ deprecated_for_removal=True),
cfg.BoolOpt('personality',
default=False,
help='Does the test environment support server personality'),
@@ -491,7 +503,7 @@
default=False,
help="Whether project networks can be reached directly from "
"the test client. This must be set to True when the "
- "'fixed' ssh_connect_method is selected."),
+ "'fixed' connect_method is selected."),
cfg.StrOpt('public_network_id',
default="",
help="Id of the public network that provides external "
@@ -642,7 +654,7 @@
cfg.StrOpt('network_for_ssh',
default='public',
help="Network used for SSH connections. Ignored if "
- "connect_method=floating or run_validation=false.",
+ "connect_method=floating.",
deprecated_opts=[cfg.DeprecatedOpt('network_for_ssh',
group='compute')]),
]
@@ -1119,6 +1131,7 @@
(compute_group, ComputeGroup),
(compute_features_group, ComputeFeaturesGroup),
(identity_group, IdentityGroup),
+ (service_clients_group, ServiceClientsGroup),
(identity_feature_group, IdentityFeatureGroup),
(image_group, ImageGroup),
(image_feature_group, ImageFeaturesGroup),
@@ -1184,6 +1197,7 @@
self.compute = _CONF.compute
self.compute_feature_enabled = _CONF['compute-feature-enabled']
self.identity = _CONF.identity
+ self.service_clients = _CONF['service-clients']
self.identity_feature_enabled = _CONF['identity-feature-enabled']
self.image = _CONF.image
self.image_feature_enabled = _CONF['image-feature-enabled']
@@ -1275,6 +1289,15 @@
lockutils.set_defaults(lock_dir)
self._config = TempestConfigPrivate(config_path=self._path)
+ # Pushing tempest internal service client configuration to the
+ # service clients register. Doing this in the config module ensures
+ # that the configuration is available by the time we register the
+ # service clients.
+ # NOTE(andreaf) This has to be done at the time the first
+ # attribute is accessed, to ensure all plugins have been already
+ # loaded, options registered, and _config is set.
+ _register_tempest_service_clients()
+
return getattr(self._config, attr)
def set_config_path(self, path):
@@ -1372,6 +1395,7 @@
* `disable_ssl_certificate_validation`
* `ca_certs`
* `trace_requests`
+ * `http_timeout`
The dict returned by this does not fit a few service clients:
@@ -1393,7 +1417,8 @@
'disable_ssl_certificate_validation':
CONF.identity.disable_ssl_certificate_validation,
'ca_certs': CONF.identity.ca_certificates_file,
- 'trace_requests': CONF.debug.trace_requests
+ 'trace_requests': CONF.debug.trace_requests,
+ 'http_timeout': CONF.service_clients.http_timeout
}
if service_client_name is None:
@@ -1432,3 +1457,29 @@
# Set service
_parameters['service'] = getattr(options, 'catalog_type')
return _parameters
+
+
+def _register_tempest_service_clients():
+ # Register tempest own service clients using the same mechanism used
+ # for external plugins.
+ # The configuration data is pushed to the registry so that automatic
+ # configuration of tempest own service clients is possible both for
+ # tempest as well as for the plugins.
+ service_clients = clients.tempest_modules()
+ registry = clients.ClientsRegistry()
+ all_clients = []
+ for service_client in service_clients:
+ module = service_clients[service_client]
+ configs = service_client.split('.')[0]
+ service_client_data = dict(
+ name=service_client.replace('.', '_'),
+ service_version=service_client,
+ module_path=module.__name__,
+ client_names=module.__all__,
+ **service_client_config(configs)
+ )
+ all_clients.append(service_client_data)
+ # NOTE(andreaf) Internal service clients do not actually belong
+ # to a plugin, so using '__tempest__' to indicate a virtual plugin
+ # which holds internal service clients.
+ registry.register_service_client('__tempest__', all_clients)
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 54a7002..1857a43 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -260,11 +260,13 @@
def __init__(self, credentials, auth_url,
disable_ssl_certificate_validation=None,
- ca_certs=None, trace_requests=None, scope='project'):
+ ca_certs=None, trace_requests=None, scope='project',
+ http_timeout=None):
super(KeystoneAuthProvider, self).__init__(credentials, scope)
self.dscv = disable_ssl_certificate_validation
self.ca_certs = ca_certs
self.trace_requests = trace_requests
+ self.http_timeout = http_timeout
self.auth_url = auth_url
self.auth_client = self._auth_client(auth_url)
@@ -342,7 +344,8 @@
def _auth_client(self, auth_url):
return json_v2id.TokenClient(
auth_url, disable_ssl_certificate_validation=self.dscv,
- ca_certs=self.ca_certs, trace_requests=self.trace_requests)
+ ca_certs=self.ca_certs, trace_requests=self.trace_requests,
+ http_timeout=self.http_timeout)
def _auth_params(self):
"""Auth parameters to be passed to the token request
@@ -429,7 +432,8 @@
def _auth_client(self, auth_url):
return json_v3id.V3TokenClient(
auth_url, disable_ssl_certificate_validation=self.dscv,
- ca_certs=self.ca_certs, trace_requests=self.trace_requests)
+ ca_certs=self.ca_certs, trace_requests=self.trace_requests,
+ http_timeout=self.http_timeout)
def _auth_params(self):
"""Auth parameters to be passed to the token request
@@ -595,7 +599,7 @@
def get_credentials(auth_url, fill_in=True, identity_version='v2',
disable_ssl_certificate_validation=None, ca_certs=None,
- trace_requests=None, **kwargs):
+ trace_requests=None, http_timeout=None, **kwargs):
"""Builds a credentials object based on the configured auth_version
:param auth_url (string): Full URI of the OpenStack Identity API(Keystone)
@@ -611,6 +615,8 @@
:param ca_certs: CA certificate bundle for validation of certificates
in SSL API requests to the auth system
:param trace_requests: trace in log API requests to the auth system
+ :param http_timeout: timeout in seconds to wait for the http request to
+ return
:param kwargs (dict): Dict of credential key/value pairs
Examples:
@@ -634,7 +640,8 @@
dscv = disable_ssl_certificate_validation
auth_provider = auth_provider_class(
creds, auth_url, disable_ssl_certificate_validation=dscv,
- ca_certs=ca_certs, trace_requests=trace_requests)
+ ca_certs=ca_certs, trace_requests=trace_requests,
+ http_timeout=http_timeout)
creds = auth_provider.fill_credentials()
return creds
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
index dffc5f9..86ea26e 100644
--- a/tempest/lib/common/http.py
+++ b/tempest/lib/common/http.py
@@ -18,7 +18,7 @@
class ClosingHttp(urllib3.poolmanager.PoolManager):
def __init__(self, disable_ssl_certificate_validation=False,
- ca_certs=None):
+ ca_certs=None, timeout=None):
kwargs = {}
if disable_ssl_certificate_validation:
@@ -29,6 +29,9 @@
kwargs['cert_reqs'] = 'CERT_REQUIRED'
kwargs['ca_certs'] = ca_certs
+ if timeout:
+ kwargs['timeout'] = timeout
+
super(ClosingHttp, self).__init__(**kwargs)
def request(self, url, method, *args, **kwargs):
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 1b0f53a..4e851b0 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -64,8 +64,10 @@
certificate validation
:param str ca_certs: File containing the CA Bundle to use in verifying a
TLS server cert
- :param str trace_request: Regex to use for specifying logging the entirety
+ :param str trace_requests: Regex to use for specifying logging the entirety
of the request and response payload
+ :param str http_timeout: Timeout in seconds to wait for the http request to
+ return
"""
TYPE = "json"
@@ -78,7 +80,7 @@
endpoint_type='publicURL',
build_interval=1, build_timeout=60,
disable_ssl_certificate_validation=False, ca_certs=None,
- trace_requests='', name=None):
+ trace_requests='', name=None, http_timeout=None):
self.auth_provider = auth_provider
self.service = service
self.region = region
@@ -99,7 +101,8 @@
'vary', 'www-authenticate'))
dscv = disable_ssl_certificate_validation
self.http_obj = http.ClosingHttp(
- disable_ssl_certificate_validation=dscv, ca_certs=ca_certs)
+ disable_ssl_certificate_validation=dscv, ca_certs=ca_certs,
+ timeout=http_timeout)
def _get_type(self):
return self.TYPE
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index 8054e62..adf666b 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -1,3 +1,4 @@
+# Copyright 2012 OpenStack Foundation
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
# All Rights Reserved.
#
@@ -13,8 +14,94 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
+import importlib
+import inspect
+import logging
+
+from tempest.lib import auth
from tempest.lib.common.utils import misc
from tempest.lib import exceptions
+from tempest.lib.services import compute
+from tempest.lib.services import image
+from tempest.lib.services import network
+
+
+LOG = logging.getLogger(__name__)
+
+
+def tempest_modules():
+ """Dict of service client modules available in Tempest.
+
+ Provides a dict of stable service modules available in Tempest, with
+ ``service_version`` as key, and the module object as value.
+ """
+ return {
+ 'compute': compute,
+ 'image.v1': image.v1,
+ 'image.v2': image.v2,
+ 'network': network
+ }
+
+
+def _tempest_internal_modules():
+ # Set of unstable service clients available in Tempest
+ # NOTE(andreaf) This list will exists only as long the remain clients
+ # are migrated to tempest.lib, and it will then be deleted without
+ # deprecation or advance notice
+ return set(['identity.v2', 'identity.v3', 'object-storage', 'volume.v1',
+ 'volume.v2', 'volume.v3'])
+
+
+def available_modules():
+ """Set of service client modules available in Tempest and plugins
+
+ Set of stable service clients from Tempest and service clients exposed
+ by plugins. This set of available modules can be used for automatic
+ configuration.
+
+ :raise PluginRegistrationException: if a plugin exposes a service_version
+ already defined by Tempest or another plugin.
+
+ Examples:
+
+ >>> from tempest import config
+ >>> params = {}
+ >>> for service_version in available_modules():
+ >>> service = service_version.split('.')[0]
+ >>> params[service] = config.service_client_config(service)
+ >>> service_clients = ServiceClients(creds, identity_uri,
+ >>> client_parameters=params)
+ """
+ extra_service_versions = set([])
+ _tempest_modules = set(tempest_modules())
+ plugin_services = ClientsRegistry().get_service_clients()
+ for plugin_name in plugin_services:
+ plug_service_versions = set([x['service_version'] for x in
+ plugin_services[plugin_name]])
+ # If a plugin exposes a duplicate service_version raise an exception
+ if plug_service_versions:
+ if not plug_service_versions.isdisjoint(extra_service_versions):
+ detailed_error = (
+ 'Plugin %s is trying to register a service %s already '
+ 'claimed by another one' % (plugin_name,
+ extra_service_versions &
+ plug_service_versions))
+ raise exceptions.PluginRegistrationException(
+ name=plugin_name, detailed_error=detailed_error)
+ # NOTE(andreaf) Once all tempest clients are stable, the following
+ # if will have to be removed.
+ if not plug_service_versions.isdisjoint(
+ _tempest_internal_modules()):
+ detailed_error = (
+ 'Plugin %s is trying to register a service %s already '
+ 'claimed by a Tempest one' % (plugin_name,
+ _tempest_internal_modules() &
+ plug_service_versions))
+ raise exceptions.PluginRegistrationException(
+ name=plugin_name, detailed_error=detailed_error)
+ extra_service_versions |= plug_service_versions
+ return _tempest_modules | extra_service_versions
@misc.singleton
@@ -34,3 +121,327 @@
def get_service_clients(self):
return self._service_clients
+
+
+class ClientsFactory(object):
+ """Builds service clients for a service client module
+
+ This class implements the logic of feeding service client parameters
+ to service clients from a specific module. It allows setting the
+ parameters once and obtaining new instances of the clients without the
+ need of passing any parameter.
+
+ ClientsFactory can be used directly, or consumed via the `ServiceClients`
+ class, which manages the authorization part.
+ """
+
+ def __init__(self, module_path, client_names, auth_provider, **kwargs):
+ """Initialises the client factory
+
+ :param module_path: Path to module that includes all service clients.
+ All service client classes must be exposed by a single module.
+ If they are separated in different modules, defining __all__
+ in the root module can help, similar to what is done by service
+ clients in tempest.
+ :param client_names: List or set of names of the service client
+ classes.
+ :param auth_provider: The auth provider used to initialise client.
+ :param kwargs: Parameters to be passed to all clients. Parameters
+ values can be overwritten when clients are initialised, but
+ parameters cannot be deleted.
+ :raise ImportError if the specified module_path cannot be imported
+
+ Example:
+
+ >>> # Get credentials and an auth_provider
+ >>> clients = ClientsFactory(
+ >>> module_path='my_service.my_service_clients',
+ >>> client_names=['ServiceClient1', 'ServiceClient2'],
+ >>> auth_provider=auth_provider,
+ >>> service='my_service',
+ >>> region='region1')
+ >>> my_api_client = clients.MyApiClient()
+ >>> my_api_client_region2 = clients.MyApiClient(region='region2')
+
+ """
+ # Import the module. If it's not importable, the raised exception
+ # provides good enough information about what happened
+ _module = importlib.import_module(module_path)
+ # If any of the classes is not in the module we fail
+ for class_name in client_names:
+ # TODO(andreaf) This always passes all parameters to all clients.
+ # In future to allow clients to specify the list of parameters
+ # that they accept based out of a list of standard ones.
+
+ # Obtain the class
+ klass = self._get_class(_module, class_name)
+ final_kwargs = copy.copy(kwargs)
+
+ # Set the function as an attribute of the factory
+ setattr(self, class_name, self._get_partial_class(
+ klass, auth_provider, final_kwargs))
+
+ def _get_partial_class(self, klass, auth_provider, kwargs):
+
+ # Define a function that returns a new class instance by
+ # combining default kwargs with extra ones
+ def partial_class(alias=None, **later_kwargs):
+ """Returns a callable the initialises a service client
+
+ Builds a callable that accepts kwargs, which are passed through
+ to the __init__ of the service client, along with a set of defaults
+ set in factory at factory __init__ time.
+ Original args in the service client can only be passed as kwargs.
+
+ It accepts one extra parameter 'alias' compared to the original
+ service client. When alias is provided, the returned callable will
+ also set an attribute called with a name defined in 'alias', which
+ contains the instance of the service client.
+
+ :param alias: str Name of the attribute set on the factory once
+ the callable is invoked which contains the initialised
+ service client. If None, no attribute is set.
+ :param later_kwargs: kwargs passed through to the service client
+ __init__ on top of defaults set at factory level.
+ """
+ kwargs.update(later_kwargs)
+ _client = klass(auth_provider=auth_provider, **kwargs)
+ if alias:
+ setattr(self, alias, _client)
+ return _client
+
+ return partial_class
+
+ @classmethod
+ def _get_class(cls, module, class_name):
+ klass = getattr(module, class_name, None)
+ if not klass:
+ msg = 'Invalid class name, %s is not found in %s'
+ raise AttributeError(msg % (class_name, module))
+ if not inspect.isclass(klass):
+ msg = 'Expected a class, got %s of type %s instead'
+ raise TypeError(msg % (klass, type(klass)))
+ return klass
+
+
+class ServiceClients(object):
+ """Service client provider class
+
+ The ServiceClients object provides a useful means for tests to access
+ service clients configured for a specified set of credentials.
+ It hides some of the complexity from the authorization and configuration
+ layers.
+
+ Examples:
+
+ >>> from tempest.lib.services import clients
+ >>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
+ >>> johndoe_clients = clients.ServiceClients(johndoe,
+ >>> identity_uri)
+ >>> johndoe_servers = johndoe_clients.servers_client.list_servers()
+
+ """
+ # NOTE(andreaf) This class does not depend on tempest configuration
+ # and its meant for direct consumption by external clients such as tempest
+ # plugins. Tempest provides a wrapper class, `clients.Manager`, that
+ # 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='', 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.
+ :param region: Default value of region for service clients.
+ :param scope: default scope for tokens produced by the auth provider
+ :param disable_ssl_certificate_validation: Applies to auth and to all
+ 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._registered_services = set([])
+ self.credentials = credentials
+ self.identity_uri = identity_uri
+ if not identity_uri:
+ raise exceptions.InvalidCredentials(
+ 'ServiceClients requires a non-empty identity_uri.')
+ self.region = region
+ # Check if passed or default credentials are valid
+ if not self.credentials.is_valid():
+ raise exceptions.InvalidCredentials()
+ # Get the identity classes matching the provided credentials
+ # TODO(andreaf) Define a new interface in Credentials to get
+ # the API version from an instance
+ identity = [(k, auth.IDENTITY_VERSION[k][1]) for k in
+ auth.IDENTITY_VERSION.keys() if
+ isinstance(self.credentials, auth.IDENTITY_VERSION[k][0])]
+ # Zero matches or more than one are both not valid.
+ if len(identity) != 1:
+ raise exceptions.InvalidCredentials()
+ self.auth_version, auth_provider_class = identity[0]
+ self.dscv = disable_ssl_certificate_validation
+ self.ca_certs = ca_certs
+ self.trace_requests = trace_requests
+ # Creates an auth provider for the credentials
+ self.auth_provider = auth_provider_class(
+ 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
+ all_modules = available_modules() | _tempest_internal_modules()
+ unversioned_services = set(
+ [x.split('.')[0] for x in all_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()))
+
+ # Register service clients from the registry (__tempest__ and plugins)
+ clients_registry = ClientsRegistry()
+ plugin_service_clients = clients_registry.get_service_clients()
+ for plugin in plugin_service_clients:
+ service_clients = plugin_service_clients[plugin]
+ # Each plugin returns a list of service client parameters
+ for service_client in service_clients:
+ # NOTE(andreaf) If a plugin cannot register, stop the
+ # registration process, log some details to help
+ # troubleshooting, and re-raise
+ try:
+ self.register_service_client_module(**service_client)
+ except Exception:
+ LOG.exception(
+ 'Failed to register service client from plugin %s '
+ 'with parameters %s' % (plugin, service_client))
+ raise
+
+ def register_service_client_module(self, name, service_version,
+ module_path, client_names, **kwargs):
+ """Register a service client module
+
+ Initiates a client factory for the specified module, using this
+ class auth_provider, and accessible via a `name` attribute in the
+ service client.
+
+ :param name: Name used to access the client
+ :param service_version: Name of the service complete with version.
+ Used to track registered services. When a plugin implements it,
+ it can be used by other plugins to obtain their configuration.
+ :param module_path: Path to module that includes all service clients.
+ All service client classes must be exposed by a single module.
+ If they are separated in different modules, defining __all__
+ in the root module can help, similar to what is done by service
+ clients in tempest.
+ :param client_names: List or set of names of service client classes.
+ :param kwargs: Extra optional parameters to be passed to all clients.
+ ServiceClient provides defaults for region, dscv, ca_certs and
+ trace_requests.
+ :raise ServiceClientRegistrationException: if the provided name is
+ already in use or if service_version is already registered.
+ :raise ImportError: if module_path cannot be imported.
+ """
+ if hasattr(self, name):
+ using_name = getattr(self, name)
+ detailed_error = 'Module name already in use: %s' % using_name
+ raise exceptions.ServiceClientRegistrationException(
+ name=name, service_version=service_version,
+ module_path=module_path, client_names=client_names,
+ detailed_error=detailed_error)
+ if service_version in self.registered_services:
+ detailed_error = 'Service %s already registered.' % service_version
+ raise exceptions.ServiceClientRegistrationException(
+ name=name, service_version=service_version,
+ module_path=module_path, client_names=client_names,
+ detailed_error=detailed_error)
+ params = dict(region=self.region,
+ disable_ssl_certificate_validation=self.dscv,
+ ca_certs=self.ca_certs,
+ trace_requests=self.trace_requests)
+ params.update(kwargs)
+ # Instantiate the client factory
+ _factory = ClientsFactory(module_path=module_path,
+ client_names=client_names,
+ auth_provider=self.auth_provider,
+ **params)
+ # Adds the client factory to the service_client
+ setattr(self, name, _factory)
+ # Add the name of the new service in self.SERVICES for discovery
+ self._registered_services.add(service_version)
+
+ @property
+ def registered_services(self):
+ # NOTE(andreaf) Once all tempest modules are stable this needs to
+ # be updated to remove _tempest_internal_modules
+ return self._registered_services | _tempest_internal_modules()
+
+ 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/lib/services/compute/agents_client.py b/tempest/lib/services/compute/agents_client.py
old mode 100644
new mode 100755
index 6d3a817..3f05d3b
--- a/tempest/lib/services/compute/agents_client.py
+++ b/tempest/lib/services/compute/agents_client.py
@@ -24,7 +24,11 @@
"""Tests Agents API"""
def list_agents(self, **params):
- """List all agent builds."""
+ """List all agent builds.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listbuilds
+ """
url = 'os-agents'
if params:
url += '?%s' % urllib.urlencode(params)
@@ -46,7 +50,11 @@
return rest_client.ResponseBody(resp, body)
def delete_agent(self, agent_id):
- """Delete an existing agent build."""
+ """Delete an existing agent build.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteBuild
+ """
resp, body = self.delete("os-agents/%s" % agent_id)
self.validate_response(schema.delete_agent, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
old mode 100644
new mode 100755
index 5be8272..ae1700c
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -28,6 +28,11 @@
class FlavorsClient(base_compute_client.BaseComputeClient):
def list_flavors(self, detail=False, **params):
+ """Lists flavors.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listFlavors
+ """
url = 'flavors'
_schema = schema.list_flavors
@@ -43,6 +48,11 @@
return rest_client.ResponseBody(resp, body)
def show_flavor(self, flavor_id):
+ """Shows details for a flavor.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#showFlavor
+ """
resp, body = self.get("flavors/%s" % flavor_id)
body = json.loads(body)
self.validate_response(schema.create_get_flavor_details, resp, body)
@@ -67,7 +77,11 @@
return rest_client.ResponseBody(resp, body)
def delete_flavor(self, flavor_id):
- """Delete the given flavor."""
+ """Delete the given flavor.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteFlavor
+ """
resp, body = self.delete("flavors/{0}".format(flavor_id))
self.validate_response(schema.delete_flavor, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -102,7 +116,11 @@
return rest_client.ResponseBody(resp, body)
def list_flavor_extra_specs(self, flavor_id):
- """Get extra Specs details of the mentioned flavor."""
+ """Get extra Specs details of the mentioned flavor.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listFlavorExtraSpecs
+ """
resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
body = json.loads(body)
self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
@@ -110,7 +128,11 @@
return rest_client.ResponseBody(resp, body)
def show_flavor_extra_spec(self, flavor_id, key):
- """Get extra Specs key-value of the mentioned flavor and key."""
+ """Get extra Specs key-value of the mentioned flavor and key.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#showFlavorExtraSpec
+ """
resp, body = self.get('flavors/%s/os-extra_specs/%s' % (flavor_id,
key))
body = json.loads(body)
@@ -136,14 +158,22 @@
def unset_flavor_extra_spec(self, flavor_id, key): # noqa
# NOTE: This noqa is for passing T111 check and we cannot rename
# to keep backwards compatibility.
- """Unset extra Specs from the mentioned flavor."""
+ """Unset extra Specs from the mentioned flavor.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteFlavorExtraSpec
+ """
resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
(flavor_id, key))
self.validate_response(schema.unset_flavor_extra_specs, resp, body)
return rest_client.ResponseBody(resp, body)
def list_flavor_access(self, flavor_id):
- """Get flavor access information given the flavor id."""
+ """Get flavor access information given the flavor id.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listFlavorAccess
+ """
resp, body = self.get('flavors/%s/os-flavor-access' % flavor_id)
body = json.loads(body)
self.validate_response(schema_access.add_remove_list_flavor_access,
@@ -151,7 +181,11 @@
return rest_client.ResponseBody(resp, body)
def add_flavor_access(self, flavor_id, tenant_id):
- """Add flavor access for the specified tenant."""
+ """Add flavor access for the specified tenant.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#addFlavorAccess
+ """
post_body = {
'addTenantAccess': {
'tenant': tenant_id
@@ -165,7 +199,11 @@
return rest_client.ResponseBody(resp, body)
def remove_flavor_access(self, flavor_id, tenant_id):
- """Remove flavor access from the specified tenant."""
+ """Remove flavor access from the specified tenant.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#removeFlavorAccess
+ """
post_body = {
'removeTenantAccess': {
'tenant': tenant_id
diff --git a/tempest/lib/services/compute/floating_ips_client.py b/tempest/lib/services/compute/floating_ips_client.py
old mode 100644
new mode 100755
index 03e4894..6922c48
--- a/tempest/lib/services/compute/floating_ips_client.py
+++ b/tempest/lib/services/compute/floating_ips_client.py
@@ -25,7 +25,11 @@
class FloatingIPsClient(base_compute_client.BaseComputeClient):
def list_floating_ips(self, **params):
- """Returns a list of all floating IPs filtered by any parameters."""
+ """Returns a list of all floating IPs filtered by any parameters.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listfloatingipsObject
+ """
url = 'os-floating-ips'
if params:
url += '?%s' % urllib.urlencode(params)
@@ -36,7 +40,11 @@
return rest_client.ResponseBody(resp, body)
def show_floating_ip(self, floating_ip_id):
- """Get the details of a floating IP."""
+ """Get the details of a floating IP.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#showFloatingIP
+ """
url = "os-floating-ips/%s" % floating_ip_id
resp, body = self.get(url)
body = json.loads(body)
@@ -57,7 +65,11 @@
return rest_client.ResponseBody(resp, body)
def delete_floating_ip(self, floating_ip_id):
- """Deletes the provided floating IP from the project."""
+ """Deletes the provided floating IP from the project.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteFloatingIP
+ """
url = "os-floating-ips/%s" % floating_ip_id
resp, body = self.delete(url)
self.validate_response(schema.add_remove_floating_ip, resp, body)
diff --git a/tempest/lib/services/compute/keypairs_client.py b/tempest/lib/services/compute/keypairs_client.py
old mode 100644
new mode 100755
index 7b8e6b2..2246739
--- a/tempest/lib/services/compute/keypairs_client.py
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -28,6 +28,11 @@
{'min': '2.2', 'max': None, 'schema': schemav22}]
def list_keypairs(self, **params):
+ """Lists keypairs that are associated with the account.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listKeypairs
+ """
url = 'os-keypairs'
if params:
url += '?%s' % urllib.urlencode(params)
@@ -38,6 +43,11 @@
return rest_client.ResponseBody(resp, body)
def show_keypair(self, keypair_name, **params):
+ """Shows details for a keypair that is associated with the account.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#showKeypair
+ """
url = "os-keypairs/%s" % keypair_name
if params:
url += '?%s' % urllib.urlencode(params)
@@ -61,6 +71,11 @@
return rest_client.ResponseBody(resp, body)
def delete_keypair(self, keypair_name, **params):
+ """Deletes a keypair.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteKeypair
+ """
url = "os-keypairs/%s" % keypair_name
if params:
url += '?%s' % urllib.urlencode(params)
diff --git a/tempest/lib/services/compute/security_groups_client.py b/tempest/lib/services/compute/security_groups_client.py
old mode 100644
new mode 100755
index 6b9c7e1..386c214
--- a/tempest/lib/services/compute/security_groups_client.py
+++ b/tempest/lib/services/compute/security_groups_client.py
@@ -26,7 +26,11 @@
class SecurityGroupsClient(base_compute_client.BaseComputeClient):
def list_security_groups(self, **params):
- """List all security groups for a user."""
+ """List all security groups for a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listSecGroups
+ """
url = 'os-security-groups'
if params:
@@ -38,7 +42,11 @@
return rest_client.ResponseBody(resp, body)
def show_security_group(self, security_group_id):
- """Get the details of a Security Group."""
+ """Get the details of a Security Group.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#showSecGroup
+ """
url = "os-security-groups/%s" % security_group_id
resp, body = self.get(url)
body = json.loads(body)
@@ -71,7 +79,11 @@
return rest_client.ResponseBody(resp, body)
def delete_security_group(self, security_group_id):
- """Delete the provided Security Group."""
+ """Delete the provided Security Group.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteSecGroup
+ """
resp, body = self.delete(
'os-security-groups/%s' % security_group_id)
self.validate_response(schema.delete_security_group, resp, body)
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
old mode 100644
new mode 100755
index 9444e20..24c0be9
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -103,7 +103,11 @@
return rest_client.ResponseBody(resp, body)
def show_server(self, server_id):
- """Get server details."""
+ """Get server details.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#showServer
+ """
resp, body = self.get("servers/%s" % server_id)
body = json.loads(body)
schema = self.get_schema(self.schema_versions_info)
@@ -111,7 +115,11 @@
return rest_client.ResponseBody(resp, body)
def delete_server(self, server_id):
- """Delete server."""
+ """Delete server.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteServer
+ """
resp, body = self.delete("servers/%s" % server_id)
self.validate_response(schema.delete_server, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -141,7 +149,11 @@
return rest_client.ResponseBody(resp, body)
def list_addresses(self, server_id):
- """Lists all addresses for a server."""
+ """Lists all addresses for a server.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#list-ips
+ """
resp, body = self.get("servers/%s/ips" % server_id)
body = json.loads(body)
self.validate_response(schema.list_addresses, resp, body)
@@ -264,12 +276,22 @@
return self.action(server_id, 'revertResize', **kwargs)
def list_server_metadata(self, server_id):
+ """Lists all metadata for a server.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listServerMetadata
+ """
resp, body = self.get("servers/%s/metadata" % server_id)
body = json.loads(body)
self.validate_response(schema.list_server_metadata, resp, body)
return rest_client.ResponseBody(resp, body)
def set_server_metadata(self, server_id, meta, no_metadata_field=False):
+ """Sets one or more metadata items for a server.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#createServerMetadata
+ """
if no_metadata_field:
post_body = ""
else:
@@ -281,6 +303,11 @@
return rest_client.ResponseBody(resp, body)
def update_server_metadata(self, server_id, meta):
+ """Updates one or more metadata items for a server.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#updateServerMetadata
+ """
post_body = json.dumps({'metadata': meta})
resp, body = self.post('servers/%s/metadata' % server_id,
post_body)
@@ -290,6 +317,11 @@
return rest_client.ResponseBody(resp, body)
def show_server_metadata_item(self, server_id, key):
+ """Shows details for a metadata item, by key, for a server.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#showServerMetadataItem
+ """
resp, body = self.get("servers/%s/metadata/%s" % (server_id, key))
body = json.loads(body)
self.validate_response(schema.set_show_server_metadata_item,
@@ -297,6 +329,11 @@
return rest_client.ResponseBody(resp, body)
def set_server_metadata_item(self, server_id, key, meta):
+ """Sets a metadata item, by key, for a server.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#setServerMetadataItem
+ """
post_body = json.dumps({'meta': meta})
resp, body = self.put('servers/%s/metadata/%s' % (server_id, key),
post_body)
@@ -306,6 +343,11 @@
return rest_client.ResponseBody(resp, body)
def delete_server_metadata_item(self, server_id, key):
+ """Deletes a metadata item, by key, from a server.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteServerMetadataItem
+ """
resp, body = self.delete("servers/%s/metadata/%s" %
(server_id, key))
self.validate_response(schema.delete_server_metadata_item,
@@ -313,9 +355,19 @@
return rest_client.ResponseBody(resp, body)
def stop_server(self, server_id, **kwargs):
+ """Stops a running server and changes its status to SHUTOFF.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#stop
+ """
return self.action(server_id, 'os-stop', **kwargs)
def start_server(self, server_id, **kwargs):
+ """Starts a stopped server and changes its status to ACTIVE.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#start
+ """
return self.action(server_id, 'os-start', **kwargs)
def attach_volume(self, server_id, **kwargs):
@@ -341,14 +393,23 @@
return rest_client.ResponseBody(resp, body)
def detach_volume(self, server_id, volume_id): # noqa
- """Detaches a volume from a server instance."""
+ """Detaches a volume from a server instance.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteVolumeAttachment
+ """
resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
(server_id, volume_id))
self.validate_response(schema.detach_volume, resp, body)
return rest_client.ResponseBody(resp, body)
def show_volume_attachment(self, server_id, volume_id):
- """Return details about the given volume attachment."""
+ """Return details about the given volume attachment.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#
+ getVolumeAttachmentDetails
+ """
resp, body = self.get('servers/%s/os-volume_attachments/%s' % (
server_id, volume_id))
body = json.loads(body)
@@ -356,7 +417,11 @@
return rest_client.ResponseBody(resp, body)
def list_volume_attachments(self, server_id):
- """Returns the list of volume attachments for a given instance."""
+ """Returns the list of volume attachments for a given instance.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listVolumeAttachments
+ """
resp, body = self.get('servers/%s/os-volume_attachments' % (
server_id))
body = json.loads(body)
@@ -366,7 +431,8 @@
def add_security_group(self, server_id, **kwargs):
"""Add a security group to the server.
- Available params: TODO
+ Available params: http://developer.openstack.org/
+ api-ref-compute-v2.1.html#addSecurityGroup
"""
# TODO(oomichi): The api-site doesn't contain this API description.
# So the above should be changed to the api-site link after
@@ -377,7 +443,8 @@
def remove_security_group(self, server_id, **kwargs):
"""Remove a security group from the server.
- Available params: TODO
+ Available params: http://developer.openstack.org/
+ api-ref-compute-v2.1.html#removeSecurityGroup
"""
# TODO(oomichi): The api-site doesn't contain this API description.
# So the above should be changed to the api-site link after
@@ -507,7 +574,11 @@
return self.action(server_id, 'rescue', schema.rescue_server, **kwargs)
def unrescue_server(self, server_id):
- """Unrescue the provided server."""
+ """Unrescue the provided server.
+
+ Available params: http://developer.openstack.org/
+ api-ref-compute-v2.1.html#unrescue
+ """
return self.action(server_id, 'unrescue')
def show_server_diagnostics(self, server_id):
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
old mode 100644
new mode 100755
index a190e5f..b6dbe28
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -25,6 +25,11 @@
class ServicesClient(base_compute_client.BaseComputeClient):
def list_services(self, **params):
+ """Lists all running Compute services for a tenant.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listServices
+ """
url = 'os-services'
if params:
url += '?%s' % urllib.urlencode(params)
diff --git a/tempest/lib/services/compute/volumes_client.py b/tempest/lib/services/compute/volumes_client.py
old mode 100644
new mode 100755
index 41d9af2..2787779
--- a/tempest/lib/services/compute/volumes_client.py
+++ b/tempest/lib/services/compute/volumes_client.py
@@ -25,7 +25,11 @@
class VolumesClient(base_compute_client.BaseComputeClient):
def list_volumes(self, detail=False, **params):
- """List all the volumes created."""
+ """List all the volumes created.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#listVolumes
+ """
url = 'os-volumes'
if detail:
@@ -39,7 +43,11 @@
return rest_client.ResponseBody(resp, body)
def show_volume(self, volume_id):
- """Return the details of a single volume."""
+ """Return the details of a single volume.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#showVolume
+ """
url = "os-volumes/%s" % volume_id
resp, body = self.get(url)
body = json.loads(body)
@@ -59,7 +67,11 @@
return rest_client.ResponseBody(resp, body)
def delete_volume(self, volume_id):
- """Delete the Specified Volume."""
+ """Delete the Specified Volume.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#deleteVolume
+ """
resp, body = self.delete("os-volumes/%s" % volume_id)
self.validate_response(schema.delete_volume, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v2/token_client.py b/tempest/lib/services/identity/v2/token_client.py
index 5716027..a5d7c86 100644
--- a/tempest/lib/services/identity/v2/token_client.py
+++ b/tempest/lib/services/identity/v2/token_client.py
@@ -22,11 +22,11 @@
class TokenClient(rest_client.RestClient):
def __init__(self, auth_url, disable_ssl_certificate_validation=None,
- ca_certs=None, trace_requests=None):
+ ca_certs=None, trace_requests=None, **kwargs):
dscv = disable_ssl_certificate_validation
super(TokenClient, self).__init__(
None, None, None, disable_ssl_certificate_validation=dscv,
- ca_certs=ca_certs, trace_requests=trace_requests)
+ ca_certs=ca_certs, trace_requests=trace_requests, **kwargs)
if auth_url is None:
raise exceptions.IdentityError("Couldn't determine auth_url")
diff --git a/tempest/lib/services/identity/v3/token_client.py b/tempest/lib/services/identity/v3/token_client.py
index 964d43f..c1f7e7b 100644
--- a/tempest/lib/services/identity/v3/token_client.py
+++ b/tempest/lib/services/identity/v3/token_client.py
@@ -22,11 +22,11 @@
class V3TokenClient(rest_client.RestClient):
def __init__(self, auth_url, disable_ssl_certificate_validation=None,
- ca_certs=None, trace_requests=None):
+ ca_certs=None, trace_requests=None, **kwargs):
dscv = disable_ssl_certificate_validation
super(V3TokenClient, self).__init__(
None, None, None, disable_ssl_certificate_validation=dscv,
- ca_certs=ca_certs, trace_requests=trace_requests)
+ ca_certs=ca_certs, trace_requests=trace_requests, **kwargs)
if auth_url is None:
raise exceptions.IdentityError("Couldn't determine auth_url")
diff --git a/tempest/lib/services/network/base.py b/tempest/lib/services/network/base.py
index a6ada04..620e0f1 100644
--- a/tempest/lib/services/network/base.py
+++ b/tempest/lib/services/network/base.py
@@ -54,18 +54,26 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
- def create_resource(self, uri, post_data):
+ def create_resource(self, uri, post_data, expect_empty_body=False):
req_uri = self.uri_prefix + uri
req_post_data = json.dumps(post_data)
resp, body = self.post(req_uri, req_post_data)
- body = json.loads(body)
+ # NOTE: RFC allows both a valid non-empty body and an empty body for
+ # response of POST API. If a body is expected not empty, we decode the
+ # body. Otherwise we returns the body as it is.
+ if not expect_empty_body:
+ body = json.loads(body)
self.expected_success(201, resp.status)
return rest_client.ResponseBody(resp, body)
- def update_resource(self, uri, post_data):
+ def update_resource(self, uri, post_data, expect_empty_body=False):
req_uri = self.uri_prefix + uri
req_post_data = json.dumps(post_data)
resp, body = self.put(req_uri, req_post_data)
- body = json.loads(body)
+ # NOTE: RFC allows both a valid non-empty body and an empty body for
+ # response of PUT API. If a body is expected not empty, we decode the
+ # body. Otherwise we returns the body as it is.
+ if not expect_empty_body:
+ body = json.loads(body)
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/networks_client.py b/tempest/lib/services/network/networks_client.py
index 7d75bf7..6b601ee 100755
--- a/tempest/lib/services/network/networks_client.py
+++ b/tempest/lib/services/network/networks_client.py
@@ -19,7 +19,7 @@
"""Creates a network.
Available params: see http://developer.openstack.org/
- api-ref-networking-v2.html#createNetwork
+ api-ref/networking/v2/index.html#create-network
"""
uri = '/networks'
post_data = {'network': kwargs}
@@ -29,7 +29,7 @@
"""Updates a network.
Available params: see http://developer.openstack.org/
- api-ref-networking-v2.html#updateNetwork
+ api-ref/networking/v2/index.html#update-network
"""
uri = '/networks/%s' % network_id
post_data = {'network': kwargs}
@@ -39,7 +39,7 @@
"""Shows details for a network.
Available params: see http://developer.openstack.org/
- api-ref-networking-v2.html#showNetwork
+ api-ref/networking/v2/index.html#show-network-details
"""
uri = '/networks/%s' % network_id
return self.show_resource(uri, **fields)
@@ -52,7 +52,7 @@
"""Lists networks to which the tenant has access.
Available params: see http://developer.openstack.org/
- api-ref-networking-v2.html#listNetworks
+ api-ref/networking/v2/index.html#list-networks
"""
uri = '/networks'
return self.list_resources(uri, **filters)
@@ -61,7 +61,7 @@
"""Create multiple networks in a single request.
Available params: see http://developer.openstack.org/
- api-ref-networking-v2.html#bulkCreateNetwork
+ api-ref/networking/v2/index.html#bulk-create-networks
"""
uri = '/networks'
return self.create_resource(uri, kwargs)
diff --git a/tempest/lib/services/volume/__init__.py b/tempest/lib/services/volume/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/volume/__init__.py
diff --git a/tempest/lib/services/volume/v1/__init__.py b/tempest/lib/services/volume/v1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/volume/v1/__init__.py
diff --git a/tempest/services/volume/v1/json/availability_zone_client.py b/tempest/lib/services/volume/v1/availability_zone_client.py
similarity index 100%
rename from tempest/services/volume/v1/json/availability_zone_client.py
rename to tempest/lib/services/volume/v1/availability_zone_client.py
diff --git a/tempest/services/volume/v1/json/extensions_client.py b/tempest/lib/services/volume/v1/extensions_client.py
similarity index 100%
rename from tempest/services/volume/v1/json/extensions_client.py
rename to tempest/lib/services/volume/v1/extensions_client.py
diff --git a/tempest/services/volume/base/admin/base_hosts_client.py b/tempest/lib/services/volume/v1/hosts_client.py
similarity index 90%
rename from tempest/services/volume/base/admin/base_hosts_client.py
rename to tempest/lib/services/volume/v1/hosts_client.py
index 382e9a8..56ba12c 100644
--- a/tempest/services/volume/base/admin/base_hosts_client.py
+++ b/tempest/lib/services/volume/v1/hosts_client.py
@@ -19,8 +19,8 @@
from tempest.lib.common import rest_client
-class BaseHostsClient(rest_client.RestClient):
- """Client class to send CRUD Volume Hosts API requests"""
+class HostsClient(rest_client.RestClient):
+ """Client class to send CRUD Volume Host API V1 requests"""
def list_hosts(self, **params):
"""Lists all hosts."""
diff --git a/tempest/services/volume/base/admin/base_services_client.py b/tempest/lib/services/volume/v1/services_client.py
similarity index 91%
rename from tempest/services/volume/base/admin/base_services_client.py
rename to tempest/lib/services/volume/v1/services_client.py
index 861eb92..d438a34 100644
--- a/tempest/services/volume/base/admin/base_services_client.py
+++ b/tempest/lib/services/volume/v1/services_client.py
@@ -19,7 +19,8 @@
from tempest.lib.common import rest_client
-class BaseServicesClient(rest_client.RestClient):
+class ServicesClient(rest_client.RestClient):
+ """Volume V1 volume services client"""
def list_services(self, **params):
url = 'os-services'
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/volume/v2/__init__.py
diff --git a/tempest/services/volume/v2/json/availability_zone_client.py b/tempest/lib/services/volume/v2/availability_zone_client.py
similarity index 100%
rename from tempest/services/volume/v2/json/availability_zone_client.py
rename to tempest/lib/services/volume/v2/availability_zone_client.py
diff --git a/tempest/services/volume/v2/json/extensions_client.py b/tempest/lib/services/volume/v2/extensions_client.py
similarity index 100%
rename from tempest/services/volume/v2/json/extensions_client.py
rename to tempest/lib/services/volume/v2/extensions_client.py
diff --git a/tempest/services/volume/base/admin/base_hosts_client.py b/tempest/lib/services/volume/v2/hosts_client.py
similarity index 86%
copy from tempest/services/volume/base/admin/base_hosts_client.py
copy to tempest/lib/services/volume/v2/hosts_client.py
index 382e9a8..dd7c482 100644
--- a/tempest/services/volume/base/admin/base_hosts_client.py
+++ b/tempest/lib/services/volume/v2/hosts_client.py
@@ -1,4 +1,4 @@
-# Copyright 2013 OpenStack Foundation
+# Copyright 2014 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -19,8 +19,9 @@
from tempest.lib.common import rest_client
-class BaseHostsClient(rest_client.RestClient):
- """Client class to send CRUD Volume Hosts API requests"""
+class HostsClient(rest_client.RestClient):
+ """Client class to send CRUD Volume V2 API requests"""
+ api_version = "v2"
def list_hosts(self, **params):
"""Lists all hosts."""
diff --git a/tempest/services/volume/base/admin/base_services_client.py b/tempest/lib/services/volume/v2/services_client.py
similarity index 85%
copy from tempest/services/volume/base/admin/base_services_client.py
copy to tempest/lib/services/volume/v2/services_client.py
index 861eb92..bc55469 100644
--- a/tempest/services/volume/base/admin/base_services_client.py
+++ b/tempest/lib/services/volume/v2/services_client.py
@@ -1,4 +1,4 @@
-# Copyright 2014 NEC Corporation
+# Copyright 2014 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -19,7 +19,9 @@
from tempest.lib.common import rest_client
-class BaseServicesClient(rest_client.RestClient):
+class ServicesClient(rest_client.RestClient):
+ """Client class to send CRUD Volume V2 API requests"""
+ api_version = "v2"
def list_services(self, **params):
url = 'os-services'
diff --git a/tempest/manager.py b/tempest/manager.py
index 3d495b6..e3174d4 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -15,15 +15,15 @@
from oslo_log import log as logging
-from tempest import clients
+from tempest import clients as tempest_clients
from tempest import config
-from tempest import service_clients
+from tempest.lib.services import clients
CONF = config.CONF
LOG = logging.getLogger(__name__)
-class Manager(service_clients.ServiceClients):
+class Manager(clients.ServiceClients):
"""Service client manager class for backward compatibility
The former manager.Manager is not a stable interface in Tempest,
@@ -37,7 +37,7 @@
"soon as the client manager becomes available in tempest.lib.")
LOG.warning(msg)
dscv = CONF.identity.disable_ssl_certificate_validation
- _, uri = clients.get_auth_provider_class(credentials)
+ _, uri = tempest_clients.get_auth_provider_class(credentials)
super(Manager, self).__init__(
credentials=credentials, scope=scope,
identity_uri=uri,
@@ -58,5 +58,5 @@
"as such it should not imported directly. It will be removed as "
"the client manager becomes available in tempest.lib.")
LOG.warning(msg)
- return clients.get_auth_provider(credentials=credentials,
- pre_auth=pre_auth, scope=scope)
+ return tempest_clients.get_auth_provider(credentials=credentials,
+ pre_auth=pre_auth, scope=scope)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index b151375..6da0570 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -127,6 +127,21 @@
wait_dict['client'] = waiter_client
self.cleanup_waits.append(wait_dict)
+ def _create_port(self, network_id, client=None, namestart='port-quotatest',
+ **kwargs):
+ if not client:
+ client = self.ports_client
+ name = data_utils.rand_name(namestart)
+ result = client.create_port(
+ name=name,
+ network_id=network_id,
+ **kwargs)
+ self.assertIsNotNone(result, 'Unable to allocate port')
+ port = result['port']
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ client.delete_port, port['id'])
+ return port
+
def _wait_for_cleanups(self):
# To handle async delete actions, a list of waits is added
# which will be iterated over as the last step of clearing the
@@ -183,7 +198,7 @@
# every network
if vnic_type:
ports = []
- networks = []
+
create_port_body = {'binding:vnic_type': vnic_type,
'namestart': 'port-smoke'}
if kwargs:
@@ -204,25 +219,30 @@
if security_groups_ids:
create_port_body[
'security_groups'] = security_groups_ids
- networks = kwargs.pop('networks')
+ networks = kwargs.pop('networks', [])
+ else:
+ networks = []
# If there are no networks passed to us we look up
- # for the project's private networks and create a port
- # if there is only one private network. The same behaviour
- # as we would expect when passing the call to the clients
- # with no networks
+ # for the project's private networks and create a port.
+ # The same behaviour as we would expect when passing
+ # the call to the clients with no networks
if not networks:
networks = clients.networks_client.list_networks(
- filters={'router:external': False})
- self.assertEqual(1, len(networks),
- "There is more than one"
- " network for the tenant")
+ **{'router:external': False, 'fields': 'id'})['networks']
+
+ # It's net['uuid'] if networks come from kwargs
+ # and net['id'] if they come from
+ # clients.networks_client.list_networks
for net in networks:
- net_id = net['uuid']
- port = self._create_port(network_id=net_id,
- client=clients.ports_client,
- **create_port_body)
- ports.append({'port': port['id']})
+ net_id = net.get('uuid', net['id'])
+ if 'port' not in net:
+ port = self._create_port(network_id=net_id,
+ client=clients.ports_client,
+ **create_port_body)
+ ports.append({'port': port['id']})
+ else:
+ ports.append({'port': net['port']})
if ports:
kwargs['networks'] = ports
self.ports = ports
@@ -808,21 +828,6 @@
return subnet
- def _create_port(self, network_id, client=None, namestart='port-quotatest',
- **kwargs):
- if not client:
- client = self.ports_client
- name = data_utils.rand_name(namestart)
- result = client.create_port(
- name=name,
- network_id=network_id,
- **kwargs)
- self.assertIsNotNone(result, 'Unable to allocate port')
- port = result['port']
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- client.delete_port, port['id'])
- return port
-
def _get_server_port_id_and_ip4(self, server, ip_addr=None):
ports = self._list_ports(device_id=server['id'], fixed_ip=ip_addr)
# A port can have more then one IP address in some cases.
@@ -832,6 +837,7 @@
# NOTE(vsaienko) With Ironic, instances live on separate hardware
# servers. Neutron does not bind ports for Ironic instances, as a
# result the port remains in the DOWN state.
+ # TODO(vsaienko) remove once bug: #1599836 is resolved.
if CONF.service_available.ironic:
p_status.append('DOWN')
port_map = [(p["id"], fxip["ip_address"])
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 9c48080..e0e1204 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -24,6 +24,7 @@
from tempest import config
from tempest import exceptions
from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
from tempest.scenario import manager
from tempest import test
@@ -410,6 +411,7 @@
@test.idempotent_id('1546850e-fbaa-42f5-8b5f-03d8a6a95f15')
@testtools.skipIf(CONF.baremetal.driver_enabled,
'Baremetal relies on a shared physical network.')
+ @decorators.skip_because(bug="1610994")
@test.services('compute', 'network')
def test_connectivity_between_vms_on_different_networks(self):
"""Test connectivity between VMs on different networks
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 59ebb7a..364b6f5 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -18,6 +18,7 @@
from tempest import config
from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
from tempest.scenario import manager
from tempest import test
@@ -254,6 +255,7 @@
self._prepare_and_test(address6_mode='dhcpv6-stateless', n_subnets6=2,
dualnet=True)
+ @decorators.skip_because(bug="1540983")
@test.idempotent_id('9178ad42-10e4-47e9-8987-e02b170cc5cd')
@test.services('compute', 'network')
def test_dualnet_multi_prefix_slaac(self):
diff --git a/tempest/service_clients.py b/tempest/service_clients.py
deleted file mode 100644
index f131b58..0000000
--- a/tempest/service_clients.py
+++ /dev/null
@@ -1,404 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-import importlib
-import inspect
-import logging
-
-from tempest.lib import auth
-from tempest.lib import exceptions
-from tempest.lib.services import clients
-from tempest.lib.services import compute
-from tempest.lib.services import image
-from tempest.lib.services import network
-
-LOG = logging.getLogger(__name__)
-
-client_modules_by_service_name = {
- 'compute': compute,
- 'image.v1': image.v1,
- 'image.v2': image.v2,
- 'network': network
-}
-
-
-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
-
- The list of available modules can be used for automatic configuration.
-
- :raise PluginRegistrationException: if a plugin exposes a service_version
- already defined by Tempest or another plugin.
-
- Examples:
-
- >>> from tempest import config
- >>> params = {}
- >>> for service_version in available_modules():
- >>> service = service_version.split('.')[0]
- >>> params[service] = config.service_client_config(service)
- >>> service_clients = ServiceClients(creds, identity_uri,
- >>> client_parameters=params)
- """
- extra_service_versions = set([])
- plugin_services = clients.ClientsRegistry().get_service_clients()
- for plugin_name in plugin_services:
- plug_service_versions = set([x['service_version'] for x in
- plugin_services[plugin_name]])
- # If a plugin exposes a duplicate service_version raise an exception
- if plug_service_versions:
- if not plug_service_versions.isdisjoint(extra_service_versions):
- detailed_error = (
- 'Plugin %s is trying to register a service %s already '
- 'claimed by another one' % (plugin_name,
- extra_service_versions &
- plug_service_versions))
- raise exceptions.PluginRegistrationException(
- name=plugin_name, detailed_error=detailed_error)
- if not plug_service_versions.isdisjoint(tempest_modules()):
- detailed_error = (
- 'Plugin %s is trying to register a service %s already '
- 'claimed by a Tempest one' % (plugin_name,
- tempest_modules() &
- plug_service_versions))
- raise exceptions.PluginRegistrationException(
- name=plugin_name, detailed_error=detailed_error)
- extra_service_versions |= plug_service_versions
- return tempest_modules() | extra_service_versions
-
-
-class ClientsFactory(object):
- """Builds service clients for a service client module
-
- This class implements the logic of feeding service client parameters
- to service clients from a specific module. It allows setting the
- parameters once and obtaining new instances of the clients without the
- need of passing any parameter.
-
- ClientsFactory can be used directly, or consumed via the `ServiceClients`
- class, which manages the authorization part.
- """
-
- def __init__(self, module_path, client_names, auth_provider, **kwargs):
- """Initialises the client factory
-
- :param module_path Path to module that includes all service clients.
- All service client classes must be exposed by a single module.
- If they are separated in different modules, defining __all__
- in the root module can help, similar to what is done by service
- clients in tempest.
- :param client_names List or set of names of the service client classes.
- :param auth_provider The auth provider used to initialise client.
- :param kwargs Parameters to be passed to all clients. Parameters values
- can be overwritten when clients are initialised, but parameters
- cannot be deleted.
- :raise ImportError if the specified module_path cannot be imported
-
- Example:
-
- >>> # Get credentials and an auth_provider
- >>> clients = ClientsFactory(
- >>> module_path='my_service.my_service_clients',
- >>> client_names=['ServiceClient1', 'ServiceClient2'],
- >>> auth_provider=auth_provider,
- >>> service='my_service',
- >>> region='region1')
- >>> my_api_client = clients.MyApiClient()
- >>> my_api_client_region2 = clients.MyApiClient(region='region2')
-
- """
- # Import the module. If it's not importable, the raised exception
- # provides good enough information about what happened
- _module = importlib.import_module(module_path)
- # If any of the classes is not in the module we fail
- for class_name in client_names:
- # TODO(andreaf) This always passes all parameters to all clients.
- # In future to allow clients to specify the list of parameters
- # that they accept based out of a list of standard ones.
-
- # Obtain the class
- klass = self._get_class(_module, class_name)
- final_kwargs = copy.copy(kwargs)
-
- # Set the function as an attribute of the factory
- setattr(self, class_name, self._get_partial_class(
- klass, auth_provider, final_kwargs))
-
- @classmethod
- def _get_partial_class(cls, klass, auth_provider, kwargs):
-
- # Define a function that returns a new class instance by
- # combining default kwargs with extra ones
- def partial_class(**later_kwargs):
- kwargs.update(later_kwargs)
- return klass(auth_provider=auth_provider, **kwargs)
-
- return partial_class
-
- @classmethod
- def _get_class(cls, module, class_name):
- klass = getattr(module, class_name, None)
- if not klass:
- msg = 'Invalid class name, %s is not found in %s'
- raise AttributeError(msg % (class_name, module))
- if not inspect.isclass(klass):
- msg = 'Expected a class, got %s of type %s instead'
- raise TypeError(msg % (klass, type(klass)))
- return klass
-
-
-class ServiceClients(object):
- """Service client provider class
-
- The ServiceClients object provides a useful means for tests to access
- service clients configured for a specified set of credentials.
- It hides some of the complexity from the authorization and configuration
- layers.
-
- Examples:
-
- >>> from tempest import service_clients
- >>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
- >>> johndoe_clients = service_clients.ServiceClients(johndoe,
- >>> identity_uri)
- >>> johndoe_servers = johndoe_clients.servers_client.list_servers()
-
- """
- # NOTE(andreaf) This class does not depend on tempest configuration
- # and its meant for direct consumption by external clients such as tempest
- # plugins. Tempest provides a wrapper class, `clients.Manager`, that
- # 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='', 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.
- :param region: Default value of region for service clients.
- :param scope: default scope for tokens produced by the auth provider
- :param disable_ssl_certificate_validation Applies to auth and to all
- 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._registered_services = set([])
- self.credentials = credentials
- self.identity_uri = identity_uri
- if not identity_uri:
- raise exceptions.InvalidCredentials(
- 'ServiceClients requires a non-empty identity_uri.')
- self.region = region
- # Check if passed or default credentials are valid
- if not self.credentials.is_valid():
- raise exceptions.InvalidCredentials()
- # Get the identity classes matching the provided credentials
- # TODO(andreaf) Define a new interface in Credentials to get
- # the API version from an instance
- identity = [(k, auth.IDENTITY_VERSION[k][1]) for k in
- auth.IDENTITY_VERSION.keys() if
- isinstance(self.credentials, auth.IDENTITY_VERSION[k][0])]
- # Zero matches or more than one are both not valid.
- if len(identity) != 1:
- raise exceptions.InvalidCredentials()
- self.auth_version, auth_provider_class = identity[0]
- self.dscv = disable_ssl_certificate_validation
- self.ca_certs = ca_certs
- self.trace_requests = trace_requests
- # Creates an auth provider for the credentials
- self.auth_provider = auth_provider_class(
- 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()))
-
- # Register service clients owned by tempest
- for service in tempest_modules():
- if service in list(client_modules_by_service_name):
- attribute = service.replace('.', '_')
- configs = service.split('.')[0]
- module = client_modules_by_service_name[service]
- self.register_service_client_module(
- attribute, service, module.__name__,
- module.__all__, **self.parameters[configs])
-
- # Register service clients from plugins
- clients_registry = clients.ClientsRegistry()
- plugin_service_clients = clients_registry.get_service_clients()
- for plugin in plugin_service_clients:
- service_clients = plugin_service_clients[plugin]
- # Each plugin returns a list of service client parameters
- for service_client in service_clients:
- # NOTE(andreaf) If a plugin cannot register, stop the
- # registration process, log some details to help
- # troubleshooting, and re-raise
- try:
- self.register_service_client_module(**service_client)
- except Exception:
- LOG.exception(
- 'Failed to register service client from plugin %s '
- 'with parameters %s' % (plugin, service_client))
- raise
-
- def register_service_client_module(self, name, service_version,
- module_path, client_names, **kwargs):
- """Register a service client module
-
- Initiates a client factory for the specified module, using this
- class auth_provider, and accessible via a `name` attribute in the
- service client.
-
- :param name: Name used to access the client
- :param service_version: Name of the service complete with version.
- Used to track registered services. When a plugin implements it,
- it can be used by other plugins to obtain their configuration.
- :param module_path: Path to module that includes all service clients.
- All service client classes must be exposed by a single module.
- If they are separated in different modules, defining __all__
- in the root module can help, similar to what is done by service
- clients in tempest.
- :param client_names: List or set of names of service client classes.
- :param kwargs: Extra optional parameters to be passed to all clients.
- ServiceClient provides defaults for region, dscv, ca_certs and
- trace_requests.
- :raise ServiceClientRegistrationException: if the provided name is
- already in use or if service_version is already registered.
- :raise ImportError: if module_path cannot be imported.
- """
- if hasattr(self, name):
- using_name = getattr(self, name)
- detailed_error = 'Module name already in use: %s' % using_name
- raise exceptions.ServiceClientRegistrationException(
- name=name, service_version=service_version,
- module_path=module_path, client_names=client_names,
- detailed_error=detailed_error)
- if service_version in self.registered_services:
- detailed_error = 'Service %s already registered.' % service_version
- raise exceptions.ServiceClientRegistrationException(
- name=name, service_version=service_version,
- module_path=module_path, client_names=client_names,
- detailed_error=detailed_error)
- params = dict(region=self.region,
- disable_ssl_certificate_validation=self.dscv,
- ca_certs=self.ca_certs,
- trace_requests=self.trace_requests)
- params.update(kwargs)
- # Instantiate the client factory
- _factory = ClientsFactory(module_path=module_path,
- client_names=client_names,
- auth_provider=self.auth_provider,
- **params)
- # Adds the client factory to the service_client
- setattr(self, name, _factory)
- # Add the name of the new service in self.SERVICES for discovery
- self._registered_services.add(service_version)
-
- @property
- def registered_services(self):
- # TODO(andreaf) Temporary set needed until all services are migrated
- _non_migrated_services = tempest_modules() - set(
- client_modules_by_service_name)
- return self._registered_services | _non_migrated_services
-
- 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/identity/v3/json/services_client.py b/tempest/services/identity/v3/json/services_client.py
index e863016..95caf7d 100644
--- a/tempest/services/identity/v3/json/services_client.py
+++ b/tempest/services/identity/v3/json/services_client.py
@@ -18,6 +18,7 @@
"""
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
@@ -57,13 +58,21 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def delete_service(self, serv_id):
- url = "services/" + serv_id
+ def delete_service(self, service_id):
+ url = "services/" + service_id
resp, body = self.delete(url)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
- def list_services(self):
+ def list_services(self, **params):
+ """List services.
+
+ Available params: see http://developer.openstack.org/
+ api-ref/identity/v3/#list-services
+ """
+ url = 'services'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
resp, body = self.get('services')
self.expected_success(200, resp.status)
body = json.loads(body)
diff --git a/tempest/services/volume/base/admin/base_quotas_client.py b/tempest/services/volume/base/admin/base_quotas_client.py
index 83816f2..2c1f50d 100644
--- a/tempest/services/volume/base/admin/base_quotas_client.py
+++ b/tempest/services/volume/base/admin/base_quotas_client.py
@@ -21,8 +21,6 @@
class BaseQuotasClient(rest_client.RestClient):
"""Client class to send CRUD Volume Quotas API requests"""
- TYPE = "json"
-
def show_default_quota_set(self, tenant_id):
"""List the default volume quota set for a tenant."""
@@ -44,12 +42,6 @@
body = jsonutils.loads(body)
return rest_client.ResponseBody(resp, body)
- def show_quota_usage(self, tenant_id):
- """List the quota set for a tenant."""
-
- body = self.show_quota_set(tenant_id, params={'usage': True})
- return body
-
def update_quota_set(self, tenant_id, **kwargs):
"""Updates quota set
diff --git a/tempest/services/volume/base/base_backups_client.py b/tempest/services/volume/base/base_backups_client.py
index 63c5417..fc247a9 100644
--- a/tempest/services/volume/base/base_backups_client.py
+++ b/tempest/services/volume/base/base_backups_client.py
@@ -116,14 +116,9 @@
self.build_timeout))
raise exceptions.TimeoutException(message)
- def wait_for_backup_deletion(self, backup_id):
- """Waits for backup deletion"""
- start_time = int(time.time())
- while True:
- try:
- self.show_backup(backup_id)
- except lib_exc.NotFound:
- return
- if int(time.time()) - start_time >= self.build_timeout:
- raise exceptions.TimeoutException
- time.sleep(self.build_interval)
+ def is_resource_deleted(self, id):
+ try:
+ self.show_backup(id)
+ except lib_exc.NotFound:
+ return True
+ return False
diff --git a/tempest/services/volume/v1/__init__.py b/tempest/services/volume/v1/__init__.py
index 6bdb8c4..52d2942 100644
--- a/tempest/services/volume/v1/__init__.py
+++ b/tempest/services/volume/v1/__init__.py
@@ -12,15 +12,14 @@
# License for the specific language governing permissions and limitations under
# the License.
-from tempest.services.volume.v1.json.admin.hosts_client import HostsClient
-from tempest.services.volume.v1.json.admin.quotas_client import QuotasClient
-from tempest.services.volume.v1.json.admin.services_client import \
- ServicesClient
-from tempest.services.volume.v1.json.admin.types_client import TypesClient
-from tempest.services.volume.v1.json.availability_zone_client import \
+from tempest.lib.services.volume.v1.availability_zone_client import \
AvailabilityZoneClient
+from tempest.lib.services.volume.v1.extensions_client import ExtensionsClient
+from tempest.lib.services.volume.v1.hosts_client import HostsClient
+from tempest.lib.services.volume.v1.services_client import ServicesClient
+from tempest.services.volume.v1.json.admin.quotas_client import QuotasClient
+from tempest.services.volume.v1.json.admin.types_client import TypesClient
from tempest.services.volume.v1.json.backups_client import BackupsClient
-from tempest.services.volume.v1.json.extensions_client import ExtensionsClient
from tempest.services.volume.v1.json.qos_client import QosSpecsClient
from tempest.services.volume.v1.json.snapshots_client import SnapshotsClient
from tempest.services.volume.v1.json.volumes_client import VolumesClient
diff --git a/tempest/services/volume/v1/json/admin/hosts_client.py b/tempest/services/volume/v1/json/admin/hosts_client.py
deleted file mode 100644
index 3b52968..0000000
--- a/tempest/services/volume/v1/json/admin/hosts_client.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.services.volume.base.admin import base_hosts_client
-
-
-class HostsClient(base_hosts_client.BaseHostsClient):
- """Client class to send CRUD Volume Host API V1 requests"""
diff --git a/tempest/services/volume/v1/json/admin/services_client.py b/tempest/services/volume/v1/json/admin/services_client.py
deleted file mode 100644
index 2bffd55..0000000
--- a/tempest/services/volume/v1/json/admin/services_client.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2014 NEC Corporation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.services.volume.base.admin import base_services_client
-
-
-class ServicesClient(base_services_client.BaseServicesClient):
- """Volume V1 volume services client"""
diff --git a/tempest/services/volume/v2/__init__.py b/tempest/services/volume/v2/__init__.py
index c75b0e5..26fbc2b 100644
--- a/tempest/services/volume/v2/__init__.py
+++ b/tempest/services/volume/v2/__init__.py
@@ -12,15 +12,14 @@
# License for the specific language governing permissions and limitations under
# the License.
-from tempest.services.volume.v2.json.admin.hosts_client import HostsClient
-from tempest.services.volume.v2.json.admin.quotas_client import QuotasClient
-from tempest.services.volume.v2.json.admin.services_client import \
- ServicesClient
-from tempest.services.volume.v2.json.admin.types_client import TypesClient
-from tempest.services.volume.v2.json.availability_zone_client import \
+from tempest.lib.services.volume.v2.availability_zone_client import \
AvailabilityZoneClient
+from tempest.lib.services.volume.v2.extensions_client import ExtensionsClient
+from tempest.lib.services.volume.v2.hosts_client import HostsClient
+from tempest.lib.services.volume.v2.services_client import ServicesClient
+from tempest.services.volume.v2.json.admin.quotas_client import QuotasClient
+from tempest.services.volume.v2.json.admin.types_client import TypesClient
from tempest.services.volume.v2.json.backups_client import BackupsClient
-from tempest.services.volume.v2.json.extensions_client import ExtensionsClient
from tempest.services.volume.v2.json.qos_client import QosSpecsClient
from tempest.services.volume.v2.json.snapshots_client import SnapshotsClient
from tempest.services.volume.v2.json.volumes_client import VolumesClient
diff --git a/tempest/services/volume/v2/json/admin/hosts_client.py b/tempest/services/volume/v2/json/admin/hosts_client.py
deleted file mode 100644
index e092c6a..0000000
--- a/tempest/services/volume/v2/json/admin/hosts_client.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2014 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.services.volume.base.admin import base_hosts_client
-
-
-class HostsClient(base_hosts_client.BaseHostsClient):
- """Client class to send CRUD Volume V2 API requests"""
- api_version = "v2"
diff --git a/tempest/services/volume/v2/json/admin/services_client.py b/tempest/services/volume/v2/json/admin/services_client.py
deleted file mode 100644
index db19ba9..0000000
--- a/tempest/services/volume/v2/json/admin/services_client.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2014 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.services.volume.base.admin import base_services_client
-
-
-class ServicesClient(base_services_client.BaseServicesClient):
- """Client class to send CRUD Volume V2 API requests"""
- api_version = "v2"
diff --git a/tempest/test_discover/plugins.py b/tempest/test_discover/plugins.py
index cfb0c7f..eb50126 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -88,7 +88,7 @@
>>> 'client_names': ['API1Client', 'API2Client'],
>>> }
>>> params.update(myservice_config)
- >>> return [params]
+ >>> return [params]
>>> # Example implementation with two service clients
>>> foo1_config = config.service_client_config('foo')
@@ -107,7 +107,7 @@
>>> 'client_names': ['API1Client', 'API2Client'],
>>> }
>>> params_foo2.update(foo2_config)
- >>> return [params_foo1, params_foo2]
+ >>> return [params_foo1, params_foo2]
"""
return []
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
deleted file mode 100644
index 5ec9720..0000000
--- a/tempest/tests/cmd/test_javelin.py
+++ /dev/null
@@ -1,422 +0,0 @@
-#!/usr/bin/env python
-#
-# 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 mock
-from oslotest import mockpatch
-
-from tempest.cmd import javelin
-from tempest.lib import exceptions as lib_exc
-from tempest.tests import base
-
-
-class JavelinUnitTest(base.TestCase):
-
- def setUp(self):
- super(JavelinUnitTest, self).setUp()
- javelin.LOG = mock.MagicMock()
- self.fake_client = mock.MagicMock()
- self.fake_object = mock.MagicMock()
-
- def test_load_resources(self):
- with mock.patch('six.moves.builtins.open', mock.mock_open(),
- create=True) as open_mock:
- with mock.patch('yaml.load', mock.MagicMock(),
- create=True) as load_mock:
- javelin.load_resources(self.fake_object)
- load_mock.assert_called_once_with(open_mock(self.fake_object))
-
- def test_keystone_admin(self):
- self.useFixture(mockpatch.PatchObject(javelin, "OSClient"))
- javelin.OPTS = self.fake_object
- javelin.keystone_admin()
- javelin.OSClient.assert_called_once_with(
- self.fake_object.os_username,
- self.fake_object.os_password,
- self.fake_object.os_tenant_name)
-
- def test_client_for_user(self):
- fake_user = mock.MagicMock()
- javelin.USERS = {fake_user['name']: fake_user}
- self.useFixture(mockpatch.PatchObject(javelin, "OSClient"))
- javelin.client_for_user(fake_user['name'])
- javelin.OSClient.assert_called_once_with(
- fake_user['name'], fake_user['pass'], fake_user['tenant'])
-
- def test_client_for_non_existing_user(self):
- fake_non_existing_user = self.fake_object
- fake_user = mock.MagicMock()
- javelin.USERS = {fake_user['name']: fake_user}
- self.useFixture(mockpatch.PatchObject(javelin, "OSClient"))
- javelin.client_for_user(fake_non_existing_user['name'])
- self.assertFalse(javelin.OSClient.called)
-
- def test_attach_volumes(self):
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
-
- self.useFixture(mockpatch.PatchObject(
- javelin, "_get_volume_by_name",
- return_value=self.fake_object.volume))
-
- self.useFixture(mockpatch.PatchObject(
- javelin, "_get_server_by_name",
- return_value=self.fake_object.server))
-
- javelin.attach_volumes([self.fake_object])
-
- mocked_function = self.fake_client.volumes.attach_volume
- mocked_function.assert_called_once_with(
- self.fake_object.volume['id'],
- instance_uuid=self.fake_object.server['id'],
- mountpoint=self.fake_object['device'])
-
-
-class TestCreateResources(JavelinUnitTest):
- def test_create_tenants(self):
-
- self.fake_client.tenants.list_tenants.return_value = {'tenants': []}
- self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
- return_value=self.fake_client))
-
- javelin.create_tenants([self.fake_object['name']])
-
- mocked_function = self.fake_client.tenants.create_tenant
- mocked_function.assert_called_once_with(name=self.fake_object['name'])
-
- def test_create_duplicate_tenant(self):
- self.fake_client.tenants.list_tenants.return_value = {'tenants': [
- {'name': self.fake_object['name']}]}
- self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
- return_value=self.fake_client))
-
- javelin.create_tenants([self.fake_object['name']])
-
- mocked_function = self.fake_client.tenants.create_tenant
- self.assertFalse(mocked_function.called)
-
- def test_create_users(self):
- self.useFixture(mockpatch.Patch(
- 'tempest.common.identity.get_tenant_by_name',
- return_value=self.fake_object['tenant']))
- self.useFixture(mockpatch.Patch(
- 'tempest.common.identity.get_user_by_username',
- side_effect=lib_exc.NotFound("user is not found")))
- self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
- return_value=self.fake_client))
-
- javelin.create_users([self.fake_object])
-
- fake_tenant_id = self.fake_object['tenant']['id']
- fake_email = "%s@%s" % (self.fake_object['user'], fake_tenant_id)
- mocked_function = self.fake_client.users.create_user
- mocked_function.assert_called_once_with(
- name=self.fake_object['name'],
- password=self.fake_object['password'],
- tenantId=fake_tenant_id,
- email=fake_email,
- enabled=True)
-
- def test_create_user_missing_tenant(self):
- self.useFixture(mockpatch.Patch(
- 'tempest.common.identity.get_tenant_by_name',
- side_effect=lib_exc.NotFound("tenant is not found")))
- self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
- return_value=self.fake_client))
-
- javelin.create_users([self.fake_object])
-
- mocked_function = self.fake_client.users.create_user
- self.assertFalse(mocked_function.called)
-
- def test_create_objects(self):
-
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- self.useFixture(mockpatch.PatchObject(javelin, "_assign_swift_role"))
- self.useFixture(mockpatch.PatchObject(javelin, "_file_contents",
- return_value=self.fake_object.content))
-
- javelin.create_objects([self.fake_object])
-
- mocked_function = self.fake_client.containers.create_container
- mocked_function.assert_called_once_with(self.fake_object['container'])
- mocked_function = self.fake_client.objects.create_object
- mocked_function.assert_called_once_with(self.fake_object['container'],
- self.fake_object['name'],
- self.fake_object.content)
-
- def test_create_images(self):
- self.fake_client.images.create_image.return_value = \
- self.fake_object['body']
-
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- self.useFixture(mockpatch.PatchObject(javelin, "_get_image_by_name",
- return_value=[]))
- self.useFixture(mockpatch.PatchObject(javelin, "_resolve_image",
- return_value=(None, None)))
-
- with mock.patch('six.moves.builtins.open', mock.mock_open(),
- create=True) as open_mock:
- javelin.create_images([self.fake_object])
-
- mocked_function = self.fake_client.images.create_image
- mocked_function.assert_called_once_with(self.fake_object['name'],
- self.fake_object['format'],
- self.fake_object['format'])
-
- mocked_function = self.fake_client.images.store_image_file
- fake_image_id = self.fake_object['body'].get('id')
- mocked_function.assert_called_once_with(fake_image_id, open_mock())
-
- def test_create_networks(self):
- self.fake_client.networks.list_networks.return_value = {
- 'networks': []}
-
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
-
- javelin.create_networks([self.fake_object])
-
- mocked_function = self.fake_client.networks.create_network
- mocked_function.assert_called_once_with(name=self.fake_object['name'])
-
- def test_create_subnet(self):
-
- fake_network = self.fake_object['network']
-
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
- return_value=fake_network))
-
- fake_netaddr = mock.MagicMock()
- self.useFixture(mockpatch.PatchObject(javelin, "netaddr",
- return_value=fake_netaddr))
- fake_version = javelin.netaddr.IPNetwork().version
-
- javelin.create_subnets([self.fake_object])
-
- mocked_function = self.fake_client.networks.create_subnet
- mocked_function.assert_called_once_with(network_id=fake_network['id'],
- cidr=self.fake_object['range'],
- name=self.fake_object['name'],
- ip_version=fake_version)
-
- @mock.patch("tempest.common.waiters.wait_for_volume_status")
- def test_create_volumes(self, mock_wait_for_volume_status):
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
- return_value=None))
- self.fake_client.volumes.create_volume.return_value = \
- self.fake_object.body
-
- javelin.create_volumes([self.fake_object])
-
- mocked_function = self.fake_client.volumes.create_volume
- mocked_function.assert_called_once_with(
- size=self.fake_object['gb'],
- display_name=self.fake_object['name'])
- mock_wait_for_volume_status.assert_called_once_with(
- self.fake_client.volumes, self.fake_object.body['volume']['id'],
- 'available')
-
- @mock.patch("tempest.common.waiters.wait_for_volume_status")
- def test_create_volume_existing(self, mock_wait_for_volume_status):
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
- return_value=self.fake_object))
- self.fake_client.volumes.create_volume.return_value = \
- self.fake_object.body
-
- javelin.create_volumes([self.fake_object])
-
- mocked_function = self.fake_client.volumes.create_volume
- self.assertFalse(mocked_function.called)
- self.assertFalse(mock_wait_for_volume_status.called)
-
- def test_create_router(self):
-
- self.fake_client.routers.list_routers.return_value = {'routers': []}
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
-
- javelin.create_routers([self.fake_object])
-
- mocked_function = self.fake_client.networks.create_router
- mocked_function.assert_called_once_with(name=self.fake_object['name'])
-
- def test_create_router_existing(self):
- self.fake_client.routers.list_routers.return_value = {
- 'routers': [self.fake_object]}
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
-
- javelin.create_routers([self.fake_object])
-
- mocked_function = self.fake_client.networks.create_router
- self.assertFalse(mocked_function.called)
-
- def test_create_secgroup(self):
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- self.fake_client.secgroups.list_security_groups.return_value = (
- {'security_groups': []})
- self.fake_client.secgroups.create_security_group.return_value = \
- {'security_group': {'id': self.fake_object['secgroup_id']}}
-
- javelin.create_secgroups([self.fake_object])
-
- mocked_function = self.fake_client.secgroups.create_security_group
- mocked_function.assert_called_once_with(
- name=self.fake_object['name'],
- description=self.fake_object['description'])
-
-
-class TestDestroyResources(JavelinUnitTest):
-
- def test_destroy_tenants(self):
-
- fake_tenant = self.fake_object['tenant']
- fake_auth = self.fake_client
- self.useFixture(mockpatch.Patch(
- 'tempest.common.identity.get_tenant_by_name',
- return_value=fake_tenant))
- self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
- return_value=fake_auth))
- javelin.destroy_tenants([fake_tenant])
-
- mocked_function = fake_auth.tenants.delete_tenant
- mocked_function.assert_called_once_with(fake_tenant['id'])
-
- def test_destroy_users(self):
-
- fake_user = self.fake_object['user']
- fake_tenant = self.fake_object['tenant']
-
- fake_auth = self.fake_client
- fake_auth.tenants.list_tenants.return_value = \
- {'tenants': [fake_tenant]}
- fake_auth.users.list_users.return_value = {'users': [fake_user]}
-
- self.useFixture(mockpatch.Patch(
- 'tempest.common.identity.get_user_by_username',
- return_value=fake_user))
- self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
- return_value=fake_auth))
-
- javelin.destroy_users([fake_user])
-
- mocked_function = fake_auth.users.delete_user
- mocked_function.assert_called_once_with(fake_user['id'])
-
- def test_destroy_objects(self):
-
- self.fake_client.objects.delete_object.return_value = \
- {'status': "200"}, ""
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- javelin.destroy_objects([self.fake_object])
-
- mocked_function = self.fake_client.objects.delete_object
- mocked_function.asswert_called_once(self.fake_object['container'],
- self.fake_object['name'])
-
- def test_destroy_images(self):
-
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- self.useFixture(mockpatch.PatchObject(javelin, "_get_image_by_name",
- return_value=self.fake_object['image']))
-
- javelin.destroy_images([self.fake_object])
-
- mocked_function = self.fake_client.images.delete_image
- mocked_function.assert_called_once_with(
- self.fake_object['image']['id'])
-
- def test_destroy_networks(self):
-
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- self.useFixture(mockpatch.PatchObject(
- javelin, "_get_resource_by_name",
- return_value=self.fake_object['resource']))
-
- javelin.destroy_networks([self.fake_object])
-
- mocked_function = self.fake_client.networks.delete_network
- mocked_function.assert_called_once_with(
- self.fake_object['resource']['id'])
-
- def test_destroy_volumes(self):
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
-
- self.useFixture(mockpatch.PatchObject(
- javelin, "_get_volume_by_name",
- return_value=self.fake_object.volume))
-
- javelin.destroy_volumes([self.fake_object])
-
- mocked_function = self.fake_client.volumes.detach_volume
- mocked_function.assert_called_once_with(self.fake_object.volume['id'])
- mocked_function = self.fake_client.volumes.delete_volume
- mocked_function.assert_called_once_with(self.fake_object.volume['id'])
-
- def test_destroy_subnets(self):
-
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- fake_subnet_id = self.fake_object['subnet_id']
- self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
- return_value={
- 'id': fake_subnet_id}))
-
- javelin.destroy_subnets([self.fake_object])
-
- mocked_function = self.fake_client.subnets.delete_subnet
- mocked_function.assert_called_once_with(fake_subnet_id)
-
- def test_destroy_routers(self):
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
-
- # this function is used on 2 different occasions in the code
- def _fake_get_resource_by_name(*args):
- if args[1] == "routers":
- return {"id": self.fake_object['router_id']}
- elif args[1] == "subnets":
- return {"id": self.fake_object['subnet_id']}
- javelin._get_resource_by_name = _fake_get_resource_by_name
-
- javelin.destroy_routers([self.fake_object])
-
- mocked_function = self.fake_client.routers.delete_router
- mocked_function.assert_called_once_with(
- self.fake_object['router_id'])
-
- def test_destroy_secgroup(self):
- self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=self.fake_client))
- fake_secgroup = {'id': self.fake_object['id']}
- self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
- return_value=fake_secgroup))
-
- javelin.destroy_secgroups([self.fake_object])
-
- mocked_function = self.fake_client.secgroups.delete_security_group
- mocked_function.assert_called_once_with(self.fake_object['id'])
diff --git a/tempest/tests/cmd/test_tempest_init.py b/tempest/tests/cmd/test_tempest_init.py
index 031bf4d..2844371 100644
--- a/tempest/tests/cmd/test_tempest_init.py
+++ b/tempest/tests/cmd/test_tempest_init.py
@@ -45,6 +45,7 @@
init_cmd = init.TempestInit(None, None)
local_sample_conf_file = os.path.join(etc_dir_path,
'tempest.conf.sample')
+
# Verify no sample config file exist
self.assertFalse(os.path.isfile(local_sample_conf_file))
init_cmd.generate_sample_config(local_dir.path)
@@ -53,6 +54,52 @@
self.assertTrue(os.path.isfile(local_sample_conf_file))
self.assertGreater(os.path.getsize(local_sample_conf_file), 0)
+ def test_update_local_conf(self):
+ local_dir = self.useFixture(fixtures.TempDir())
+ etc_dir_path = os.path.join(local_dir.path, 'etc/')
+ os.mkdir(etc_dir_path)
+ lock_dir = os.path.join(local_dir.path, 'tempest_lock')
+ config_path = os.path.join(etc_dir_path, 'tempest.conf')
+ log_dir = os.path.join(local_dir.path, 'logs')
+
+ init_cmd = init.TempestInit(None, None)
+
+ # Generate the config file
+ init_cmd.generate_sample_config(local_dir.path)
+
+ # Create a conf file with populated values
+ config_parser_pre = init_cmd.get_configparser(config_path)
+ with open(config_path, 'w+') as conf_file:
+ # create the same section init will check for and add values to
+ config_parser_pre.add_section('oslo_concurrency')
+ config_parser_pre.set('oslo_concurrency', 'TEST', local_dir.path)
+ # create a new section
+ config_parser_pre.add_section('TEST')
+ config_parser_pre.set('TEST', 'foo', "bar")
+ config_parser_pre.write(conf_file)
+
+ # Update the config file the same way tempest init does
+ init_cmd.update_local_conf(config_path, lock_dir, log_dir)
+
+ # parse the new config file to verify it
+ config_parser_post = init_cmd.get_configparser(config_path)
+
+ # check that our value in oslo_concurrency wasn't overwritten
+ self.assertTrue(config_parser_post.has_section('oslo_concurrency'))
+ self.assertEqual(config_parser_post.get('oslo_concurrency', 'TEST'),
+ local_dir.path)
+ # check that the lock directory was set correctly
+ self.assertEqual(config_parser_post.get('oslo_concurrency',
+ 'lock_path'), lock_dir)
+
+ # check that our new section still exists and wasn't modified
+ self.assertTrue(config_parser_post.has_section('TEST'))
+ self.assertEqual(config_parser_post.get('TEST', 'foo'), 'bar')
+
+ # check that the DEFAULT values are correct
+ # NOTE(auggy): has_section ignores DEFAULT
+ self.assertEqual(config_parser_post.get('DEFAULT', 'log_dir'), log_dir)
+
def test_create_working_dir_with_existing_local_dir_non_empty(self):
fake_local_dir = self.useFixture(fixtures.TempDir())
fake_local_conf_dir = self.useFixture(fixtures.TempDir())
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 70cbf87..00b4542 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -188,34 +188,54 @@
False, True)
@mock.patch('tempest.lib.common.http.ClosingHttp.request')
- def test_verify_cinder_api_versions_no_v2(self, mock_request):
+ def test_verify_cinder_api_versions_no_v3(self, mock_request):
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, '_get_unversioned_endpoint',
return_value='http://fake_endpoint:5000'))
- fake_resp = {'versions': [{'id': 'v1.0'}]}
+ fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v2.0'}]}
fake_resp = json.dumps(fake_resp)
mock_request.return_value = (None, fake_resp)
fake_os = mock.MagicMock()
with mock.patch.object(verify_tempest_config,
'print_and_or_update') as print_mock:
verify_tempest_config.verify_cinder_api_versions(fake_os, True)
- print_mock.assert_called_once_with('api_v2', 'volume-feature-enabled',
- False, True)
+ print_mock.assert_not_called()
+
+ @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+ def test_verify_cinder_api_versions_no_v2(self, mock_request):
+ self.useFixture(mockpatch.PatchObject(
+ verify_tempest_config, '_get_unversioned_endpoint',
+ return_value='http://fake_endpoint:5000'))
+ fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v3.0'}]}
+ fake_resp = json.dumps(fake_resp)
+ mock_request.return_value = (None, fake_resp)
+ fake_os = mock.MagicMock()
+ with mock.patch.object(verify_tempest_config,
+ 'print_and_or_update') as print_mock:
+ verify_tempest_config.verify_cinder_api_versions(fake_os, True)
+ print_mock.assert_any_call('api_v2', 'volume-feature-enabled',
+ False, True)
+ print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
+ True, True)
+ self.assertEqual(2, print_mock.call_count)
@mock.patch('tempest.lib.common.http.ClosingHttp.request')
def test_verify_cinder_api_versions_no_v1(self, mock_request):
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, '_get_unversioned_endpoint',
return_value='http://fake_endpoint:5000'))
- fake_resp = {'versions': [{'id': 'v2.0'}]}
+ fake_resp = {'versions': [{'id': 'v2.0'}, {'id': 'v3.0'}]}
fake_resp = json.dumps(fake_resp)
mock_request.return_value = (None, fake_resp)
fake_os = mock.MagicMock()
with mock.patch.object(verify_tempest_config,
'print_and_or_update') as print_mock:
verify_tempest_config.verify_cinder_api_versions(fake_os, True)
- print_mock.assert_called_once_with('api_v1', 'volume-feature-enabled',
- False, True)
+ print_mock.assert_any_call('api_v1', 'volume-feature-enabled',
+ False, True)
+ print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
+ True, True)
+ self.assertEqual(2, print_mock.call_count)
def test_verify_glance_version_no_v2_with_v1_1(self):
def fake_get_versions():
diff --git a/tempest/tests/lib/common/utils/test_data_utils.py b/tempest/tests/lib/common/utils/test_data_utils.py
index 399c4af..94a4847 100644
--- a/tempest/tests/lib/common/utils/test_data_utils.py
+++ b/tempest/tests/lib/common/utils/test_data_utils.py
@@ -59,7 +59,7 @@
def test_rand_password(self):
actual = data_utils.rand_password()
self.assertIsInstance(actual, str)
- self.assertRegex(actual, "[A-Za-z0-9~!@#$%^&*_=+]{15,}")
+ self.assertRegex(actual, "[A-Za-z0-9~!@#%^&*_=+]{15,}")
actual2 = data_utils.rand_password()
self.assertNotEqual(actual, actual2)
@@ -67,7 +67,7 @@
actual = data_utils.rand_password(8)
self.assertIsInstance(actual, str)
self.assertEqual(len(actual), 8)
- self.assertRegex(actual, "[A-Za-z0-9~!@#$%^&*_=+]{8}")
+ self.assertRegex(actual, "[A-Za-z0-9~!@#%^&*_=+]{8}")
actual2 = data_utils.rand_password(8)
self.assertNotEqual(actual, actual2)
@@ -75,7 +75,7 @@
actual = data_utils.rand_password(2)
self.assertIsInstance(actual, str)
self.assertEqual(len(actual), 3)
- self.assertRegex(actual, "[A-Za-z0-9~!@#$%^&*_=+]{3}")
+ self.assertRegex(actual, "[A-Za-z0-9~!@#%^&*_=+]{3}")
actual2 = data_utils.rand_password(2)
self.assertNotEqual(actual, actual2)
diff --git a/tempest/tests/test_service_clients.py b/tempest/tests/lib/services/test_clients.py
similarity index 81%
rename from tempest/tests/test_service_clients.py
rename to tempest/tests/lib/services/test_clients.py
index 3d8b360..5db932c 100644
--- a/tempest/tests/test_service_clients.py
+++ b/tempest/tests/lib/services/test_clients.py
@@ -19,7 +19,7 @@
from tempest.lib import auth
from tempest.lib import exceptions
-from tempest import service_clients
+from tempest.lib.services import clients
from tempest.tests import base
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib import fake_credentials
@@ -54,14 +54,14 @@
def test___init___one_class(self):
fake_partial = 'fake_partial'
partial_mock = self.useFixture(fixtures.MockPatch(
- 'tempest.service_clients.ClientsFactory._get_partial_class',
+ 'tempest.lib.services.clients.ClientsFactory._get_partial_class',
return_value=fake_partial)).mock
class_names = ['FakeServiceClient1']
mock_importlib = self._setup_fake_module(class_names=class_names)
auth_provider = fake_auth_provider.FakeAuthProvider()
params = {'k1': 'v1', 'k2': 'v2'}
- factory = service_clients.ClientsFactory('fake_path', class_names,
- auth_provider, **params)
+ factory = clients.ClientsFactory('fake_path', class_names,
+ auth_provider, **params)
# Assert module has been imported
mock_importlib.assert_called_once_with('fake_path')
# All attributes have been created
@@ -77,14 +77,14 @@
def test___init___two_classes(self):
fake_partial = 'fake_partial'
partial_mock = self.useFixture(fixtures.MockPatch(
- 'tempest.service_clients.ClientsFactory._get_partial_class',
+ 'tempest.lib.services.clients.ClientsFactory._get_partial_class',
return_value=fake_partial)).mock
class_names = ['FakeServiceClient1', 'FakeServiceClient2']
mock_importlib = self._setup_fake_module(class_names=class_names)
auth_provider = fake_auth_provider.FakeAuthProvider()
params = {'k1': 'v1', 'k2': 'v2'}
- factory = service_clients.ClientsFactory('fake_path', class_names,
- auth_provider, **params)
+ factory = clients.ClientsFactory('fake_path', class_names,
+ auth_provider, **params)
# Assert module has been imported
mock_importlib.assert_called_once_with('fake_path')
# All attributes have been created
@@ -100,8 +100,8 @@
auth_provider = fake_auth_provider.FakeAuthProvider()
class_names = ['FakeServiceClient1', 'FakeServiceClient2']
with testtools.ExpectedException(ImportError, '.*fake_module.*'):
- service_clients.ClientsFactory('fake_module', class_names,
- auth_provider)
+ clients.ClientsFactory('fake_module', class_names,
+ auth_provider)
def test___init___not_a_class(self):
class_names = ['FakeServiceClient1', 'FakeServiceClient2']
@@ -111,8 +111,8 @@
auth_provider = fake_auth_provider.FakeAuthProvider()
expected_msg = '.*not_really_a_class.*str.*'
with testtools.ExpectedException(TypeError, expected_msg):
- service_clients.ClientsFactory('fake_module', extended_class_names,
- auth_provider)
+ clients.ClientsFactory('fake_module', extended_class_names,
+ auth_provider)
def test___init___class_not_found(self):
class_names = ['FakeServiceClient1', 'FakeServiceClient2']
@@ -121,15 +121,15 @@
auth_provider = fake_auth_provider.FakeAuthProvider()
expected_msg = '.*not_really_a_class.*fake_service_client.*'
with testtools.ExpectedException(AttributeError, expected_msg):
- service_clients.ClientsFactory('fake_module', extended_class_names,
- auth_provider)
+ clients.ClientsFactory('fake_module', extended_class_names,
+ auth_provider)
def test__get_partial_class_no_later_kwargs(self):
expected_fake_client = 'not_really_a_client'
self._setup_fake_module(class_names=[])
auth_provider = fake_auth_provider.FakeAuthProvider()
params = {'k1': 'v1', 'k2': 'v2'}
- factory = service_clients.ClientsFactory(
+ factory = clients.ClientsFactory(
'fake_path', [], auth_provider, **params)
klass_mock = mock.Mock(return_value=expected_fake_client)
partial = factory._get_partial_class(klass_mock, auth_provider, params)
@@ -147,7 +147,7 @@
auth_provider = fake_auth_provider.FakeAuthProvider()
params = {'k1': 'v1', 'k2': 'v2'}
later_params = {'k2': 'v4', 'k3': 'v3'}
- factory = service_clients.ClientsFactory(
+ factory = clients.ClientsFactory(
'fake_path', [], auth_provider, **params)
klass_mock = mock.Mock(return_value=expected_fake_client)
partial = factory._get_partial_class(klass_mock, auth_provider, params)
@@ -160,21 +160,45 @@
klass_mock.assert_called_once_with(auth_provider=auth_provider,
**params)
+ def test__get_partial_class_with_alias(self):
+ expected_fake_client = 'not_really_a_client'
+ client_alias = 'fake_client'
+ self._setup_fake_module(class_names=[])
+ auth_provider = fake_auth_provider.FakeAuthProvider()
+ params = {'k1': 'v1', 'k2': 'v2'}
+ later_params = {'k2': 'v4', 'k3': 'v3'}
+ factory = clients.ClientsFactory(
+ 'fake_path', [], auth_provider, **params)
+ klass_mock = mock.Mock(return_value=expected_fake_client)
+ partial = factory._get_partial_class(klass_mock, auth_provider, params)
+ # Class has not be initialised yet
+ klass_mock.assert_not_called()
+ # Use partial and assert on parameters
+ client = partial(alias=client_alias, **later_params)
+ params.update(later_params)
+ self.assertEqual(expected_fake_client, client)
+ klass_mock.assert_called_once_with(auth_provider=auth_provider,
+ **params)
+ self.assertThat(factory, has_attribute(client_alias))
+ self.assertEqual(expected_fake_client, getattr(factory, client_alias))
+
class TestServiceClients(base.TestCase):
def setUp(self):
super(TestServiceClients, self).setUp()
self.useFixture(fixtures.MockPatch(
- 'tempest.service_clients.tempest_modules',
- return_value=set(['fake_service1', 'fake_service2'])))
+ 'tempest.lib.services.clients.tempest_modules', return_value={}))
+ self.useFixture(fixtures.MockPatch(
+ 'tempest.lib.services.clients._tempest_internal_modules',
+ return_value=set(['fake_service1'])))
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()
uri = 'fake_uri'
- _manager = service_clients.ServiceClients(creds, identity_uri=uri)
+ _manager = clients.ServiceClients(creds, identity_uri=uri)
self.assertIsInstance(_manager.auth_provider,
auth.KeystoneV2AuthProvider)
@@ -183,7 +207,7 @@
# is required to run the test successfully
creds = fake_credentials.FakeKeystoneV3Credentials()
uri = 'fake_uri'
- _manager = service_clients.ServiceClients(creds, identity_uri=uri)
+ _manager = clients.ServiceClients(creds, identity_uri=uri)
self.assertIsInstance(_manager.auth_provider,
auth.KeystoneV3AuthProvider)
@@ -191,14 +215,14 @@
creds = fake_credentials.FakeCredentials()
uri = 'fake_uri'
with testtools.ExpectedException(exceptions.InvalidCredentials):
- service_clients.ServiceClients(creds, identity_uri=uri)
+ clients.ServiceClients(creds, identity_uri=uri)
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)
+ clients.ServiceClients(creds, identity_uri=uri)
def test___init___creds_uri_none(self):
creds = fake_credentials.FakeKeystoneV2Credentials()
@@ -206,7 +230,7 @@
"non-empty")
with testtools.ExpectedException(exceptions.InvalidCredentials,
value_re=msg):
- service_clients.ServiceClients(creds, None)
+ clients.ServiceClients(creds, None)
def test___init___creds_uri_params(self):
creds = fake_credentials.FakeKeystoneV2Credentials()
@@ -214,8 +238,8 @@
'fake_param2': 'fake_value2'}
params = {'fake_service1': expeted_params}
uri = 'fake_uri'
- _manager = service_clients.ServiceClients(creds, identity_uri=uri,
- client_parameters=params)
+ _manager = 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())
@@ -231,14 +255,14 @@
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)
+ 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)
+ return clients.ServiceClients(creds, identity_uri='fake_uri',
+ region=init_region)
def test__setup_parameters_none_no_region(self):
kwargs = {}
@@ -270,7 +294,7 @@
_manager = self._get_manager(init_region='fake_region_default')
# Mock after the _manager is setup to preserve the call count
factory_mock = self.useFixture(fixtures.MockPatch(
- 'tempest.service_clients.ClientsFactory')).mock
+ 'tempest.lib.services.clients.ClientsFactory')).mock
_manager.register_service_client_module(
name='fake_module',
service_version='fake_service',
@@ -299,7 +323,7 @@
_manager = self._get_manager(init_region='fake_region_default')
# Mock after the _manager is setup to preserve the call count
factory_mock = self.useFixture(fixtures.MockPatch(
- 'tempest.service_clients.ClientsFactory')).mock
+ 'tempest.lib.services.clients.ClientsFactory')).mock
_manager.register_service_client_module(
name='fake_module',
service_version='fake_service',
@@ -322,7 +346,7 @@
def test_register_service_client_module_duplicate_name(self):
self.useFixture(fixtures.MockPatch(
- 'tempest.service_clients.ClientsFactory'))
+ 'tempest.lib.services.clients.ClientsFactory')).mock
_manager = self._get_manager()
name_owner = 'this_is_a_string'
setattr(_manager, 'fake_module', name_owner)
@@ -335,7 +359,7 @@
def test_register_service_client_module_duplicate_service(self):
self.useFixture(fixtures.MockPatch(
- 'tempest.service_clients.ClientsFactory'))
+ 'tempest.lib.services.clients.ClientsFactory')).mock
_manager = self._get_manager()
duplicate_service = 'fake_service1'
expected_error = '.*' + duplicate_service