Merge "Remove Ceilometer tempest tests"
diff --git a/HACKING.rst b/HACKING.rst
index 4095c4b..d3ac5c6 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -16,7 +16,7 @@
- [T107] Check that a service tag isn't in the module path
- [T108] Check no hyphen at the end of rand_name() argument
- [T109] Cannot use testtools.skip decorator; instead use
- decorators.skip_because from tempest-lib
+ decorators.skip_because from tempest.lib
- [T110] Check that service client names of GET should be consistent
- [T111] Check that service client names of DELETE should be consistent
- [T112] Check that tempest.lib should not import local tempest code
@@ -132,16 +132,16 @@
Set-up is split in a series of steps (setup stages), which can be overwritten
by test classes. Set-up stages are:
-- `skip_checks`
-- `setup_credentials`
-- `setup_clients`
-- `resource_setup`
+ - `skip_checks`
+ - `setup_credentials`
+ - `setup_clients`
+ - `resource_setup`
Tear-down is also split in a series of steps (teardown stages), which are
stacked for execution only if the corresponding setup stage had been
reached during the setup phase. Tear-down stages are:
-- `clear_credentials` (defined in the base test class)
-- `resource_cleanup`
+ - `clear_credentials` (defined in the base test class)
+ - `resource_cleanup`
Skipping Tests
--------------
@@ -157,33 +157,7 @@
Negative Tests
--------------
-Newly added negative tests should use the negative test framework. First step
-is to create an interface description in a python file under
-`tempest/api_schema/request/`. These descriptions consists of two important
-sections for the test (one of those is mandatory):
-
- - A resource (part of the URL of the request): Resources needed for a test
- must be created in `setUpClass` and registered with `set_resource` e.g.:
- `cls.set_resource("server", server['id'])`
-
- - A json schema: defines properties for a request.
-
-After that a test class must be added to automatically generate test scenarios
-out of the given interface description::
-
- load_tests = test.NegativeAutoTest.load_tests
-
- @test.SimpleNegativeAutoTest
- class SampleTestNegativeTestJSON(<your base class>, test.NegativeAutoTest):
- _service = 'compute'
- _schema = <your schema file>
-
-The class decorator `SimpleNegativeAutoTest` will automatically generate test
-cases out of the given schema in the attribute `_schema`.
-
-All negative tests should be added into a separate negative test file.
-If such a file doesn't exist for the particular resource being tested a new
-test file should be added.
+TODO: Write the guideline related to negative tests.
Test skips because of Known Bugs
--------------------------------
@@ -333,9 +307,9 @@
# The created server should be in the detailed list of all servers
...
-Tempest-lib includes a ``check-uuid`` tool that will test for the existence
+Tempest.lib includes a ``check-uuid`` tool that will test for the existence
and uniqueness of idempotent_id metadata for every test. If you have
-tempest-lib installed you run the tool against Tempest by calling from the
+tempest installed you run the tool against Tempest by calling from the
tempest repo::
check-uuid
diff --git a/README.rst b/README.rst
index 7da83cd..650a1ed 100644
--- a/README.rst
+++ b/README.rst
@@ -58,7 +58,7 @@
#. You first need to install Tempest. This is done with pip after you check out
the Tempest repo::
- $ git clone https://github.com/openstack/tempest/
+ $ git clone http://git.openstack.org/openstack/tempest
$ pip install tempest/
This can be done within a venv, but the assumption for this guide is that
@@ -71,14 +71,14 @@
it's recommended that you copy or rename tempest.conf.sample to tempest.conf
and make those changes to that file in /etc/tempest
-#. Setup a local working Tempest dir. This is done using the tempest init
+#. Setup a local working Tempest dir. This is done by using the tempest init
command::
- tempest init cloud-01
+ $ tempest init cloud-01
works the same as::
- mkdir cloud-01 && cd cloud-01 && tempest init
+ $ mkdir cloud-01 && cd cloud-01 && tempest init
This will create a new directory for running a single Tempest configuration.
If you'd like to run Tempest against multiple OpenStack deployments the idea
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 245386b..bcb1e3e 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -9,23 +9,38 @@
config file which explains the purpose of each individual option. You can see
the sample config file here: :ref:`tempest-sampleconf`
-Auth/Credentials
+Test Credentials
----------------
-Tempest currently has two different ways in configuration to provide credentials
-to use when running Tempest. One is a traditional set of configuration options
-in the tempest.conf file. These options are clearly labelled in the ``identity``
-section and let you specify a set of credentials for a regular user, a global
-admin user, and an alternate user, consisting of a username, password, and
-project name.
+Tempest allows for configuring a set of admin credentials in the ``auth``
+section, via the following parameters:
-The other method to provide credentials is using the accounts.yaml file. This
-file is used to specify an arbitrary number of users available to run tests
-with. You can specify the location of the file in the ``auth`` section in the
+ #. ``admin_username``
+ #. ``admin_password``
+ #. ``admin_project_name``
+ #. ``admin_domain_name``
+
+Admin credentials are not mandatory to run Tempest, but when provided they
+can be used to:
+
+- Run tests for admin APIs
+- Generate test credentials on the fly (see `Dynamic Credentials`_)
+
+Tempest allows for configuring pre-provisioned test credentials as well.
+This can be done in two different ways.
+
+One is to provide credentials is using the accounts.yaml file (see
+`Pre-Provisioned Credentials`_). This file is used to specify an arbitrary
+number of users available to run tests with.
+You can specify the location of the file in the ``auth`` section in the
tempest.conf file. To see the specific format used in the file please refer to
-the accounts.yaml.sample file included in Tempest. Eventually the config
-options for providing credentials to Tempest will be deprecated and removed in
-favor of the accounts.yaml file.
+the accounts.yaml.sample file included in Tempest.
+
+A second way - now deprecated - is a set of configuration options in the
+tempest.conf file (see `Legacy Credentials`_). These options are clearly
+labelled in the ``identity`` section and let you specify a set of credentials
+for a regular user and an alternate user, consisting of a username, password,
+project and domain name.
Keystone Connection Info
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -81,8 +96,9 @@
configured to use dynamic credentials.
-Pre-Provisioned Credentials (aka accounts.yaml or accounts file)
-""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+Pre-Provisioned Credentials
+"""""""""""""""""""""""""""
+
For a long time using dynamic credentials was the only method available if you
wanted to enable parallel execution of Tempest tests. However, this was
insufficient for certain use cases because of the admin credentials requirement
@@ -116,9 +132,10 @@
to the tests using the credentials, and failure to do this will likely cause
unexpected failures in some tests.
+Pre-Provisioned Credentials are also know as accounts.yaml or accounts file.
-Legacy Credentials (aka credentials config options)
-"""""""""""""""""""""""""""""""""""""""""""""""""""
+Legacy Credentials
+""""""""""""""""""
**Starting in the Liberty release this mechanism was deprecated; it will be
removed in a future release.**
@@ -132,9 +149,6 @@
#. ``username``
#. ``password``
#. ``project_name``
- #. ``admin_username``
- #. ``admin_password``
- #. ``admin_project_name``
#. ``alt_username``
#. ``alt_password``
#. ``alt_project_name``
diff --git a/doc/source/library.rst b/doc/source/library.rst
index 64bd2c2..a89512c 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -7,15 +7,14 @@
test suites an interface for reusing pieces of tempest code. Any public
interface that lives in tempest/lib in the tempest repo is treated as a stable
public interface and it should be safe to external consume that. Every effort
-goes into maintaining backwards compatibility with any change. Just as with
-tempest-lib the library is self contained and doesn't have any dependency on
-other tempest internals outside of lib. (including no usage of tempest
-configuration)
+goes into maintaining backwards compatibility with any change.
+The library is self contained and doesn't have any dependency on
+other tempest internals outside of lib (including no usage of tempest
+configuration).
Stability
---------
-Just as tempest-lib before it any code that lives in tempest/lib will be treated
-as a stable interface, nothing has changed in regards to interface stability.
+Any code that lives in tempest/lib will be treated as a stable interface.
This means that any public interface under the tempest/lib directory is
expected to be a stable interface suitable for public consumption. However, for
any interfaces outside of tempest/lib in the tempest tree (unless otherwise
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 2622c22..ad26741 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -11,13 +11,13 @@
=================
Creating a plugin is fairly straightforward and doesn't require much additional
-effort on top of creating a test suite using tempest-lib. One thing to note with
+effort on top of creating a test suite using tempest.lib. One thing to note with
doing this is that the interfaces exposed by tempest are not considered stable
(with the exception of configuration variables which ever effort goes into
ensuring backwards compatibility). You should not need to import anything from
tempest itself except where explicitly noted. If there is an interface from
tempest that you need to rely on in your plugin it likely needs to be migrated
-to tempest-lib. In that situation, file a bug, push a migration patch, etc. to
+to tempest.lib. In that situation, file a bug, push a migration patch, etc. to
expedite providing the interface in a reliable manner.
Plugin Cookiecutter
@@ -102,11 +102,6 @@
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.
-Also, note eventually this abstract class will likely live in tempest-lib, when
-that migration occurs a deprecation shim will be added to tempest so as to not
-break any existing plugins. But, when that occurs migrating to using tempest-lib
-as the source for the abstract class will be prudent.
-
Abstract Plugin Class
---------------------
diff --git a/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml b/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
new file mode 100644
index 0000000..9baf035
--- /dev/null
+++ b/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+ This release is marking the end of Kilo release support in Tempest
+other:
+ - OpenStack Releases Supported after this release are **Liberty**
+ and **Mitaka**
+
+ The release under current development as of this tag is Newton,
+ meaning that every Tempest commit is also tested against master during
+ the Newton cycle. However, this does not necessarily mean that using
+ Tempest as of this tag will work against a Newton (or future releases)
+ cloud.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index b617b22..2c22408 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -5,6 +5,7 @@
.. toctree::
:maxdepth: 1
+ v12.0.0
v11.0.0
v10.0.0
unreleased
diff --git a/releasenotes/source/v12.0.0.rst b/releasenotes/source/v12.0.0.rst
new file mode 100644
index 0000000..0bc8343
--- /dev/null
+++ b/releasenotes/source/v12.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v12.0.0 Release Notes
+=====================
+
+.. release-notes:: 12.0.0 Release Notes
+ :version: 12.0.0
diff --git a/requirements.txt b/requirements.txt
index 79ae42e..dd73257 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,6 @@
# process, which may cause wedges in the gate later.
pbr>=1.6 # Apache-2.0
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
-anyjson>=0.3.3 # BSD
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
testtools>=1.4.0 # MIT
paramiko>=1.16.0 # LGPL
@@ -17,7 +16,6 @@
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.5.0 # Apache-2.0
six>=1.9.0 # MIT
-iso8601>=0.1.9 # MIT
fixtures<2.0,>=1.3.1 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
PyYAML>=3.1.0 # MIT
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index f6ea3a4..a9e9644 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -49,7 +49,7 @@
@test.idempotent_id('c6ddbadb-c94e-4500-b12f-8ffc43843ff8')
def test_list_hosts_with_nonexistent_zone(self):
# If send the request with a nonexistent zone, the request will be
- # successful and no hosts will be retured
+ # successful and no hosts will be returned
hosts = self.client.list_hosts(zone='xxx')['hosts']
self.assertEqual(0, len(hosts))
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index f81d665..6113c04 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -15,8 +15,10 @@
import testtools
from tempest.api.compute import base
+from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib import exceptions
from tempest import test
CONF = config.CONF
@@ -28,6 +30,7 @@
def setup_clients(cls):
super(MigrationsAdminTest, cls).setup_clients()
cls.client = cls.os_adm.migrations_client
+ cls.flavors_admin_client = cls.os_adm.flavors_client
@test.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
def test_list_migrations(self):
@@ -53,3 +56,50 @@
instance_uuids = [x['instance_uuid'] for x in body]
self.assertIn(server_id, instance_uuids)
+
+ def _flavor_clean_up(self, flavor_id):
+ try:
+ self.flavors_admin_client.delete_flavor(flavor_id)
+ self.flavors_admin_client.wait_for_resource_deletion(flavor_id)
+ except exceptions.NotFound:
+ pass
+
+ @test.idempotent_id('33f1fec3-ba18-4470-8e4e-1d888e7c3593')
+ @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+ 'Resize not available.')
+ def test_resize_server_revert_deleted_flavor(self):
+ # Tests that we can revert the resize on an instance whose original
+ # flavor has been deleted.
+
+ # First we have to create a flavor that we can delete so make a copy
+ # of the normal flavor from which we'd create a server.
+ flavor = self.flavors_admin_client.show_flavor(
+ self.flavor_ref)['flavor']
+ flavor = self.flavors_admin_client.create_flavor(
+ name=data_utils.rand_name('test_resize_flavor_'),
+ ram=flavor['ram'],
+ disk=flavor['disk'],
+ vcpus=flavor['vcpus']
+ )['flavor']
+ self.addCleanup(self._flavor_clean_up, flavor['id'])
+
+ # Now boot a server with the copied flavor.
+ server = self.create_test_server(
+ wait_until='ACTIVE', flavor=flavor['id'])
+
+ # Delete the flavor we used to boot the instance.
+ self._flavor_clean_up(flavor['id'])
+
+ # Now resize the server and wait for it to go into verify state.
+ self.servers_client.resize_server(server['id'], self.flavor_ref_alt)
+ waiters.wait_for_server_status(self.servers_client, server['id'],
+ 'VERIFY_RESIZE')
+
+ # Now revert the resize, it should be OK even though the original
+ # flavor used to boot the server was deleted.
+ self.servers_client.revert_resize_server(server['id'])
+ waiters.wait_for_server_status(self.servers_client, server['id'],
+ 'ACTIVE')
+
+ server = self.servers_client.show_server(server['id'])['server']
+ self.assertEqual(flavor['id'], server['flavor']['id'])
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 2907e26..b1f0755 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -185,7 +185,7 @@
# increment all of the values for updating the default quota class
for quota, default in six.iteritems(body):
# NOTE(sdague): we need to increment a lot, otherwise
- # there is a real chance that we go from -1 (unlimitted)
+ # there is a real chance that we go from -1 (unlimited)
# to a very small number which causes issues.
body[quota] = default + 100
LOG.debug("update limits for the default quota class set")
diff --git a/tempest/api/compute/admin/test_servers_on_multinodes.py b/tempest/api/compute/admin/test_servers_on_multinodes.py
index 814a876..1bbde98 100644
--- a/tempest/api/compute/admin/test_servers_on_multinodes.py
+++ b/tempest/api/compute/admin/test_servers_on_multinodes.py
@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.compute import base
from tempest import config
from tempest import test
@@ -34,6 +36,9 @@
server_id)['server']['OS-EXT-SRV-ATTR:host']
@test.idempotent_id('26a9d5df-6890-45f2-abc4-a659290cb130')
+ @testtools.skipUnless(
+ test.is_scheduler_filter_enabled("SameHostFilter"),
+ 'SameHostFilter is not available.')
def test_create_servers_on_same_host(self):
server01 = self.create_test_server(wait_until='ACTIVE')['id']
@@ -45,6 +50,9 @@
self.assertEqual(host01, host02)
@test.idempotent_id('cc7ca884-6e3e-42a3-a92f-c522fcf25e8e')
+ @testtools.skipUnless(
+ test.is_scheduler_filter_enabled("DifferentHostFilter"),
+ 'DifferentHostFilter is not available.')
def test_create_servers_on_different_hosts(self):
server01 = self.create_test_server(wait_until='ACTIVE')['id']
@@ -56,6 +64,9 @@
self.assertNotEqual(host01, host02)
@test.idempotent_id('7869cc84-d661-4e14-9f00-c18cdc89cf57')
+ @testtools.skipUnless(
+ test.is_scheduler_filter_enabled("DifferentHostFilter"),
+ 'DifferentHostFilter is not available.')
def test_create_servers_on_different_hosts_with_list_of_servers(self):
server01 = self.create_test_server(wait_until='ACTIVE')['id']
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 87f3c86..c05045e 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -119,7 +119,9 @@
self.get_server_ip(self.server),
self.ssh_user,
self.password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.client)
self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
@test.idempotent_id('ac1ad47f-984b-4441-9274-c9079b7a0666')
@@ -131,7 +133,9 @@
self.get_server_ip(self.server),
self.ssh_user,
self.password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.client)
self.assertTrue(linux_client.hostname_equals_servername(self.name))
@test.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
@@ -322,7 +326,9 @@
self.get_server_ip(server_no_eph_disk),
self.ssh_user,
admin_pass,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server_no_eph_disk,
+ servers_client=self.client)
partition_num = len(linux_client.get_partitions().split('\n'))
# Explicit server deletion necessary for Juno compatibility
@@ -340,7 +346,9 @@
self.get_server_ip(server_with_eph_disk),
self.ssh_user,
admin_pass,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server_with_eph_disk,
+ servers_client=self.client)
partition_num_emph = len(linux_client.get_partitions().split('\n'))
self.assertEqual(partition_num + 1, partition_num_emph)
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 6796bb5..079465d 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -16,6 +16,7 @@
import testtools
from tempest.api.compute import base
+from tempest.common import compute
from tempest.common import waiters
from tempest import config
from tempest import test
@@ -84,16 +85,8 @@
def test_delete_server_while_in_shelved_state(self):
# Delete a server while it's VM state is Shelved
server = self.create_test_server(wait_until='ACTIVE')
- self.client.shelve_server(server['id'])
+ compute.shelve_server(self.client, server['id'])
- offload_time = CONF.compute.shelved_offload_time
- if offload_time >= 0:
- waiters.wait_for_server_status(self.client, server['id'],
- 'SHELVED_OFFLOADED',
- extra_timeout=offload_time)
- else:
- waiters.wait_for_server_status(self.client, server['id'],
- 'SHELVED')
self.client.delete_server(server['id'])
waiters.wait_for_server_termination(self.client, server['id'])
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 5b4417a..f01657b 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -19,6 +19,7 @@
import testtools
from tempest.api.compute import base
+from tempest.common import compute
from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
from tempest.common import waiters
@@ -90,7 +91,9 @@
linux_client = remote_client.RemoteClient(
self.get_server_ip(server),
self.ssh_user,
- new_password)
+ new_password,
+ server=server,
+ servers_client=self.client)
linux_client.validate_authentication()
def _test_reboot_server(self, reboot_type):
@@ -101,7 +104,9 @@
self.get_server_ip(server),
self.ssh_user,
self.password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.client)
boot_time = linux_client.get_boot_time()
self.client.reboot_server(self.server_id, type=reboot_type)
@@ -113,7 +118,9 @@
self.get_server_ip(server),
self.ssh_user,
self.password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.client)
new_boot_time = linux_client.get_boot_time()
self.assertTrue(new_boot_time > boot_time,
'%s > %s' % (new_boot_time, boot_time))
@@ -182,7 +189,9 @@
self.get_server_ip(rebuilt_server),
self.ssh_user,
password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=rebuilt_server,
+ servers_client=self.client)
linux_client.validate_authentication()
@test.idempotent_id('30449a88-5aff-4f9b-9866-6ee9b17f906d')
@@ -440,20 +449,8 @@
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
def test_shelve_unshelve_server(self):
- self.client.shelve_server(self.server_id)
-
- offload_time = CONF.compute.shelved_offload_time
- if offload_time >= 0:
- waiters.wait_for_server_status(self.client, self.server_id,
- 'SHELVED_OFFLOADED',
- extra_timeout=offload_time)
- else:
- waiters.wait_for_server_status(self.client, self.server_id,
- 'SHELVED')
-
- self.client.shelve_offload_server(self.server_id)
- waiters.wait_for_server_status(self.client, self.server_id,
- 'SHELVED_OFFLOADED')
+ compute.shelve_server(self.client, self.server_id,
+ force_shelve_offload=True)
server = self.client.show_server(self.server_id)['server']
image_name = server['name'] + '-shelved'
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index 74d34a2..baa4f9a 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -66,7 +66,9 @@
linux_client = remote_client.RemoteClient(
self.get_server_ip(server),
self.ssh_user, password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.client)
self.assertEqual(file_contents,
linux_client.exec_command(
'sudo cat %s' % file_path))
@@ -130,7 +132,9 @@
linux_client = remote_client.RemoteClient(
self.get_server_ip(server),
self.ssh_user, password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.client)
for i in person:
self.assertEqual(base64.b64decode(i['contents']),
linux_client.exec_command(
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 8f0e430..10ea31d 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -18,6 +18,7 @@
import testtools
from tempest.api.compute import base
+from tempest.common import compute
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
@@ -453,18 +454,7 @@
@test.attr(type=['negative'])
def test_shelve_shelved_server(self):
# shelve a shelved server.
- self.client.shelve_server(self.server_id)
-
- offload_time = CONF.compute.shelved_offload_time
- if offload_time >= 0:
- waiters.wait_for_server_status(self.client,
- self.server_id,
- 'SHELVED_OFFLOADED',
- extra_timeout=offload_time)
- else:
- waiters.wait_for_server_status(self.client,
- self.server_id,
- 'SHELVED')
+ compute.shelve_server(self.client, self.server_id)
server = self.client.show_server(self.server_id)['server']
image_name = server['name'] + '-shelved'
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index e9c8e30..fa3fdfe 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -16,6 +16,7 @@
import testtools
from tempest.api.compute import base
+from tempest.common import compute
from tempest.common.utils.linux import remote_client
from tempest.common import waiters
from tempest import config
@@ -25,6 +26,7 @@
class AttachVolumeTestJSON(base.BaseV2ComputeTest):
+ max_microversion = '2.19'
def __init__(self, *args, **kwargs):
super(AttachVolumeTestJSON, self).__init__(*args, **kwargs)
@@ -62,7 +64,7 @@
self.volumes_client.wait_for_resource_deletion(self.volume['id'])
self.volume = None
- def _create_and_attach(self):
+ def _create_and_attach(self, shelve_server=False):
# Start a server and wait for it to become ready
self.admin_pass = self.image_ssh_password
self.server = self.create_test_server(
@@ -81,6 +83,22 @@
waiters.wait_for_volume_status(self.volumes_client,
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
+ compute.shelve_server(self.servers_client, self.server['id'])
+
# Attach the volume to the server
self.attachment = self.servers_client.attach_volume(
self.server['id'],
@@ -111,7 +129,9 @@
self.get_server_ip(self.server),
self.image_ssh_user,
self.admin_pass,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.servers_client)
partitions = linux_client.get_partitions()
self.assertIn(self.device, partitions)
@@ -130,7 +150,9 @@
self.get_server_ip(self.server),
self.image_ssh_user,
self.admin_pass,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.servers_client)
partitions = linux_client.get_partitions()
self.assertNotIn(self.device, partitions)
@@ -152,3 +174,68 @@
self.assertEqual(self.server['id'], body['serverId'])
self.assertEqual(self.volume['id'], body['volumeId'])
self.assertEqual(self.attachment['id'], body['id'])
+
+
+class AttachVolumeShelveTestJSON(AttachVolumeTestJSON):
+ """Testing volume with shelved instance.
+
+ This test checks the attaching and detaching volumes from
+ a shelved or shelved ofload instance.
+ """
+
+ min_microversion = '2.20'
+ max_microversion = 'latest'
+
+ def _unshelve_server_and_check_volumes(self, number_of_partition):
+ # Unshelve the instance and check that there are expected volumes
+ self.servers_client.unshelve_server(self.server['id'])
+ 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)
+
+ command = 'grep vd /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)
+
+ # Unshelve the instance and check that there are two volumes
+ self._unshelve_server_and_check_volumes('2')
+
+ # Get Volume attachment of the server
+ volume_attachment = self.servers_client.show_volume_attachment(
+ self.server['id'],
+ self.attachment['id'])['volumeAttachment']
+ self.assertEqual(self.server['id'], volume_attachment['serverId'])
+ self.assertEqual(self.attachment['id'], volume_attachment['id'])
+ # Check the mountpoint is not None after unshelve server even in
+ # case of shelved_offloaded.
+ self.assertIsNotNone(volume_attachment['device'])
+
+ @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)
+
+ # Detach the volume
+ self._detach(self.server['id'], self.volume['id'])
+ self.attachment = None
+
+ # Unshelve the instance and check that there is only one volume
+ self._unshelve_server_and_check_volumes('1')
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index 990e429..f709c91 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -27,7 +27,7 @@
# ensure that the backing file for the volume group that Nova uses
# has space for at least 3 1G volumes!
# If you are running a Devstack environment, ensure that the
- # VOLUME_BACKING_FILE_SIZE is atleast 4G in your localrc
+ # VOLUME_BACKING_FILE_SIZE is at least 4G in your localrc
@classmethod
def skip_checks(cls):
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
index bdb4383..bb7a0a4 100644
--- a/tempest/api/database/flavors/test_flavors.py
+++ b/tempest/api/database/flavors/test_flavors.py
@@ -27,6 +27,7 @@
@test.attr(type='smoke')
@test.idempotent_id('c94b825e-0132-4686-8049-8a4a2bc09525')
+ @decorators.skip_because(bug='1567134')
def test_get_db_flavor(self):
# The expected flavor details should be returned
flavor = (self.client.show_db_flavor(self.db_flavor_ref)
@@ -38,6 +39,7 @@
@test.attr(type='smoke')
@test.idempotent_id('685025d6-0cec-4673-8a8d-995cb8e0d3bb')
+ @decorators.skip_because(bug='1567134')
def test_list_db_flavors(self):
flavor = (self.client.show_db_flavor(self.db_flavor_ref)
['flavor'])
diff --git a/tempest/api/identity/v3/test_projects.py b/tempest/api/identity/v3/test_projects.py
index 995b77e..1574ab7 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -33,7 +33,7 @@
# user can successfully authenticate using his credentials and
# project name from received projects list
for project in resp['projects']:
- # 'user_domain_id' needs to be specified otherwise tempest_lib
+ # 'user_domain_id' needs to be specified otherwise tempest.lib
# assumes it to be 'default'
token_id, body = self.non_admin_token.get_token(
username=self.os.credentials.username,
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index 593bf2a..d5bed96 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -30,7 +30,7 @@
password = creds.password
user_domain_id = creds.user_domain_id
- # 'user_domain_id' needs to be specified otherwise tempest_lib assumes
+ # 'user_domain_id' needs to be specified otherwise tempest.lib assumes
# it to be 'default'
token_id, resp = self.non_admin_token.get_token(
user_id=user_id,
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 1a84d06..cad22f3 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -32,7 +32,7 @@
if container_format in a_formats and container_format != disk_format:
msg = ("The container format and the disk format don't match. "
- "Contaiter format: %(container)s, Disk format: %(disk)s." %
+ "Container format: %(container)s, Disk format: %(disk)s." %
{'container': container_format, 'disk': disk_format})
raise exceptions.InvalidConfiguration(message=msg)
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 3654b2e..46b068b 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -364,6 +364,23 @@
self._verify_router_interface(router['id'], subnet02['id'],
interface02['port_id'])
+ @test.idempotent_id('96522edf-b4b5-45d9-8443-fa11c26e6eff')
+ def test_router_interface_port_update_with_fixed_ip(self):
+ network = self.create_network()
+ subnet = self.create_subnet(network)
+ router = self._create_router(data_utils.rand_name('router-'))
+ fixed_ip = [{'subnet_id': subnet['id']}]
+ interface = self._add_router_interface_with_subnet_id(router['id'],
+ subnet['id'])
+ self.assertIn('port_id', interface)
+ self.assertIn('subnet_id', interface)
+ port = self.ports_client.show_port(interface['port_id'])
+ self.assertEqual(port['port']['id'], interface['port_id'])
+ router_port = self.ports_client.update_port(port['port']['id'],
+ fixed_ips=fixed_ip)
+ self.assertEqual(subnet['id'],
+ router_port['port']['fixed_ips'][0]['subnet_id'])
+
def _verify_router_interface(self, router_id, subnet_id, port_id):
show_port_body = self.ports_client.show_port(port_id)
interface_port = show_port_body['port']
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index e8b035b..dc926e0 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -179,23 +179,14 @@
@test.idempotent_id('84dafe57-9666-4f6d-84c8-0814d37923b8')
def test_create_object_with_expect_continue(self):
# create object with expect_continue
+
object_name = data_utils.rand_name(name='TestObject')
data = data_utils.arbitrary_string()
- metadata = {'Expect': '100-continue'}
- resp = self.object_client.create_object_continue(
- self.container_name,
- object_name,
- data,
- metadata=metadata)
- self.assertIn('status', resp)
- self.assertEqual(resp['status'], '100')
+ status, _ = self.object_client.create_object_continue(
+ self.container_name, object_name, data)
- self.object_client.create_object_continue(
- self.container_name,
- object_name,
- data,
- metadata=None)
+ self.assertEqual(status, 201)
# check uploaded content
_, body = self.object_client.get_object(self.container_name,
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index f19717e..00acc7d 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -32,14 +32,9 @@
@classmethod
def resource_setup(cls):
super(VolumeMultiBackendV2Test, cls).resource_setup()
- # support 2 backends names, deprecated_for_removal.
- # keep support 2 backend names, in case they are not empty
- if CONF.volume.backend1_name and CONF.volume.backend2_name:
- cls.backend_names = {CONF.volume.backend1_name,
- CONF.volume.backend2_name}
- else:
- # read backend name from a list .
- cls.backend_names = set(CONF.volume.backend_names)
+
+ # read backend name from a list .
+ cls.backend_names = set(CONF.volume.backend_names)
cls.name_field = cls.special_fields['name_field']
cls.volume_type_id_list = []
diff --git a/tempest/api/volume/admin/test_volume_pools.py b/tempest/api/volume/admin/test_volume_pools.py
new file mode 100644
index 0000000..c662e8c
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_pools.py
@@ -0,0 +1,43 @@
+# Copyright 2016 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.api.volume import base
+from tempest import test
+
+
+class VolumePoolsAdminV2TestsJSON(base.BaseVolumeAdminTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(VolumePoolsAdminV2TestsJSON, cls).resource_setup()
+ # Create a test shared volume for tests
+ cls.volume = cls.create_volume()
+
+ @test.idempotent_id('0248a46c-e226-4933-be10-ad6fca8227e7')
+ def test_get_pools_without_details(self):
+ volume_info = self.admin_volume_client. \
+ show_volume(self.volume['id'])['volume']
+ cinder_pools = self.admin_volume_client.show_pools()['pools']
+ self.assertIn(volume_info['os-vol-host-attr:host'],
+ [pool['name'] for pool in cinder_pools])
+
+ @test.idempotent_id('d4bb61f7-762d-4437-b8a4-5785759a0ced')
+ def test_get_pools_with_details(self):
+ volume_info = self.admin_volume_client. \
+ show_volume(self.volume['id'])['volume']
+ cinder_pools = self.admin_volume_client.\
+ show_pools(detail=True)['pools']
+ self.assertIn(volume_info['os-vol-host-attr:host'],
+ [pool['name'] for pool in cinder_pools])
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 7202881..6fc6f1c 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -28,9 +28,6 @@
self.volumes_client.delete_volume(volume_id)
self.volumes_client.wait_for_resource_deletion(volume_id)
- def _delete_volume_type(self, volume_type_id):
- self.volume_types_client.delete_volume_type(volume_type_id)
-
@test.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
def test_volume_type_list(self):
# List volume types.
@@ -54,7 +51,8 @@
name=vol_type_name,
extra_specs=extra_specs)['volume_type']
volume_types.append(vol_type)
- self.addCleanup(self._delete_volume_type, vol_type['id'])
+ self.addCleanup(self.volume_types_client.delete_volume_type,
+ vol_type['id'])
params = {self.name_field: vol_name,
'volume_type': volume_types[0]['id']}
@@ -103,7 +101,8 @@
name=name,
extra_specs=extra_specs)['volume_type']
self.assertIn('id', body)
- self.addCleanup(self._delete_volume_type, body['id'])
+ self.addCleanup(self.volume_types_client.delete_volume_type,
+ body['id'])
self.assertIn('name', body)
self.assertEqual(body['name'], name,
"The created volume_type name is not equal "
@@ -130,7 +129,8 @@
name = data_utils.rand_name("volume-type")
body = self.volume_types_client.create_volume_type(
name=name)['volume_type']
- self.addCleanup(self._delete_volume_type, body['id'])
+ self.addCleanup(self.volume_types_client.delete_volume_type,
+ body['id'])
# Create encryption type
encryption_type = self.volume_types_client.create_encryption_type(
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 866e676..6707121 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -14,6 +14,7 @@
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib import decorators
from tempest import test
CONF = config.CONF
@@ -34,6 +35,9 @@
cls.name_field = cls.special_fields['name_field']
cls.descrip_field = cls.special_fields['descrip_field']
+ # Create 2 snapshots
+ for _ in xrange(2):
+ cls.create_snapshot(cls.volume_origin['id'])
def _detach(self, volume_id):
"""Detach volume."""
@@ -58,6 +62,14 @@
('details' if with_detail else '', key)
self.assertEqual(params[key], snap[key], msg)
+ def _list_snapshots_by_param_limit(self, limit, expected_elements):
+ """list snapshots by limit param"""
+ # Get snapshots list using limit parameter
+ fetched_snap_list = self.snapshots_client.list_snapshots(
+ limit=limit)['snapshots']
+ # Validating filtered snapshots length equals to expected_elements
+ self.assertEqual(expected_elements, len(fetched_snap_list))
+
@test.idempotent_id('b467b54c-07a4-446d-a1cf-651dedcc3ff1')
@test.services('compute')
def test_snapshot_create_with_volume_in_use(self):
@@ -178,6 +190,25 @@
self.volumes_client.wait_for_resource_deletion(volume['id'])
self.cleanup_snapshot(snapshot)
+ @test.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
+ def test_snapshot_list_param_limit(self):
+ # List returns limited elements
+ self._list_snapshots_by_param_limit(limit=1, expected_elements=1)
+
+ @test.idempotent_id('a1427f61-420e-48a5-b6e3-0b394fa95400')
+ def test_snapshot_list_param_limit_equals_infinite(self):
+ # List returns all elements when request limit exceeded
+ # snapshots number
+ snap_list = self.snapshots_client.list_snapshots()['snapshots']
+ self._list_snapshots_by_param_limit(limit=100000,
+ expected_elements=len(snap_list))
+
+ @decorators.skip_because(bug='1540893')
+ @test.idempotent_id('e3b44b7f-ae87-45b5-8a8c-66110eb24d0a')
+ def test_snapshot_list_param_limit_equals_zero(self):
+ # List returns zero elements
+ self._list_snapshots_by_param_limit(limit=0, expected_elements=0)
+
def cleanup_snapshot(self, snapshot):
# Delete the snapshot
self.snapshots_client.delete_snapshot(snapshot['id'])
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index a154d0b..5fab961 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -262,7 +262,7 @@
user = identity.get_user_by_username(tenants_admin,
tenant['id'], u['name'])
except tempest.lib.exceptions.NotFound:
- LOG.error("User: %s - not found" % u['user'])
+ LOG.error("User: %s - not found" % u['name'])
continue
for r in u['role_ids']:
try:
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index ac67ce4..633b9e9 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -54,21 +54,28 @@
real_prefix = getattr(sys, 'real_prefix', None)
base_prefix = getattr(sys, 'base_prefix', None)
prefix = sys.prefix
+ global_conf_dir = '/etc/tempest'
if (real_prefix is None and
- (base_prefix is None or base_prefix == prefix)):
+ (base_prefix is None or base_prefix == prefix) and
+ os.path.isdir(global_conf_dir)):
# Probably not running in a virtual environment.
# NOTE(andreaf) we cannot distinguish this case from the case of
# a virtual environment created with virtualenv, and running python3.
# Also if it appears we are not in virtual env and fail to find
# global config: '/etc/tempest', fall back to
# '[sys.prefix]/etc/tempest'
- global_conf_dir = '/etc/tempest'
- if os.path.isdir(global_conf_dir):
- return global_conf_dir
- else:
- return os.path.join(prefix, 'etc/tempest')
+ return global_conf_dir
else:
- return os.path.join(prefix, 'etc/tempest')
+ conf_dir = os.path.join(prefix, 'etc/tempest')
+ if os.path.isdir(conf_dir):
+ return conf_dir
+ else:
+ # NOTE: The prefix is gotten from the path which pyconfig.h is
+ # installed under. Some envs contain it under /usr/include, not
+ # /user/local/include. Then prefix becomes /usr on such envs.
+ # However, etc/tempest is installed under /usr/local and the bove
+ # path logic mismatches. This is a workaround for such envs.
+ return os.path.join(prefix, 'local/etc/tempest')
class TempestInit(command.Command):
@@ -78,6 +85,10 @@
parser = super(TempestInit, self).get_parser(prog_name)
parser.add_argument('dir', nargs='?', default=os.getcwd())
parser.add_argument('--config-dir', '-c', default=None)
+ parser.add_argument('--show-global-config-dir', '-s',
+ action='store_true', dest='show_global_dir',
+ help="Print the global config dir location, "
+ "then exit")
return parser
def generate_testr_conf(self, local_path):
@@ -91,8 +102,7 @@
def update_local_conf(self, conf_path, lock_dir, log_dir):
config_parse = moves.configparser.SafeConfigParser()
config_parse.optionxform = str
- with open(conf_path, 'w+') as conf_file:
- config_parse.readfp(conf_file)
+ 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')
@@ -101,6 +111,7 @@
config_parse.set('DEFAULT', 'log_dir', log_dir)
# Set default log filename to tempest.log
config_parse.set('DEFAULT', 'log_file', 'tempest.log')
+ config_parse.write(conf_file)
def copy_config(self, etc_dir, config_dir):
shutil.copytree(config_dir, etc_dir)
@@ -149,4 +160,7 @@
def take_action(self, parsed_args):
config_dir = parsed_args.config_dir or get_tempest_default_config_dir()
+ if parsed_args.show_global_dir:
+ print("Global config dir is located at: %s" % config_dir)
+ sys.exit(0)
self.create_working_dir(parsed_args.dir, config_dir)
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index fbc5026..72ea894 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -16,6 +16,7 @@
import argparse
import os
+import re
import sys
import traceback
@@ -77,9 +78,16 @@
not CONF.image_feature_enabled.api_v2, update)
+def _remove_version_project(url_path):
+ # The regex matches strings like /v2.0, /v3/, /v2.1/project-id/
+ return re.sub(r'/v\d+(\.\d+)?(/[^/]+)?', '', url_path)
+
+
def _get_unversioned_endpoint(base_url):
endpoint_parts = urlparse.urlparse(base_url)
- endpoint = endpoint_parts.scheme + '://' + endpoint_parts.netloc
+ new_path = _remove_version_project(endpoint_parts.path)
+ endpoint_parts = endpoint_parts._replace(path=new_path)
+ endpoint = urlparse.urlunparse(endpoint_parts)
return endpoint
@@ -89,7 +97,9 @@
'keystone': os.identity_client,
'cinder': os.volumes_client,
}
- client_dict[service].skip_path()
+ if service != 'keystone':
+ # Since keystone may be listening on a path, do not remove the path.
+ client_dict[service].skip_path()
endpoint = _get_unversioned_endpoint(client_dict[service].base_url)
http = tempest.lib.common.http.ClosingHttp(
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index b5c4547..7ebc283 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -157,3 +157,29 @@
% server['id'])
return body, servers
+
+
+def shelve_server(client, server_id, force_shelve_offload=False):
+ """Common wrapper utility to shelve server.
+
+ This method is a common wrapper to make server in 'SHELVED'
+ or 'SHELVED_OFFLOADED' state.
+
+ :param server_id: Server to make in shelve state
+ :param force_shelve_offload: Forcefully offload shelve server if it
+ is configured not to offload server
+ automatically after offload time.
+ """
+ client.shelve_server(server_id)
+
+ offload_time = CONF.compute.shelved_offload_time
+ if offload_time >= 0:
+ waiters.wait_for_server_status(client, server_id,
+ 'SHELVED_OFFLOADED',
+ extra_timeout=offload_time)
+ else:
+ waiters.wait_for_server_status(client, server_id, 'SHELVED')
+ if force_shelve_offload:
+ client.shelve_offload_server(server_id)
+ waiters.wait_for_server_status(client, server_id,
+ 'SHELVED_OFFLOADED')
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index a4b2ae8..5cce0bb 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -88,6 +88,11 @@
def __getattr__(self, item):
return getattr(self._credentials, item)
+ def __str__(self):
+ _format = "Credentials: %s, Network: %s, Subnet: %s, Router: %s"
+ return _format % (self._credentials, self.network, self.subnet,
+ self.router)
+
def set_resources(self, **kwargs):
for key in kwargs.keys():
if hasattr(self, key):
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 58157ef..6cb43f3 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -26,7 +26,7 @@
"""This module provides factories of credential and credential providers
-Credentials providers and clients are (going to be) part of tempest-lib,
+Credentials providers and clients are (going to be) part of tempest.lib,
and so they may not hold any dependency to tempest configuration.
Methods in this module collect the relevant configuration details and pass
@@ -133,7 +133,7 @@
def get_creds_by_roles(self, roles, force_new=False):
msg = "Credentials being specified through the config file can not be"\
" used with tests that specify using credentials by roles. "\
- "Either exclude/skip the tests doing this or use either an "\
+ "Either exclude/skip the tests doing this or use either a "\
"test_accounts_file or dynamic credentials."
raise exceptions.InvalidConfiguration(msg)
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index d374be4..a63af38 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -154,6 +154,20 @@
return cred_provider.TestResources(creds)
def _create_network_resources(self, tenant_id):
+ """The function creates network resources in the given tenant.
+
+ The function checks if network_resources class member is empty,
+ In case it is, it will create a network, a subnet and a router for
+ the tenant according to the given tenant id parameter.
+ Otherwise it will create a network resource according
+ to the values from network_resources dict.
+
+ :param tenant_id: The tenant id to create resources for.
+ :type tenant_id: str
+ :raises: InvalidConfiguration, Exception
+ :returns: network resources(network,subnet,router)
+ :rtype: tuple
+ """
network = None
subnet = None
router = None
diff --git a/tempest/common/generator/base_generator.py b/tempest/common/generator/base_generator.py
index 3a51f2e..0647edb 100644
--- a/tempest/common/generator/base_generator.py
+++ b/tempest/common/generator/base_generator.py
@@ -169,7 +169,7 @@
expected_result = generator_result[2]
element = path.pop()
if len(path) > 0:
- schema_snip = reduce(dict.get, path, schema)
+ schema_snip = six.moves.reduce(dict.get, path, schema)
schema_snip[element] = invalid_snippet
else:
schema[element] = invalid_snippet
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index f3df387..51f723b 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -219,9 +219,9 @@
else:
hashes = self.hash_dict['creds'].keys()
# NOTE(mtreinish): admin is a special case because of the increased
- # privlege set which could potentially cause issues on tests where that
- # is not expected. So unless the admin role isn't specified do not
- # allocate admin.
+ # privilege set which could potentially cause issues on tests where
+ # that is not expected. So unless the admin role isn't specified do
+ # not allocate admin.
admin_hashes = self.hash_dict['roles'].get(self.admin_role,
None)
if ((not roles or self.admin_role not in roles) and
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 3a215a0..3f573b7 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -12,6 +12,8 @@
import netaddr
import re
+import six
+import sys
import time
from oslo_log import log as logging
@@ -19,6 +21,7 @@
from tempest import config
from tempest import exceptions
from tempest.lib.common import ssh
+from tempest.lib.common.utils import misc as misc_utils
import tempest.lib.exceptions
CONF = config.CONF
@@ -26,16 +29,61 @@
LOG = logging.getLogger(__name__)
+def debug_ssh(function):
+ """Decorator to generate extra debug info in case off SSH failure"""
+ def wrapper(self, *args, **kwargs):
+ try:
+ return function(self, *args, **kwargs)
+ except tempest.lib.exceptions.SSHTimeout:
+ try:
+ original_exception = sys.exc_info()
+ caller = misc_utils.find_test_caller() or "not found"
+ if self.server:
+ msg = 'Caller: %s. Timeout trying to ssh to server %s'
+ LOG.debug(msg, caller, self.server)
+ if self.log_console and self.servers_client:
+ try:
+ msg = 'Console log for server %s: %s'
+ console_log = (
+ self.servers_client.get_console_output(
+ self.server['id'])['output'])
+ LOG.debug(msg, self.server['id'], console_log)
+ except Exception:
+ msg = 'Could not get console_log for server %s'
+ LOG.debug(msg, self.server['id'])
+ # re-raise the original ssh timeout exception
+ six.reraise(*original_exception)
+ finally:
+ # Delete the traceback to avoid circular references
+ _, _, trace = original_exception
+ del trace
+ return wrapper
+
+
class RemoteClient(object):
- def __init__(self, ip_address, username, password=None, pkey=None):
+ def __init__(self, ip_address, username, password=None, pkey=None,
+ server=None, servers_client=None):
+ """Executes commands in a VM over ssh
+
+ :param ip_address: IP address to ssh to
+ :param username: ssh username
+ :param password: ssh password (optional)
+ :param pkey: ssh public key (optional)
+ :param server: server dict, used for debugging purposes
+ :param servers_client: servers client, used for debugging purposes
+ """
+ self.server = server
+ self.servers_client = servers_client
ssh_timeout = CONF.validation.ssh_timeout
connect_timeout = CONF.validation.connect_timeout
+ self.log_console = CONF.compute_feature_enabled.console_output
self.ssh_client = ssh.Client(ip_address, username, password,
ssh_timeout, pkey=pkey,
channel_timeout=connect_timeout)
+ @debug_ssh
def exec_command(self, cmd):
# Shell options below add more clearness on failures,
# path is extended for some non-cirros guest oses (centos7)
@@ -43,6 +91,7 @@
LOG.debug("Remote command: %s" % cmd)
return self.ssh_client.exec_command(cmd)
+ @debug_ssh
def validate_authentication(self):
"""Validate ssh connection and authentication
diff --git a/tempest/config.py b/tempest/config.py
index f8d49b9..30cd501 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -15,6 +15,7 @@
from __future__ import print_function
+import functools
import logging as std_logging
import os
import tempfile
@@ -22,6 +23,7 @@
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log as logging
+import testtools
from tempest.test_discover import plugins
@@ -91,7 +93,7 @@
"create users and projects",
deprecated_group='identity'),
cfg.StrOpt('admin_project_name',
- help="Project name to use for an administrative user. This is "
+ help="Project name to use for an administrative user. This is "
"needed for authenticating requests made by project "
"isolation to create users and projects",
deprecated_opts=[cfg.DeprecatedOpt('admin_tenant_name',
@@ -99,7 +101,7 @@
cfg.DeprecatedOpt('admin_tenant_name',
group='identity')]),
cfg.StrOpt('admin_password',
- help="Password to use for an administrative user. This is "
+ help="Password to use for an administrative user. This is "
"needed for authenticating requests made by project "
"isolation to create users and projects",
secret=True,
@@ -700,22 +702,10 @@
choices=['public', 'admin', 'internal',
'publicURL', 'adminURL', 'internalURL'],
help="The endpoint type to use for the volume service."),
- cfg.StrOpt('backend1_name',
- default='',
- help='Name of the backend1 (must be declared in cinder.conf)',
- deprecated_for_removal=True),
- cfg.StrOpt('backend2_name',
- default='',
- help='Name of the backend2 (must be declared in cinder.conf)',
- deprecated_for_removal=True),
cfg.ListOpt('backend_names',
default=['BACKEND_1', 'BACKEND_2'],
help='A list of backend names separated by comma. '
- 'The backend name must be declared in cinder.conf',
- deprecated_opts=[cfg.DeprecatedOpt('BACKEND_1',
- group='volume'),
- cfg.DeprecatedOpt('BACKEND_2',
- group='volume')]),
+ 'The backend name must be declared in cinder.conf'),
cfg.StrOpt('storage_protocol',
default='iSCSI',
help='Backend protocol to target when creating volume types'),
@@ -1109,7 +1099,7 @@
'shelve, snapshot, and suspend')
-# NOTE(deva): Ironic tests have been ported to tempest-lib. New config options
+# NOTE(deva): Ironic tests have been ported to tempest.lib. New config options
# should be added to ironic/ironic_tempest_plugin/config.py.
# However, these options need to remain here for testing stable
# branches until Liberty release reaches EOL.
@@ -1342,3 +1332,72 @@
CONF = TempestConfigProxy()
+
+
+def skip_unless_config(*args):
+ """Decorator to raise a skip if a config opt doesn't exist or is False
+
+ :param str group: The first arg, the option group to check
+ :param str name: The second arg, the option name to check
+ :param str msg: Optional third arg, the skip msg to use if a skip is raised
+ :raises testtools.TestCaseskipException: If the specified config option
+ doesn't exist or it exists and evaluates to False
+ """
+ def decorator(f):
+ group = args[0]
+ name = args[1]
+
+ @functools.wraps(f)
+ def wrapper(self, *func_args, **func_kwargs):
+ if not hasattr(CONF, group):
+ msg = "Config group %s doesn't exist" % group
+ raise testtools.TestCase.skipException(msg)
+
+ conf_group = getattr(CONF, group)
+ if not hasattr(conf_group, name):
+ msg = "Config option %s.%s doesn't exist" % (group,
+ name)
+ raise testtools.TestCase.skipException(msg)
+
+ value = getattr(conf_group, name)
+ if not value:
+ if len(args) == 3:
+ msg = args[2]
+ else:
+ msg = "Config option %s.%s is false" % (group,
+ name)
+ raise testtools.TestCase.skipException(msg)
+ return f(self, *func_args, **func_kwargs)
+ return wrapper
+ return decorator
+
+
+def skip_if_config(*args):
+ """Raise a skipException if a config exists and is True
+
+ :param str group: The first arg, the option group to check
+ :param str name: The second arg, the option name to check
+ :param str msg: Optional third arg, the skip msg to use if a skip is raised
+ :raises testtools.TestCase.skipException: If the specified config option
+ exists and evaluates to True
+ """
+ def decorator(f):
+ group = args[0]
+ name = args[1]
+
+ @functools.wraps(f)
+ def wrapper(self, *func_args, **func_kwargs):
+ if hasattr(CONF, group):
+ conf_group = getattr(CONF, group)
+ if hasattr(conf_group, name):
+ value = getattr(conf_group, name)
+ if value:
+ if len(args) == 3:
+ msg = args[2]
+ else:
+ msg = "Config option %s.%s is false" % (group,
+ name)
+ raise testtools.TestCase.skipException(msg)
+ return f(self, *func_args, **func_kwargs)
+ return wrapper
+ return decorator
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index b8e052f..92f335f 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -116,7 +116,7 @@
# NOTE(andreaf) This exception is added here to facilitate the migration
-# of get_network_from_name and preprov_creds to tempest-lib, and it should
+# of get_network_from_name and preprov_creds to tempest.lib, and it should
# be migrated along with them
class InvalidTestResource(TempestException):
message = "%(name) is not a valid %(type), or the name is ambiguous"
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 125fb3c..aff9dee 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -140,7 +140,7 @@
"""
if TESTTOOLS_SKIP_DECORATOR.match(logical_line):
yield (0, "T109: Cannot use testtools.skip decorator; instead use "
- "decorators.skip_because from tempest-lib")
+ "decorators.skip_because from tempest.lib")
def _common_service_clients_check(logical_line, physical_line, filename,
diff --git a/tempest/lib/api_schema/response/compute/v2_19/__init__.py b/tempest/lib/api_schema/response/compute/v2_19/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_19/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_19/servers.py b/tempest/lib/api_schema/response/compute/v2_19/servers.py
new file mode 100644
index 0000000..883839e
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -0,0 +1,49 @@
+# Copyright 2016 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.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers as serversv21
+from tempest.lib.api_schema.response.compute.v2_9 import servers as serversv29
+
+get_server = copy.deepcopy(serversv29.get_server)
+get_server['response_body']['properties']['server'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+get_server['response_body']['properties']['server'][
+ 'required'].append('description')
+
+list_servers_detail = copy.deepcopy(serversv29.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'required'].append('description')
+
+update_server = copy.deepcopy(serversv21.update_server)
+update_server['response_body']['properties']['server'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+update_server['response_body']['properties']['server'][
+ 'required'].append('description')
+
+rebuild_server = copy.deepcopy(serversv21.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+rebuild_server['response_body']['properties']['server'][
+ 'required'].append('description')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+ serversv21.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'required'].append('description')
diff --git a/tempest/lib/api_schema/response/compute/v2_9/__init__.py b/tempest/lib/api_schema/response/compute/v2_9/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_9/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py
new file mode 100644
index 0000000..e9b7249
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -0,0 +1,29 @@
+# Copyright 2016 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.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers
+
+get_server = copy.deepcopy(servers.get_server)
+get_server['response_body']['properties']['server'][
+ 'properties'].update({'locked': {'type': 'boolean'}})
+get_server['response_body']['properties']['server'][
+ 'required'].append('locked')
+
+list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'properties'].update({'locked': {'type': 'boolean'}})
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'required'].append('locked')
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
index b7d6a24..f35b14c 100755
--- a/tempest/lib/cmd/skip_tracker.py
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -48,7 +48,7 @@
def find_skips(start):
- """Find the entire list of skiped tests.
+ """Find the entire list of skipped tests.
Returns a list of tuples (method, bug) that represent
test methods that have been decorated to skip because of
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index d001d27..fafb303 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -400,6 +400,10 @@
caller_name=None, extra=None):
if 'X-Auth-Token' in req_headers:
req_headers['X-Auth-Token'] = '<omitted>'
+ # A shallow copy is sufficient
+ resp_log = resp.copy()
+ if 'x-subject-token' in resp_log:
+ resp_log['x-subject-token'] = '<omitted>'
log_fmt = """Request - Headers: %s
Body: %s
Response - Headers: %s
@@ -409,7 +413,7 @@
log_fmt % (
str(req_headers),
self._safe_body(req_body),
- str(resp),
+ str(resp_log),
self._safe_body(resp_body)),
extra=extra)
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 01b6477..9605479 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -39,7 +39,7 @@
def rand_name(name='', prefix=None):
- """Generate a random name that inclues a random number
+ """Generate a random name that includes a random number
:param str name: The name that you want to include
:param str prefix: The prefix that you want to include
@@ -81,7 +81,7 @@
def rand_url():
- """Generate a random url that inclues a random number
+ """Generate a random url that includes a random number
:return: a random url. The format is 'https://url-<random number>.com'.
(e.g. 'https://url-154876201.com')
@@ -121,6 +121,18 @@
return ':'.join(["%02x" % x for x in mac])
+def rand_infiniband_guid_address():
+ """Generate an Infiniband GUID address
+
+ :return: an random Infiniband GUID address
+ :rtype: string
+ """
+ guid = []
+ for i in range(8):
+ guid.append("%02x" % random.randint(0x00, 0xff))
+ return ':'.join(guid)
+
+
def parse_image_id(image_ref):
"""Return the image id from a given image ref
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index a37f167..8e4eca1 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -20,11 +20,17 @@
from six.moves.urllib import parse as urllib
from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
+from tempest.lib.api_schema.response.compute.v2_19 import servers as schemav219
+from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
class ServersClient(base_compute_client.BaseComputeClient):
+ schema_versions_info = [
+ {'min': None, 'max': '2.8', 'schema': schema},
+ {'min': '2.9', 'max': '2.18', 'schema': schemav29},
+ {'min': '2.19', 'max': None, 'schema': schemav219}]
def __init__(self, auth_provider, service, region,
enable_instance_password=True, **kwargs):
@@ -55,7 +61,7 @@
post_body = {'server': body}
if hints:
- post_body = dict(post_body.items() + hints.items())
+ post_body.update(hints)
post_body = json.dumps(post_body)
resp, body = self.post('servers', post_body)
@@ -88,6 +94,7 @@
post_body = json.dumps({'server': kwargs})
resp, body = self.put("servers/%s" % server_id, post_body)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.update_server, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -95,6 +102,7 @@
"""Get server details."""
resp, body = self.get("servers/%s" % server_id)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_server, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -114,6 +122,7 @@
"""
url = 'servers'
+ schema = self.get_schema(self.schema_versions_info)
_schema = schema.list_servers
if detail:
@@ -209,6 +218,7 @@
kwargs['imageRef'] = image_ref
if 'disk_config' in kwargs:
kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config')
+ schema = self.get_schema(self.schema_versions_info)
if self.enable_instance_password:
rebuild_schema = schema.rebuild_server_with_admin_pass
else:
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 988ee1a..956fe88 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -29,7 +29,7 @@
from tempest import exceptions
from tempest.lib.common.utils import misc as misc_utils
from tempest.lib import exceptions as lib_exc
-from tempest.services.network import resources as net_resources
+from tempest.scenario import network_resources
import tempest.test
CONF = config.CONF
@@ -648,7 +648,7 @@
"""
if CONF.validation.connect_method == 'floating':
# The tests calling this method don't have a floating IP
- # and can't make use of the validattion resources. So the
+ # and can't make use of the validation resources. So the
# method is creating the floating IP there.
return self.create_floating_ip(server)['ip']
elif CONF.validation.connect_method == 'fixed':
@@ -697,7 +697,7 @@
tenant_id = networks_client.tenant_id
name = data_utils.rand_name(namestart)
result = networks_client.create_network(name=name, tenant_id=tenant_id)
- network = net_resources.DeletableNetwork(
+ network = network_resources.DeletableNetwork(
networks_client=networks_client, routers_client=routers_client,
**result['network'])
self.assertEqual(network.name, name)
@@ -790,7 +790,7 @@
if not is_overlapping_cidr:
raise
self.assertIsNotNone(result, 'Unable to allocate tenant network')
- subnet = net_resources.DeletableSubnet(
+ subnet = network_resources.DeletableSubnet(
subnets_client=subnets_client,
routers_client=routers_client, **result['subnet'])
self.assertEqual(subnet.cidr, str_cidr)
@@ -807,8 +807,8 @@
network_id=network_id,
**kwargs)
self.assertIsNotNone(result, 'Unable to allocate port')
- port = net_resources.DeletablePort(ports_client=client,
- **result['port'])
+ port = network_resources.DeletablePort(ports_client=client,
+ **result['port'])
self.addCleanup(self.delete_wrapper, port.delete)
return port
@@ -838,7 +838,7 @@
net = self._list_networks(name=network_name)
self.assertNotEqual(len(net), 0,
"Unable to get network by name: %s" % network_name)
- return net_resources.AttributeDict(net[0])
+ return network_resources.AttributeDict(net[0])
def create_floating_ip(self, thing, external_network_id=None,
port_id=None, client=None):
@@ -857,7 +857,7 @@
tenant_id=thing['tenant_id'],
fixed_ip_address=ip4
)
- floating_ip = net_resources.DeletableFloatingIp(
+ floating_ip = network_resources.DeletableFloatingIp(
client=client,
**result['floatingip'])
self.addCleanup(self.delete_wrapper, floating_ip.delete)
@@ -878,8 +878,8 @@
def check_floating_ip_status(self, floating_ip, status):
"""Verifies floatingip reaches the given status
- :param floating_ip: net_resources.DeletableFloatingIp floating IP to
- to check status
+ :param floating_ip: network_resources.DeletableFloatingIp floating
+ IP to check status
:param status: target status
:raises: AssertionError if status doesn't match
"""
@@ -991,7 +991,7 @@
description=sg_desc)
sg_dict['tenant_id'] = tenant_id
result = client.create_security_group(**sg_dict)
- secgroup = net_resources.DeletableSecurityGroup(
+ secgroup = network_resources.DeletableSecurityGroup(
client=client, routers_client=self.routers_client,
**result['security_group']
)
@@ -1016,8 +1016,8 @@
]
msg = "No default security group for tenant %s." % (tenant_id)
self.assertTrue(len(sgs) > 0, msg)
- return net_resources.DeletableSecurityGroup(client=client,
- **sgs[0])
+ return network_resources.DeletableSecurityGroup(client=client,
+ **sgs[0])
def _create_security_group_rule(self, secgroup=None,
sec_group_rules_client=None,
@@ -1055,7 +1055,7 @@
ruleset.update(kwargs)
sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
- sg_rule = net_resources.DeletableSecurityGroupRule(
+ sg_rule = network_resources.DeletableSecurityGroupRule(
client=sec_group_rules_client,
**sg_rule['security_group_rule']
)
@@ -1135,7 +1135,7 @@
network_id = CONF.network.public_network_id
if router_id:
body = client.show_router(router_id)
- return net_resources.AttributeDict(**body['router'])
+ return network_resources.AttributeDict(**body['router'])
elif network_id:
router = self._create_router(client, tenant_id)
router.set_gateway(network_id)
@@ -1154,8 +1154,8 @@
result = client.create_router(name=name,
admin_state_up=True,
tenant_id=tenant_id)
- router = net_resources.DeletableRouter(routers_client=client,
- **result['router'])
+ router = network_resources.DeletableRouter(routers_client=client,
+ **result['router'])
self.assertEqual(router.name, name)
self.addCleanup(self.delete_wrapper, router.delete)
return router
diff --git a/tempest/services/network/resources.py b/tempest/scenario/network_resources.py
similarity index 98%
rename from tempest/services/network/resources.py
rename to tempest/scenario/network_resources.py
index 329c54d..667476f 100644
--- a/tempest/services/network/resources.py
+++ b/tempest/scenario/network_resources.py
@@ -76,7 +76,7 @@
"""Waits for a network resource to reach a status
@param fetch: the callable to be used to query the resource status
- @type fecth: callable that takes no parameters and returns the resource
+ @type fetch: callable that takes no parameters and returns the resource
@param status: the status that the resource has to reach
@type status: String
"""
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index dfa4815..b9fdd18 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -24,7 +24,7 @@
from tempest import config
from tempest import exceptions
from tempest.scenario import manager
-from tempest.services.network import resources as net_resources
+from tempest.scenario import network_resources
from tempest import test
CONF = config.CONF
@@ -269,8 +269,9 @@
"Old port: %s. Number of new ports: %d" % (
CONF.network.build_timeout, old_port,
len(self.new_port_list)))
- new_port = net_resources.DeletablePort(ports_client=self.ports_client,
- **self.new_port_list[0])
+ new_port = network_resources.DeletablePort(
+ ports_client=self.ports_client,
+ **self.new_port_list[0])
def check_new_nic():
new_nic_list = self._get_server_nics(ssh_client)
@@ -686,8 +687,9 @@
unschedule_router = (self.admin_manager.network_agents_client.
delete_router_from_l3_agent)
- agent_list = set(a["id"] for a in
- self._list_agents(agent_type="L3 agent"))
+ agent_list_alive = set(a["id"] for a in
+ self._list_agents(agent_type="L3 agent") if
+ a["alive"] is True)
self._setup_network_and_servers()
# NOTE(kevinbenton): we have to use the admin credentials to check
@@ -702,7 +704,7 @@
# remove resource from agents
hosting_agents = set(a["id"] for a in
list_hosts(self.router.id)['agents'])
- no_migration = agent_list == hosting_agents
+ no_migration = agent_list_alive == hosting_agents
LOG.info("Router will be assigned to {mig} hosting agent".
format(mig="the same" if no_migration else "a new"))
@@ -722,7 +724,7 @@
# schedule resource to new agent
target_agent = list(hosting_agents if no_migration else
- agent_list - hosting_agents)[0]
+ agent_list_alive - hosting_agents)[0]
schedule_router(target_agent,
router_id=self.router['id'])
self.assertEqual(
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 4b932ce..504d72b 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -31,7 +31,7 @@
"""The test suite for server advanced operations
This test case stresses some advanced server instance operations:
- * Resizing an instance
+ * Resizing a volume-backed instance
* Sequence suspend resume
"""
@@ -50,10 +50,10 @@
@test.idempotent_id('e6c28180-7454-4b59-b188-0257af08a63b')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize is not available.')
- @test.services('compute')
- def test_resize_server_confirm(self):
+ @test.services('compute', 'volume')
+ def test_resize_volume_backed_server_confirm(self):
# We create an instance for use in this test
- instance = self.create_server(wait_until='ACTIVE')
+ instance = self.create_server(wait_until='ACTIVE', volume_backed=True)
instance_id = instance['id']
resize_flavor = CONF.compute.flavor_ref_alt
LOG.debug("Resizing instance %s from flavor %s to flavor %s",
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 77de47e..6d3ecd4 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -15,6 +15,7 @@
import testtools
+from tempest.common import compute
from tempest.common import waiters
from tempest import config
from tempest.scenario import manager
@@ -35,18 +36,9 @@
"""
def _shelve_then_unshelve_server(self, server):
- self.servers_client.shelve_server(server['id'])
- offload_time = CONF.compute.shelved_offload_time
- if offload_time >= 0:
- waiters.wait_for_server_status(self.servers_client, server['id'],
- 'SHELVED_OFFLOADED',
- extra_timeout=offload_time)
- else:
- waiters.wait_for_server_status(self.servers_client,
- server['id'], 'SHELVED')
- self.servers_client.shelve_offload_server(server['id'])
- waiters.wait_for_server_status(self.servers_client, server['id'],
- 'SHELVED_OFFLOADED')
+ compute.shelve_server(self.servers_client, server['id'],
+ force_shelve_offload=True)
+
self.servers_client.unshelve_server(server['id'])
waiters.wait_for_server_status(self.servers_client, server['id'],
'ACTIVE')
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 78bda5d..fa43d94 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -18,6 +18,7 @@
from six.moves.urllib import parse as urlparse
from tempest.lib.common import rest_client
+from tempest.lib import exceptions
class ObjectClient(rest_client.RestClient):
@@ -150,9 +151,6 @@
def put_object_with_chunk(self, container, name, contents, chunk_size):
"""Put an object with Transfer-Encoding header"""
- if self.base_url is None:
- self._set_auth()
-
headers = {'Transfer-Encoding': 'chunked'}
if self.token:
headers['X-Auth-Token'] = self.token
@@ -173,31 +171,67 @@
def create_object_continue(self, container, object_name,
data, metadata=None):
- """Create storage object."""
+ """Put an object using Expect:100-continue"""
headers = {}
if metadata:
for key in metadata:
headers[str(key)] = metadata[key]
- if not data:
- headers['content-length'] = '0'
-
- if self.base_url is None:
- self._set_auth()
headers['X-Auth-Token'] = self.token
+ headers['content-length'] = 0 if data is None else len(data)
+ headers['Expect'] = '100-continue'
- conn = put_object_connection(self.base_url, str(container),
- str(object_name), data, None, headers)
+ parsed = urlparse.urlparse(self.base_url)
+ path = str(parsed.path) + "/"
+ path += "%s/%s" % (str(container), str(object_name))
+ conn = create_connection(parsed)
+
+ # Send the PUT request and the headers including the "Expect" header
+ conn.putrequest('PUT', path)
+
+ for header, value in six.iteritems(headers):
+ conn.putheader(header, value)
+ conn.endheaders()
+
+ # Read the 100 status prior to sending the data
response = conn.response_class(conn.sock,
strict=conn.strict,
method=conn._method)
- version, status, reason = response._read_status()
- resp = {'version': version,
- 'status': str(status),
- 'reason': reason}
+ _, status, _ = response._read_status()
- return resp
+ # toss the CRLF at the end of the response
+ response._safe_read(2)
+
+ # Expecting a 100 here, if not close and throw an exception
+ if status != 100:
+ conn.close()
+ pattern = "%s %s" % (
+ """Unexpected http success status code {0}.""",
+ """The expected status code is {1}""")
+ details = pattern.format(status, 100)
+ raise exceptions.UnexpectedResponseCode(details)
+
+ # If a continue was received go ahead and send the data
+ # and get the final response
+ conn.send(data)
+
+ resp = conn.getresponse()
+
+ return resp.status, resp.reason
+
+
+def create_connection(parsed_url):
+ """Helper function to create connection with httplib
+
+ :param parsed_url: parsed url of the remote location
+ """
+ if parsed_url.scheme == 'https':
+ conn = httplib.HTTPSConnection(parsed_url.netloc)
+ else:
+ conn = httplib.HTTPConnection(parsed_url.netloc)
+
+ return conn
def put_object_connection(base_url, container, name, contents=None,
@@ -216,13 +250,12 @@
:param query_string: if set will be appended with '?' to generated path
"""
parsed = urlparse.urlparse(base_url)
- if parsed.scheme == 'https':
- conn = httplib.HTTPSConnection(parsed.netloc)
- else:
- conn = httplib.HTTPConnection(parsed.netloc)
+
path = str(parsed.path) + "/"
path += "%s/%s" % (str(container), str(name))
+ conn = create_connection(parsed)
+
if query_string:
path += '?' + query_string
if headers:
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
index 4344802..6237745 100644
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -62,6 +62,17 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+ def show_pools(self, detail=False):
+ # List all the volumes pools (hosts)
+ url = 'scheduler-stats/get_pools'
+ if detail:
+ url += '?detail=True'
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
def show_volume(self, volume_id):
"""Returns the details of a single volume."""
url = "volumes/%s" % str(volume_id)
diff --git a/tempest/test.py b/tempest/test.py
index bbadb5c..aefe1a9 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -349,11 +349,14 @@
@classmethod
def setup_credentials(cls):
- """Allocate credentials and the client managers from them.
+ """Allocate credentials and create the client managers from them.
- A test class that requires network resources must override
- setup_credentials and defined the required resources before super
- is invoked.
+ For every element of credentials param function creates tenant/user,
+ Then it creates client manager for that credential.
+
+ Network related tests must override this function with
+ set_network_resources() method, otherwise it will create
+ network resources(network resources are created in a later step).
"""
for credentials_type in cls.credentials:
# This may raise an exception in case credentials are not available
diff --git a/tempest/tests/lib/base.py b/tempest/tests/base.py
similarity index 100%
rename from tempest/tests/lib/base.py
rename to tempest/tests/base.py
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
index b8c9969..2d0256a 100644
--- a/tempest/tests/cmd/test_javelin.py
+++ b/tempest/tests/cmd/test_javelin.py
@@ -17,7 +17,7 @@
from tempest.cmd import javelin
from tempest.lib import exceptions as lib_exc
-from tempest.tests.lib import base
+from tempest.tests import base
class JavelinUnitTest(base.TestCase):
diff --git a/tempest/tests/cmd/test_list_plugins.py b/tempest/tests/cmd/test_list_plugins.py
index 782dde7..17ddb18 100644
--- a/tempest/tests/cmd/test_list_plugins.py
+++ b/tempest/tests/cmd/test_list_plugins.py
@@ -14,7 +14,7 @@
import subprocess
-from tempest.tests.lib import base
+from tempest.tests import base
class TestTempestListPlugins(base.TestCase):
diff --git a/tempest/tests/cmd/test_tempest_init.py b/tempest/tests/cmd/test_tempest_init.py
index 6c5326a..685a0b3 100644
--- a/tempest/tests/cmd/test_tempest_init.py
+++ b/tempest/tests/cmd/test_tempest_init.py
@@ -18,7 +18,7 @@
import fixtures
from tempest.cmd import init
-from tempest.tests.lib import base
+from tempest.tests import base
class TestTempestInit(base.TestCase):
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 330c393..70cbf87 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -12,28 +12,56 @@
# License for the specific language governing permissions and limitations
# under the License.
-
import mock
from oslo_serialization import jsonutils as json
from oslotest import mockpatch
from tempest.cmd import verify_tempest_config
from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
class TestGetAPIVersions(base.TestCase):
+ def test_remove_version_project(self):
+ f = verify_tempest_config._remove_version_project
+ self.assertEqual('/', f('/v2.1/%s/' % data_utils.rand_uuid_hex()))
+ self.assertEqual('', f('/v2.1/tenant_id'))
+ self.assertEqual('', f('/v3'))
+ self.assertEqual('/', f('/v3/'))
+ self.assertEqual('/something/', f('/something/v2.1/tenant_id/'))
+ self.assertEqual('/something', f('/something/v2.1/tenant_id'))
+ self.assertEqual('/something', f('/something/v3'))
+ self.assertEqual('/something/', f('/something/v3/'))
+ self.assertEqual('/', f('/')) # http://localhost/
+ self.assertEqual('', f('')) # http://localhost
+
def test_url_grab_versioned_nova_nossl(self):
base_url = 'http://127.0.0.1:8774/v2/'
endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
- self.assertEqual('http://127.0.0.1:8774', endpoint)
+ self.assertEqual('http://127.0.0.1:8774/', endpoint)
def test_url_grab_versioned_nova_ssl(self):
base_url = 'https://127.0.0.1:8774/v3/'
endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
- self.assertEqual('https://127.0.0.1:8774', endpoint)
+ self.assertEqual('https://127.0.0.1:8774/', endpoint)
+
+ def test_get_unversioned_endpoint_base(self):
+ base_url = 'https://127.0.0.1:5000/'
+ endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
+ self.assertEqual('https://127.0.0.1:5000/', endpoint)
+
+ def test_get_unversioned_endpoint_subpath(self):
+ base_url = 'https://127.0.0.1/identity/v3'
+ endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
+ self.assertEqual('https://127.0.0.1/identity', endpoint)
+
+ def test_get_unversioned_endpoint_subpath_trailing_solidus(self):
+ base_url = 'https://127.0.0.1/identity/v3/'
+ endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
+ self.assertEqual('https://127.0.0.1/identity/', endpoint)
class TestDiscovery(base.TestCase):
diff --git a/tempest/tests/common/test_admin_available.py b/tempest/tests/common/test_admin_available.py
index 7461c4b..01a9cd0 100644
--- a/tempest/tests/common/test_admin_available.py
+++ b/tempest/tests/common/test_admin_available.py
@@ -17,8 +17,8 @@
from tempest.common import credentials_factory as credentials
from tempest import config
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
class TestAdminAvailable(base.TestCase):
diff --git a/tempest/tests/common/test_alt_available.py b/tempest/tests/common/test_alt_available.py
index 1a36068..d4cfab6 100644
--- a/tempest/tests/common/test_alt_available.py
+++ b/tempest/tests/common/test_alt_available.py
@@ -17,8 +17,8 @@
from tempest.common import credentials_factory as credentials
from tempest import config
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
class TestAltAvailable(base.TestCase):
diff --git a/tempest/tests/common/test_configured_creds.py b/tempest/tests/common/test_configured_creds.py
index 2ce354d..3c242b3 100644
--- a/tempest/tests/common/test_configured_creds.py
+++ b/tempest/tests/common/test_configured_creds.py
@@ -21,8 +21,8 @@
from tempest.lib import exceptions as lib_exc
from tempest.lib.services.identity.v2 import token_client as v2_client
from tempest.lib.services.identity.v3 import token_client as v3_client
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
from tempest.tests.lib import fake_identity
diff --git a/tempest/tests/common/test_credentials.py b/tempest/tests/common/test_credentials.py
index 3d09d9f..00f2d39 100644
--- a/tempest/tests/common/test_credentials.py
+++ b/tempest/tests/common/test_credentials.py
@@ -15,8 +15,8 @@
from tempest.common import credentials_factory as credentials
from tempest import config
from tempest import exceptions
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
class TestLegacyCredentialsProvider(base.TestCase):
diff --git a/tempest/tests/common/test_custom_matchers.py b/tempest/tests/common/test_custom_matchers.py
index d664961..2656a47 100644
--- a/tempest/tests/common/test_custom_matchers.py
+++ b/tempest/tests/common/test_custom_matchers.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.common import custom_matchers
-from tempest.tests.lib import base
+from tempest.tests import base
from testtools.tests.matchers import helpers
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index 1488793..8d4f33b 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -31,8 +31,8 @@
from tempest.services.identity.v2.json import users_client as \
json_users_client
from tempest.services.network.json import routers_client
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
from tempest.tests.lib import fake_http
from tempest.tests.lib import fake_identity
diff --git a/tempest/tests/common/test_preprov_creds.py b/tempest/tests/common/test_preprov_creds.py
index fba5b1f..b595c88 100644
--- a/tempest/tests/common/test_preprov_creds.py
+++ b/tempest/tests/common/test_preprov_creds.py
@@ -28,8 +28,8 @@
from tempest.lib import auth
from tempest.lib import exceptions as lib_exc
from tempest.lib.services.identity.v2 import token_client
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
from tempest.tests.lib import fake_identity
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index e0cef62..492bdca 100644
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -19,7 +19,7 @@
from tempest.common import waiters
from tempest import exceptions
from tempest.services.volume.base import base_volumes_client
-from tempest.tests.lib import base
+from tempest.tests import base
import tempest.tests.utils as utils
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index b3bba72..7d625cf 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import fixtures
import time
from oslo_config import cfg
@@ -19,8 +20,37 @@
from tempest.common.utils.linux import remote_client
from tempest import config
+from tempest.lib import exceptions as lib_exc
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
+
+
+SERVER = {
+ 'id': 'server_uuid',
+ 'name': 'fake_server',
+ 'status': 'ACTIVE'
+}
+
+BROKEN_SERVER = {
+ 'id': 'broken_server_uuid',
+ 'name': 'broken_server',
+ 'status': 'ERROR'
+}
+
+
+class FakeServersClient(object):
+
+ CONSOLE_OUTPUT = "Console output for %s"
+
+ def get_console_output(self, server_id):
+ status = 'ERROR'
+ for s in SERVER, BROKEN_SERVER:
+ if s['id'] == server_id:
+ status = s['status']
+ if status == 'ERROR':
+ raise lib_exc.BadRequest('Server in ERROR state')
+ else:
+ return dict(output=self.CONSOLE_OUTPUT % server_id)
class TestRemoteClient(base.TestCase):
@@ -155,3 +185,78 @@
self.conn.set_nic_state(nic, "down")
self._assert_exec_called_with(
'sudo ip link set %s down' % nic)
+
+
+class TestRemoteClientWithServer(base.TestCase):
+
+ server = SERVER
+
+ def setUp(self):
+ super(TestRemoteClientWithServer, self).setUp()
+ self.useFixture(fake_config.ConfigFixture())
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
+ cfg.CONF.set_default('ip_version_for_ssh', 4, group='validation')
+ cfg.CONF.set_default('network_for_ssh', 'public',
+ group='validation')
+ cfg.CONF.set_default('connect_timeout', 1, group='validation')
+ cfg.CONF.set_default('console_output', True,
+ group='compute-feature-enabled')
+
+ self.conn = remote_client.RemoteClient(
+ '127.0.0.1', 'user', 'pass',
+ server=self.server, servers_client=FakeServersClient())
+ self.useFixture(fixtures.MockPatch(
+ 'tempest.lib.common.ssh.Client._get_ssh_connection',
+ side_effect=lib_exc.SSHTimeout(host='127.0.0.1',
+ user='user',
+ password='pass')))
+ self.log = self.useFixture(fixtures.FakeLogger(
+ name='tempest.common.utils.linux.remote_client',
+ level='DEBUG'))
+
+ def test_validate_debug_ssh_console(self):
+ self.assertRaises(lib_exc.SSHTimeout,
+ self.conn.validate_authentication)
+ msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+ 'TestRemoteClientWithServer:test_validate_debug_ssh_console',
+ self.server)
+ self.assertIn(msg, self.log.output)
+ self.assertIn('Console output for', self.log.output)
+
+ def test_exec_command_debug_ssh_console(self):
+ self.assertRaises(lib_exc.SSHTimeout,
+ self.conn.exec_command, 'fake command')
+ self.assertIn('fake command', self.log.output)
+ msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+ 'TestRemoteClientWithServer:test_exec_command_debug_ssh_console',
+ self.server)
+ self.assertIn(msg, self.log.output)
+ self.assertIn('Console output for', self.log.output)
+
+
+class TestRemoteClientWithBrokenServer(TestRemoteClientWithServer):
+
+ server = BROKEN_SERVER
+
+ def test_validate_debug_ssh_console(self):
+ self.assertRaises(lib_exc.SSHTimeout,
+ self.conn.validate_authentication)
+ msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+ 'TestRemoteClientWithBrokenServer:test_validate_debug_ssh_console',
+ self.server)
+ self.assertIn(msg, self.log.output)
+ msg = 'Could not get console_log for server %s' % self.server['id']
+ self.assertIn(msg, self.log.output)
+
+ def test_exec_command_debug_ssh_console(self):
+ self.assertRaises(lib_exc.SSHTimeout,
+ self.conn.exec_command, 'fake command')
+ self.assertIn('fake command', self.log.output)
+ caller = ":".join(['TestRemoteClientWithBrokenServer',
+ 'test_exec_command_debug_ssh_console'])
+ msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+ caller, self.server)
+ self.assertIn(msg, self.log.output)
+ msg = 'Could not get console_log for server %s' % self.server['id']
+ self.assertIn(msg, self.log.output)
diff --git a/tempest/tests/common/utils/test_file_utils.py b/tempest/tests/common/utils/test_file_utils.py
index 1a14592..937aefa 100644
--- a/tempest/tests/common/utils/test_file_utils.py
+++ b/tempest/tests/common/utils/test_file_utils.py
@@ -16,7 +16,7 @@
import mock
from tempest.common.utils import file_utils
-from tempest.tests.lib import base
+from tempest.tests import base
class TestFileUtils(base.TestCase):
diff --git a/tempest/tests/fake_auth_provider.py b/tempest/tests/fake_auth_provider.py
index bc68d26..769f6a6 100644
--- a/tempest/tests/fake_auth_provider.py
+++ b/tempest/tests/fake_auth_provider.py
@@ -18,3 +18,9 @@
def auth_request(self, method, url, headers=None, body=None, filters=None):
return url, headers, body
+
+ def get_token(self):
+ return "faketoken"
+
+ def base_url(self, filters, auth_data=None):
+ return "https://example.com"
diff --git a/tempest/tests/lib/cli/test_command_failed.py b/tempest/tests/lib/cli/test_command_failed.py
index 8ce34c2..388028a 100644
--- a/tempest/tests/lib/cli/test_command_failed.py
+++ b/tempest/tests/lib/cli/test_command_failed.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class TestOutputParser(base.TestCase):
diff --git a/tempest/tests/lib/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
index b5f7145..b846c46 100644
--- a/tempest/tests/lib/cli/test_execute.py
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -14,7 +14,7 @@
from tempest.lib.cli import base as cli_base
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class TestExecute(base.TestCase):
diff --git a/tempest/tests/lib/cli/test_output_parser.py b/tempest/tests/lib/cli/test_output_parser.py
index a2c1b2d..d88dfc3 100644
--- a/tempest/tests/lib/cli/test_output_parser.py
+++ b/tempest/tests/lib/cli/test_output_parser.py
@@ -16,7 +16,7 @@
from tempest.lib.cli import output_parser
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class TestOutputParser(base.TestCase):
diff --git a/tempest/tests/lib/common/test_api_version_request.py b/tempest/tests/lib/common/test_api_version_request.py
index bdaa936..58e7040 100644
--- a/tempest/tests/lib/common/test_api_version_request.py
+++ b/tempest/tests/lib/common/test_api_version_request.py
@@ -14,7 +14,7 @@
from tempest.lib.common import api_version_request
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class APIVersionRequestTests(base.TestCase):
diff --git a/tempest/tests/lib/common/test_api_version_utils.py b/tempest/tests/lib/common/test_api_version_utils.py
index 591b87e..6206379 100644
--- a/tempest/tests/lib/common/test_api_version_utils.py
+++ b/tempest/tests/lib/common/test_api_version_utils.py
@@ -16,7 +16,7 @@
from tempest.lib.common import api_version_utils
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class TestVersionSkipLogic(base.TestCase):
diff --git a/tempest/tests/lib/common/utils/test_data_utils.py b/tempest/tests/lib/common/utils/test_data_utils.py
index 493df89..f9e1f44 100644
--- a/tempest/tests/lib/common/utils/test_data_utils.py
+++ b/tempest/tests/lib/common/utils/test_data_utils.py
@@ -16,7 +16,7 @@
import netaddr
from tempest.lib.common.utils import data_utils
-from tempest.tests.lib import base
+from tempest.tests import base
class TestDataUtils(base.TestCase):
@@ -93,6 +93,15 @@
actual2 = data_utils.rand_int_id()
self.assertNotEqual(actual, actual2)
+ def test_rand_infiniband_guid_address(self):
+ actual = data_utils.rand_infiniband_guid_address()
+ self.assertIsInstance(actual, str)
+ self.assertRegex(actual, "^([0-9a-f][0-9a-f]:){7}"
+ "[0-9a-f][0-9a-f]$")
+
+ actual2 = data_utils.rand_infiniband_guid_address()
+ self.assertNotEqual(actual, actual2)
+
def test_rand_mac_address(self):
actual = data_utils.rand_mac_address()
self.assertIsInstance(actual, str)
diff --git a/tempest/tests/lib/common/utils/test_misc.py b/tempest/tests/lib/common/utils/test_misc.py
index e23d7fb..9597f5b 100644
--- a/tempest/tests/lib/common/utils/test_misc.py
+++ b/tempest/tests/lib/common/utils/test_misc.py
@@ -15,7 +15,7 @@
from tempest.lib.common.utils import misc
-from tempest.tests.lib import base
+from tempest.tests import base
@misc.singleton
diff --git a/tempest/tests/lib/services/compute/base.py b/tempest/tests/lib/services/compute/base.py
index c805de2..e77b436 100644
--- a/tempest/tests/lib/services/compute/base.py
+++ b/tempest/tests/lib/services/compute/base.py
@@ -15,7 +15,7 @@
from oslo_serialization import jsonutils as json
from oslotest import mockpatch
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_http
diff --git a/tempest/tests/lib/services/identity/v2/test_token_client.py b/tempest/tests/lib/services/identity/v2/test_token_client.py
index d91ecdc..7925152 100644
--- a/tempest/tests/lib/services/identity/v2/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -19,7 +19,7 @@
from tempest.lib.common import rest_client
from tempest.lib import exceptions
from tempest.lib.services.identity.v2 import token_client
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_http
diff --git a/tempest/tests/lib/services/identity/v3/test_token_client.py b/tempest/tests/lib/services/identity/v3/test_token_client.py
index 52b8e01..e9ef740 100644
--- a/tempest/tests/lib/services/identity/v3/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_token_client.py
@@ -19,7 +19,7 @@
from tempest.lib.common import rest_client
from tempest.lib import exceptions
from tempest.lib.services.identity.v3 import token_client
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_http
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index 50ad566..cc71c92 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -22,7 +22,7 @@
from tempest.lib import exceptions
from tempest.lib.services.identity.v2 import token_client as v2_client
from tempest.lib.services.identity.v3 import token_client as v3_client
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_credentials
from tempest.tests.lib import fake_identity
diff --git a/tempest/tests/lib/test_credentials.py b/tempest/tests/lib/test_credentials.py
index b8acf83..ca3baa1 100644
--- a/tempest/tests/lib/test_credentials.py
+++ b/tempest/tests/lib/test_credentials.py
@@ -19,7 +19,7 @@
from tempest.lib import exceptions
from tempest.lib.services.identity.v2 import token_client as v2_client
from tempest.lib.services.identity.v3 import token_client as v3_client
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_identity
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index f83bcbb..f3a4e9c 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -18,7 +18,7 @@
from tempest.lib import base as test
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest.tests.lib import base
+from tempest.tests import base
class TestSkipBecauseDecorator(base.TestCase):
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 572b446..2959294 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -22,7 +22,7 @@
from tempest.lib.common import http
from tempest.lib.common import rest_client
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib import fake_http
import tempest.tests.utils as utils
diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py
index d001c51..b07f6bc 100644
--- a/tempest/tests/lib/test_ssh.py
+++ b/tempest/tests/lib/test_ssh.py
@@ -21,7 +21,7 @@
from tempest.lib.common import ssh
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
import tempest.tests.utils as utils
diff --git a/tempest/tests/lib/test_tempest_lib.py b/tempest/tests/lib/test_tempest_lib.py
index 9731e96..d70e53d 100644
--- a/tempest/tests/lib/test_tempest_lib.py
+++ b/tempest/tests/lib/test_tempest_lib.py
@@ -19,7 +19,7 @@
Tests for `tempest.lib` module.
"""
-from tempest.tests.lib import base
+from tempest.tests import base
class TestTempest_lib(base.TestCase):
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
index 1795466..44ce567 100644
--- a/tempest/tests/negative/test_negative_auto_test.py
+++ b/tempest/tests/negative/test_negative_auto_test.py
@@ -15,8 +15,8 @@
from tempest import config
import tempest.test as test
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
class TestNegativeAutoTest(base.TestCase):
diff --git a/tempest/tests/negative/test_negative_generators.py b/tempest/tests/negative/test_negative_generators.py
index e0d7f42..78fd80d 100644
--- a/tempest/tests/negative/test_negative_generators.py
+++ b/tempest/tests/negative/test_negative_generators.py
@@ -22,7 +22,7 @@
from tempest.common.generator import base_generator
from tempest.common.generator import negative_generator
from tempest.common.generator import valid_generator
-from tempest.tests.lib import base
+from tempest.tests import base
class TestNegativeBasicGenerator(base.TestCase):
diff --git a/tempest/tests/services/object_storage/__init__.py b/tempest/tests/services/object_storage/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/services/object_storage/__init__.py
diff --git a/tempest/tests/services/object_storage/test_object_client.py b/tempest/tests/services/object_storage/test_object_client.py
new file mode 100644
index 0000000..cd8c8f1
--- /dev/null
+++ b/tempest/tests/services/object_storage/test_object_client.py
@@ -0,0 +1,109 @@
+# Copyright 2016 IBM Corp.
+# 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 mock
+import six
+
+from tempest.lib import exceptions
+from tempest.services.object_storage import object_client
+from tempest.tests import base
+from tempest.tests import fake_auth_provider
+
+
+class TestObjectClient(base.TestCase):
+
+ def setUp(self):
+ super(TestObjectClient, self).setUp()
+ self.fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.url = self.fake_auth.base_url(None)
+ self.object_client = object_client.ObjectClient(self.fake_auth,
+ 'swift', 'region1')
+
+ @mock.patch.object(object_client, 'create_connection')
+ def test_create_object_continue_no_data(self, mock_poc):
+ self._validate_create_object_continue(None, mock_poc)
+
+ @mock.patch.object(object_client, 'create_connection')
+ def test_create_object_continue_with_data(self, mock_poc):
+ self._validate_create_object_continue('hello', mock_poc)
+
+ @mock.patch.object(object_client, 'create_connection')
+ def test_create_continue_with_no_continue_received(self, mock_poc):
+ self._validate_create_object_continue('hello', mock_poc,
+ initial_status=201)
+
+ def _validate_create_object_continue(self, req_data,
+ mock_poc, initial_status=100):
+
+ expected_hdrs = {
+ 'X-Auth-Token': self.fake_auth.get_token(),
+ 'content-length': 0 if req_data is None else len(req_data),
+ 'Expect': '100-continue'}
+
+ # Setup the Mocks prior to invoking the object creation
+ mock_resp_cls = mock.Mock()
+ mock_resp_cls._read_status.return_value = ("1", initial_status, "OK")
+
+ mock_poc.return_value.response_class.return_value = mock_resp_cls
+
+ # This is the final expected return value
+ mock_poc.return_value.getresponse.return_value.status = 201
+ mock_poc.return_value.getresponse.return_value.reason = 'OK'
+
+ # Call method to PUT object using expect:100-continue
+ cnt = "container1"
+ obj = "object1"
+ path = "/%s/%s" % (cnt, obj)
+
+ # If the expected initial status is not 100, then an exception
+ # should be thrown and the connection closed
+ if initial_status is 100:
+ status, reason = \
+ self.object_client.create_object_continue(cnt, obj, req_data)
+ else:
+ self.assertRaises(exceptions.UnexpectedResponseCode,
+ self.object_client.create_object_continue, cnt,
+ obj, req_data)
+ mock_poc.return_value.close.assert_called_once_with()
+
+ # Verify that putrequest is called 1 time with the appropriate values
+ mock_poc.return_value.putrequest.assert_called_once_with('PUT', path)
+
+ # Verify that headers were written, including "Expect:100-continue"
+ calls = []
+
+ for header, value in six.iteritems(expected_hdrs):
+ calls.append(mock.call(header, value))
+
+ mock_poc.return_value.putheader.assert_has_calls(calls, False)
+ mock_poc.return_value.endheaders.assert_called_once_with()
+
+ # The following steps are only taken if the initial status is 100
+ if initial_status is 100:
+ # Verify that the method returned what it was supposed to
+ self.assertEqual(status, 201)
+
+ # Verify that _safe_read was called once to remove the CRLF
+ # after the 100 response
+ mock_rc = mock_poc.return_value.response_class.return_value
+ mock_rc._safe_read.assert_called_once_with(2)
+
+ # Verify the actual data was written via send
+ mock_poc.return_value.send.assert_called_once_with(req_data)
+
+ # Verify that the getresponse method was called to receive
+ # the final
+ mock_poc.return_value.getresponse.assert_called_once_with()
diff --git a/tempest/tests/stress/test_stress.py b/tempest/tests/stress/test_stress.py
index a35b4d7..dfe0291 100644
--- a/tempest/tests/stress/test_stress.py
+++ b/tempest/tests/stress/test_stress.py
@@ -18,7 +18,7 @@
from oslo_log import log as logging
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
LOG = logging.getLogger(__name__)
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index 9ffb7a1..dc355b4 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -19,8 +19,8 @@
from tempest.common import fixed_network
from tempest import config
from tempest import test
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
class TestBaseTestCase(base.TestCase):
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index ee68676..8c5d861 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -21,8 +21,8 @@
from tempest import exceptions
from tempest.lib.common.utils import data_utils
from tempest import test
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
class BaseDecoratorsTest(base.TestCase):
@@ -246,3 +246,96 @@
self.assertIn("test_fake_negative", dir(obj))
obj.test_fake_negative()
mock.assert_called_once_with(self.FakeNegativeJSONTest._schema)
+
+
+class TestConfigDecorators(BaseDecoratorsTest):
+ def setUp(self):
+ super(TestConfigDecorators, self).setUp()
+ cfg.CONF.set_default('nova', True, 'service_available')
+ cfg.CONF.set_default('glance', False, 'service_available')
+
+ def _assert_skip_message(self, func, skip_msg):
+ try:
+ func()
+ self.fail()
+ except testtools.TestCase.skipException as skip_exc:
+ self.assertEqual(skip_exc.args[0], skip_msg)
+
+ def _test_skip_unless_config(self, expected_to_skip=True, *decorator_args):
+
+ class TestFoo(test.BaseTestCase):
+ @config.skip_unless_config(*decorator_args)
+ def test_bar(self):
+ return 0
+
+ t = TestFoo('test_bar')
+ if expected_to_skip:
+ self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ if (len(decorator_args) >= 3):
+ # decorator_args[2]: skip message specified
+ self._assert_skip_message(t.test_bar, decorator_args[2])
+ else:
+ try:
+ self.assertEqual(t.test_bar(), 0)
+ except testtools.TestCase.skipException:
+ # We caught a skipException but we didn't expect to skip
+ # this test so raise a hard test failure instead.
+ raise testtools.TestCase.failureException(
+ "Not supposed to skip")
+
+ def _test_skip_if_config(self, expected_to_skip=True,
+ *decorator_args):
+
+ class TestFoo(test.BaseTestCase):
+ @config.skip_if_config(*decorator_args)
+ def test_bar(self):
+ return 0
+
+ t = TestFoo('test_bar')
+ if expected_to_skip:
+ self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ if (len(decorator_args) >= 3):
+ # decorator_args[2]: skip message specified
+ self._assert_skip_message(t.test_bar, decorator_args[2])
+ else:
+ try:
+ self.assertEqual(t.test_bar(), 0)
+ except testtools.TestCase.skipException:
+ # We caught a skipException but we didn't expect to skip
+ # this test so raise a hard test failure instead.
+ raise testtools.TestCase.failureException(
+ "Not supposed to skip")
+
+ def test_skip_unless_no_group(self):
+ self._test_skip_unless_config(True, 'fake_group', 'an_option')
+
+ def test_skip_unless_no_option(self):
+ self._test_skip_unless_config(True, 'service_available',
+ 'not_an_option')
+
+ def test_skip_unless_false_option(self):
+ self._test_skip_unless_config(True, 'service_available', 'glance')
+
+ def test_skip_unless_false_option_msg(self):
+ self._test_skip_unless_config(True, 'service_available', 'glance',
+ 'skip message')
+
+ def test_skip_unless_true_option(self):
+ self._test_skip_unless_config(False,
+ 'service_available', 'nova')
+
+ def test_skip_if_no_group(self):
+ self._test_skip_if_config(False, 'fake_group', 'an_option')
+
+ def test_skip_if_no_option(self):
+ self._test_skip_if_config(False, 'service_available', 'not_an_option')
+
+ def test_skip_if_false_option(self):
+ self._test_skip_if_config(False, 'service_available', 'glance')
+
+ def test_skip_if_true_option(self):
+ self._test_skip_if_config(True, 'service_available', 'nova')
+
+ def test_skip_if_true_option_msg(self):
+ self._test_skip_if_config(True, 'service_available', 'nova',
+ 'skip message')
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index fdbc2d2..768cd05 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -22,8 +22,8 @@
from tempest.common import glance_http
from tempest import exceptions
+from tempest.tests import base
from tempest.tests import fake_auth_provider
-from tempest.tests.lib import base
from tempest.tests.lib import fake_http
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index 6b3aa0d..aba2aab 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -13,7 +13,7 @@
# under the License.
from tempest.hacking import checks
-from tempest.tests.lib import base
+from tempest.tests import base
class HackingTestCase(base.TestCase):
diff --git a/tempest/tests/test_list_tests.py b/tempest/tests/test_list_tests.py
index 69527b1..38d4c5c 100644
--- a/tempest/tests/test_list_tests.py
+++ b/tempest/tests/test_list_tests.py
@@ -17,7 +17,7 @@
import six
import subprocess
-from tempest.tests.lib import base
+from tempest.tests import base
class TestTestList(base.TestCase):
diff --git a/tempest/tests/test_microversions.py b/tempest/tests/test_microversions.py
index b80cf80..173accb 100644
--- a/tempest/tests/test_microversions.py
+++ b/tempest/tests/test_microversions.py
@@ -18,8 +18,8 @@
from tempest.api.compute import base as compute_base
from tempest import config
from tempest.lib import exceptions
+from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import base
class VersionTestNoneTolatest(compute_base.BaseV2ComputeTest):
diff --git a/tempest/tests/test_negative_rest_client.py b/tempest/tests/test_negative_rest_client.py
index e46649c..9d9c20f 100644
--- a/tempest/tests/test_negative_rest_client.py
+++ b/tempest/tests/test_negative_rest_client.py
@@ -20,9 +20,9 @@
from tempest.common import negative_rest_client
from tempest import config
+from tempest.tests import base
from tempest.tests import fake_auth_provider
from tempest.tests import fake_config
-from tempest.tests.lib import base
class TestNegativeRestClient(base.TestCase):
diff --git a/tempest/tests/test_tempest_plugin.py b/tempest/tests/test_tempest_plugin.py
index f66dfc8..c07e98c 100644
--- a/tempest/tests/test_tempest_plugin.py
+++ b/tempest/tests/test_tempest_plugin.py
@@ -14,8 +14,8 @@
# under the License.
from tempest.test_discover import plugins
+from tempest.tests import base
from tempest.tests import fake_tempest_plugin as fake_plugin
-from tempest.tests.lib import base
class TestPluginDiscovery(base.TestCase):
diff --git a/tempest/tests/test_wrappers.py b/tempest/tests/test_wrappers.py
index edb9061..a4ef699 100644
--- a/tempest/tests/test_wrappers.py
+++ b/tempest/tests/test_wrappers.py
@@ -19,7 +19,7 @@
import six
-from tempest.tests.lib import base
+from tempest.tests import base
DEVNULL = open(os.devnull, 'wb')
diff --git a/tox.ini b/tox.ini
index e371406..44162fd 100644
--- a/tox.ini
+++ b/tox.ini
@@ -35,6 +35,7 @@
commands = python setup.py testr --coverage --testr-arg='tempest\.tests {posargs}'
[testenv:all]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
# 'all' includes slow tests
setenv =
@@ -68,6 +69,7 @@
bash tools/pretty_tox.sh '{posargs}'
[testenv:full]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
@@ -78,6 +80,7 @@
bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
[testenv:full-serial]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
@@ -88,6 +91,7 @@
bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
[testenv:smoke]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
@@ -96,6 +100,7 @@
bash tools/pretty_tox.sh '\[.*\bsmoke\b.*\] {posargs}'
[testenv:smoke-serial]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
@@ -107,6 +112,7 @@
bash tools/pretty_tox_serial.sh '\[.*\bsmoke\b.*\] {posargs}'
[testenv:stress]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
@@ -116,6 +122,13 @@
[testenv:venv]
commands = {posargs}
+[testenv:venv-tempest]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+commands = {posargs}
+
[testenv:docs]
commands =
python setup.py build_sphinx {posargs}