Merge "tempest-cleanup can only be invoked as tempest cleanup"
diff --git a/README.rst b/README.rst
index 3c0463b..f1dac1c 100644
--- a/README.rst
+++ b/README.rst
@@ -92,18 +92,18 @@
be done using the :ref:`tempest_run` command. This can be done by either
running::
- $ tempest run
+ $ tempest run
from the Tempest workspace directory. Or you can use the ``--workspace``
argument to run in the workspace you created regarless of your current
working directory. For example::
- $ tempest run --workspace cloud-01
+ $ tempest run --workspace cloud-01
There is also the option to use testr directly, or any `testr`_ based test
runner, like `ostestr`_. For example, from the workspace dir run::
- $ ostestr --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))'
+ $ ostestr --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))'
will run the same set of tests as the default gate jobs.
@@ -161,9 +161,9 @@
of the configuration.
You can generate a new sample tempest.conf file, run the following
-command from the top level of the Tempest directory:
+command from the top level of the Tempest directory::
- tox -egenconfig
+ $ tox -egenconfig
The most important pieces that are needed are the user ids, openstack
endpoint, and basic flavors and images needed to run tests.
@@ -257,11 +257,11 @@
and run the tests or use tox to do the same. Tox also contains several existing
job configurations. For example::
- $ tox -efull
+ $ tox -efull
which will run the same set of tests as the OpenStack gate. (it's exactly how
the gate invokes Tempest) Or::
- $ tox -esmoke
+ $ tox -esmoke
to run the tests tagged as smoke.
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index fd9ad05..18269bf 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -95,7 +95,7 @@
accounts will be assigned a role on domain configured in
``default_credentials_domain_name``. This will make the accounts provisioned
usable in a cloud where domain scoped tokens are required by keystone for
-admin operations. Note that the the initial pre-provision admin accounts,
+admin operations. Note that the initial pre-provision admin accounts,
configured in tempest.conf, must have a role on the same domain as well, for
Dynamic Credentials to work.
@@ -151,7 +151,7 @@
``admin_domain_scope`` as ``default_credentials_domain_name`` are configured
properly in tempest.conf.
-Pre-Provisioned Credentials are also know as accounts.yaml or accounts file.
+Pre-Provisioned Credentials are also known as accounts.yaml or accounts file.
Compute
-------
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index d34023f..285ad5d 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -213,7 +213,7 @@
* **client_names**: Name of the classes that implement service clients in the
service clients module.
-Example usage of the the service clients in tests::
+Example usage of the service clients in tests::
# my_creds is instance of tempest.lib.auth.Credentials
# identity_uri is v2 or v3 depending on the configuration
diff --git a/releasenotes/notes/Tempest-library-interface-0eb680b810139a50.yaml b/releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
similarity index 100%
rename from releasenotes/notes/Tempest-library-interface-0eb680b810139a50.yaml
rename to releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
diff --git a/releasenotes/notes/start-using-reno-ed9518126fd0e1a3.yaml b/releasenotes/notes/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
similarity index 100%
rename from releasenotes/notes/start-using-reno-ed9518126fd0e1a3.yaml
rename to releasenotes/notes/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
diff --git a/releasenotes/notes/api-microversion-testing-support-2ceddd2255670932.yaml b/releasenotes/notes/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml
similarity index 100%
rename from releasenotes/notes/api-microversion-testing-support-2ceddd2255670932.yaml
rename to releasenotes/notes/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml
diff --git a/releasenotes/notes/compute-microversion-support-e0b23f960f894b9b.yaml b/releasenotes/notes/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
similarity index 100%
rename from releasenotes/notes/compute-microversion-support-e0b23f960f894b9b.yaml
rename to releasenotes/notes/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
diff --git a/releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml b/releasenotes/notes/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
similarity index 100%
rename from releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml
rename to releasenotes/notes/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
diff --git a/releasenotes/notes/add-scope-to-auth-b5a82493ea89f41e.yaml b/releasenotes/notes/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
similarity index 100%
rename from releasenotes/notes/add-scope-to-auth-b5a82493ea89f41e.yaml
rename to releasenotes/notes/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
diff --git a/releasenotes/notes/add-tempest-run-3d0aaf69c2ca4115.yaml b/releasenotes/notes/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
similarity index 100%
rename from releasenotes/notes/add-tempest-run-3d0aaf69c2ca4115.yaml
rename to releasenotes/notes/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
diff --git a/releasenotes/notes/add-tempest-workspaces-228a2ba4690b5589.yaml b/releasenotes/notes/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
similarity index 100%
rename from releasenotes/notes/add-tempest-workspaces-228a2ba4690b5589.yaml
rename to releasenotes/notes/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
diff --git a/releasenotes/notes/add_subunit_describe_calls-5498a37e6cd66c4b.yaml b/releasenotes/notes/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
similarity index 100%
rename from releasenotes/notes/add_subunit_describe_calls-5498a37e6cd66c4b.yaml
rename to releasenotes/notes/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
diff --git a/releasenotes/notes/bug-1486834-7ebca15836ae27a9.yaml b/releasenotes/notes/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
similarity index 100%
rename from releasenotes/notes/bug-1486834-7ebca15836ae27a9.yaml
rename to releasenotes/notes/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
diff --git a/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml b/releasenotes/notes/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
similarity index 100%
rename from releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml
rename to releasenotes/notes/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
diff --git a/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml b/releasenotes/notes/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
similarity index 100%
rename from releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
rename to releasenotes/notes/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
diff --git a/releasenotes/notes/new-test-utils-module-adf34468c4d52719.yaml b/releasenotes/notes/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
similarity index 100%
rename from releasenotes/notes/new-test-utils-module-adf34468c4d52719.yaml
rename to releasenotes/notes/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
diff --git a/releasenotes/notes/remove-input-scenarios-functionality-01308e6d4307f580.yaml b/releasenotes/notes/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
similarity index 100%
rename from releasenotes/notes/remove-input-scenarios-functionality-01308e6d4307f580.yaml
rename to releasenotes/notes/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
diff --git a/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml b/releasenotes/notes/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
similarity index 100%
rename from releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml
rename to releasenotes/notes/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
diff --git a/releasenotes/notes/remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml b/releasenotes/notes/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
similarity index 100%
rename from releasenotes/notes/remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
rename to releasenotes/notes/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
diff --git a/releasenotes/notes/remove-trove-tests-666522e9113549f9.yaml b/releasenotes/notes/12.1.0-remove-trove-tests-666522e9113549f9.yaml
similarity index 100%
rename from releasenotes/notes/remove-trove-tests-666522e9113549f9.yaml
rename to releasenotes/notes/12.1.0-remove-trove-tests-666522e9113549f9.yaml
diff --git a/releasenotes/notes/routers-client-as-library-25a363379da351f6.yaml b/releasenotes/notes/12.1.0-routers-client-as-library-25a363379da351f6.yaml
similarity index 100%
rename from releasenotes/notes/routers-client-as-library-25a363379da351f6.yaml
rename to releasenotes/notes/12.1.0-routers-client-as-library-25a363379da351f6.yaml
diff --git a/releasenotes/notes/support-chunked-encoding-d71f53225f68edf3.yaml b/releasenotes/notes/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
similarity index 100%
rename from releasenotes/notes/support-chunked-encoding-d71f53225f68edf3.yaml
rename to releasenotes/notes/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
diff --git a/releasenotes/notes/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml b/releasenotes/notes/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
similarity index 100%
rename from releasenotes/notes/tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
rename to releasenotes/notes/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
diff --git a/releasenotes/notes/add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml b/releasenotes/notes/add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
new file mode 100644
index 0000000..1ef2b0d
--- /dev/null
+++ b/releasenotes/notes/add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
@@ -0,0 +1,14 @@
+---
+features:
+ - |
+ Define volume service clients as libraries.
+ The following volume service clients are defined as library interface,
+ so the other projects can use these modules as stable libraries without
+ any maintenance changes.
+
+ * encryption_types_client (v1)
+ * qos_clients (v1)
+ * qos_clients (v2)
+ * snapshots_client (v1)
+ * snapshots_client (v2)
+
diff --git a/releasenotes/notes/deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml b/releasenotes/notes/deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
new file mode 100644
index 0000000..0884cfa
--- /dev/null
+++ b/releasenotes/notes/deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
@@ -0,0 +1,4 @@
+---
+deprecations:
+ - Oslo.utils provides same method get_ipv6_addr_by_EUI64,
+ so deprecate it in Newton and remove it in Ocata.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 2c22408..0ec0e94 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -5,10 +5,10 @@
.. toctree::
:maxdepth: 1
+ unreleased
v12.0.0
v11.0.0
v10.0.0
- unreleased
Indices and tables
==================
diff --git a/requirements.txt b/requirements.txt
index a773d16..4655b9f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,20 +6,21 @@
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
testtools>=1.4.0 # MIT
paramiko>=2.0 # LGPLv2.1+
-netaddr!=0.7.16,>=0.7.12 # BSD
+netaddr!=0.7.16,>=0.7.13 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
oslo.concurrency>=3.8.0 # Apache-2.0
oslo.config>=3.14.0 # Apache-2.0
-oslo.i18n>=2.1.0 # Apache-2.0
-oslo.log>=1.14.0 # Apache-2.0
+oslo.log>=3.11.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.16.0 # Apache-2.0
six>=1.9.0 # MIT
fixtures>=3.0.0 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
PyYAML>=3.1.0 # MIT
+python-subunit>=0.0.18 # Apache-2.0/BSD
stevedore>=1.16.0 # Apache-2.0
PrettyTable<0.8,>=0.7 # BSD
os-testr>=0.7.0 # Apache-2.0
urllib3>=1.15.1 # MIT
debtcollector>=1.2.0 # Apache-2.0
+unittest2 # BSD
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 4f48ad0..61359f1 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -16,7 +16,6 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
from tempest import test
LOG = log.getLogger(__name__)
@@ -30,24 +29,16 @@
super(AgentsAdminTestJSON, cls).setup_clients()
cls.client = cls.os_adm.agents_client
- def setUp(self):
- super(AgentsAdminTestJSON, self).setUp()
- params = self._param_helper(
+ @classmethod
+ def resource_setup(cls):
+ super(AgentsAdminTestJSON, cls).resource_setup()
+ cls.params_agent = cls._param_helper(
hypervisor='common', os='linux', architecture='x86_64',
version='7.0', url='xxx://xxxx/xxx/xxx',
md5hash='add6bb58e139be103324d04d82d8f545')
- body = self.client.create_agent(**params)['agent']
- self.agent_id = body['agent_id']
- def tearDown(self):
- try:
- test_utils.call_and_ignore_notfound_exc(
- self.client.delete_agent, self.agent_id)
- except Exception:
- LOG.exception('Exception raised deleting agent %s', self.agent_id)
- super(AgentsAdminTestJSON, self).tearDown()
-
- def _param_helper(self, **kwargs):
+ @staticmethod
+ def _param_helper(**kwargs):
rand_key = 'architecture'
if rand_key in kwargs:
# NOTE: The rand_name is for avoiding agent conflicts.
@@ -71,33 +62,42 @@
@test.idempotent_id('dc9ffd51-1c50-4f0e-a820-ae6d2a568a9e')
def test_update_agent(self):
- # Update an agent.
+ # Create and update an agent.
+ body = self.client.create_agent(**self.params_agent)['agent']
+ self.addCleanup(self.client.delete_agent, body['agent_id'])
+ agent_id = body['agent_id']
params = self._param_helper(
version='8.0', url='xxx://xxxx/xxx/xxx2',
md5hash='add6bb58e139be103324d04d82d8f547')
- body = self.client.update_agent(self.agent_id, **params)['agent']
+ body = self.client.update_agent(agent_id, **params)['agent']
for expected_item, value in params.items():
self.assertEqual(value, body[expected_item])
@test.idempotent_id('470e0b89-386f-407b-91fd-819737d0b335')
def test_delete_agent(self):
- # Delete an agent.
- self.client.delete_agent(self.agent_id)
+ # Create an agent and delete it.
+ body = self.client.create_agent(**self.params_agent)['agent']
+ self.client.delete_agent(body['agent_id'])
# Verify the list doesn't contain the deleted agent.
agents = self.client.list_agents()['agents']
- self.assertNotIn(self.agent_id, map(lambda x: x['agent_id'], agents))
+ self.assertNotIn(body['agent_id'], map(lambda x: x['agent_id'],
+ agents))
@test.idempotent_id('6a326c69-654b-438a-80a3-34bcc454e138')
def test_list_agents(self):
- # List all agents.
+ # Create an agent and list all agents.
+ body = self.client.create_agent(**self.params_agent)['agent']
+ self.addCleanup(self.client.delete_agent, body['agent_id'])
agents = self.client.list_agents()['agents']
self.assertTrue(len(agents) > 0, 'Cannot get any agents.(%s)' % agents)
- self.assertIn(self.agent_id, map(lambda x: x['agent_id'], agents))
+ self.assertIn(body['agent_id'], map(lambda x: x['agent_id'], agents))
@test.idempotent_id('eabadde4-3cd7-4ec4-a4b5-5a936d2d4408')
def test_list_agents_with_filter(self):
- # List the agent builds by the filter.
+ # Create agents and list the agent builds by the filter.
+ body = self.client.create_agent(**self.params_agent)['agent']
+ self.addCleanup(self.client.delete_agent, body['agent_id'])
params = self._param_helper(
hypervisor='xen', os='linux', architecture='x86',
version='7.0', url='xxx://xxxx/xxx/xxx1',
@@ -110,4 +110,5 @@
['agents'])
self.assertTrue(len(agents) > 0, 'Cannot get any agents.(%s)' % agents)
self.assertIn(agent_id_xen, map(lambda x: x['agent_id'], agents))
- self.assertNotIn(self.agent_id, map(lambda x: x['agent_id'], agents))
+ self.assertNotIn(body['agent_id'], map(lambda x: x['agent_id'],
+ agents))
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 95e7ef1..fde5622 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -161,6 +161,7 @@
verify_flavor_response_extension(flavor)
# Check if flavor is present in list
+ flag = False
flavors = self.user_client.list_flavors(detail=True)['flavors']
for flavor in flavors:
if flavor['name'] == flavor_name:
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index 6113c04..62dbfe4 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -31,6 +31,8 @@
super(MigrationsAdminTest, cls).setup_clients()
cls.client = cls.os_adm.migrations_client
cls.flavors_admin_client = cls.os_adm.flavors_client
+ cls.admin_hosts_client = cls.os_adm.hosts_client
+ cls.admin_servers_client = cls.os_adm.servers_client
@test.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
def test_list_migrations(self):
@@ -103,3 +105,28 @@
server = self.servers_client.show_server(server['id'])['server']
self.assertEqual(flavor['id'], server['flavor']['id'])
+
+ @test.idempotent_id('4bf0be52-3b6f-4746-9a27-3143636fe30d')
+ @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+ 'Cold migration not available.')
+ def test_cold_migration(self):
+ if CONF.compute.min_compute_nodes < 2:
+ msg = "Less than 2 compute nodes, skipping multinode tests."
+ raise self.skipException(msg)
+
+ server = self.create_test_server(wait_until="ACTIVE")
+ src_host = self.admin_servers_client.show_server(
+ server['id'])['server']['OS-EXT-SRV-ATTR:host']
+
+ self.admin_servers_client.migrate_server(server['id'])
+
+ waiters.wait_for_server_status(self.servers_client,
+ server['id'], 'VERIFY_RESIZE')
+
+ self.servers_client.confirm_resize_server(server['id'])
+ waiters.wait_for_server_status(self.servers_client,
+ server['id'], 'ACTIVE')
+ dst_host = self.admin_servers_client.show_server(
+ server['id'])['server']['OS-EXT-SRV-ATTR:host']
+
+ self.assertNotEqual(src_host, dst_host)
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index aabb40c..c9ffcca 100755
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -102,14 +102,18 @@
params = {'tenant_id': tenant_id}
body = self.client.list_servers(detail=True, **params)
servers = body['servers']
- self.assertEqual([], servers)
+ servers_name = map(lambda x: x['name'], servers)
+ self.assertNotIn(self.s1_name, servers_name)
+ self.assertNotIn(self.s2_name, servers_name)
- # List the admin tenant which has no servers
+ # List the admin tenant shouldn't get servers created by other tenants
admin_tenant_id = self.client.tenant_id
params = {'all_tenants': '', 'tenant_id': admin_tenant_id}
body = self.client.list_servers(detail=True, **params)
servers = body['servers']
- self.assertEqual([], servers)
+ servers_name = map(lambda x: x['name'], servers)
+ self.assertNotIn(self.s1_name, servers_name)
+ self.assertNotIn(self.s2_name, servers_name)
@test.idempotent_id('86c7a8f7-50cf-43a9-9bac-5b985317134f')
def test_list_servers_filter_by_exist_host(self):
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 26cbb090..611d5a2 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -119,8 +119,8 @@
self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
@test.idempotent_id('ca78e20e-fddb-4ce6-b7f7-bcbf8605e66e')
- def test_list_servers_filter_by_server_status(self):
- # Filter the list of servers by server status
+ def test_list_servers_filter_by_active_status(self):
+ # Filter the list of servers by server active status
params = {'status': 'active'}
body = self.client.list_servers(**params)
servers = body['servers']
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index eb1beb1..4dd26af 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -21,16 +21,10 @@
class MultipleCreateTestJSON(base.BaseV2ComputeTest):
_name = 'multiple-create-test'
- def _generate_name(self):
- return data_utils.rand_name(self._name)
-
- def _create_multiple_servers(self, name=None, wait_until=None, **kwargs):
- # NOTE: This is the right way to create_multiple servers and manage to
- # get the created servers into the servers list to be cleaned up after
- # all.
- kwargs['name'] = name if name else self._generate_name()
- if wait_until:
- kwargs['wait_until'] = wait_until
+ def _create_multiple_servers(self, **kwargs):
+ # This is the right way to create_multiple servers and manage to get
+ # the created servers into the servers list to be cleaned up after all.
+ kwargs['name'] = kwargs.get('name', data_utils.rand_name(self._name))
body = self.create_test_server(**kwargs)
return body
diff --git a/tempest/api/compute/servers/test_multiple_create_negative.py b/tempest/api/compute/servers/test_multiple_create_negative.py
index e5b4f46..c4dbe23 100644
--- a/tempest/api/compute/servers/test_multiple_create_negative.py
+++ b/tempest/api/compute/servers/test_multiple_create_negative.py
@@ -22,13 +22,10 @@
class MultipleCreateNegativeTestJSON(base.BaseV2ComputeTest):
_name = 'multiple-create-test'
- def _generate_name(self):
- return data_utils.rand_name(self._name)
-
- def _create_multiple_servers(self, name=None, wait_until=None, **kwargs):
+ def _create_multiple_servers(self, **kwargs):
# This is the right way to create_multiple servers and manage to get
# the created servers into the servers list to be cleaned up after all.
- kwargs['name'] = kwargs.get('name', self._generate_name())
+ kwargs['name'] = kwargs.get('name', data_utils.rand_name(self._name))
body = self.create_test_server(**kwargs)
return body
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index ff5dc49..c48169f 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -17,6 +17,7 @@
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
from tempest import config
@@ -71,8 +72,9 @@
def _create_and_attach_volume(self, server):
# Create a volume and wait for it to become ready
+ vol_name = data_utils.rand_name(self.__class__.__name__ + '-volume')
volume = self.volumes_client.create_volume(
- size=CONF.volume.volume_size, display_name='test')['volume']
+ size=CONF.volume.volume_size, display_name=vol_name)['volume']
self.addCleanup(self.delete_volume, volume['id'])
waiters.wait_for_volume_status(self.volumes_client,
volume['id'], 'available')
@@ -163,7 +165,7 @@
"""Testing volume with shelved instance.
This test checks the attaching and detaching volumes from
- a shelved or shelved ofload instance.
+ a shelved or shelved offload instance.
"""
min_microversion = '2.20'
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index 9e67c25..d8f103a 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -40,13 +40,6 @@
'x-image-meta-disk_format': 'wrong'})
@test.attr(type=['negative'])
- @test.idempotent_id('bb016f15-0820-4f27-a92d-09b2f67d2488')
- def test_delete_image_with_invalid_image_id(self):
- # An image should not be deleted with invalid image id
- self.assertRaises(lib_exc.NotFound, self.client.delete_image,
- '!@$%^&*()')
-
- @test.attr(type=['negative'])
@test.idempotent_id('ec652588-7e3c-4b67-a2f2-0fa96f57c8fc')
def test_delete_non_existent_image(self):
# Return an error while trying to delete a non-existent image
diff --git a/tempest/api/image/v2/test_images_metadefs_resource_types.py b/tempest/api/image/v2/test_images_metadefs_resource_types.py
index a5143a1..3dd432b 100644
--- a/tempest/api/image/v2/test_images_metadefs_resource_types.py
+++ b/tempest/api/image/v2/test_images_metadefs_resource_types.py
@@ -18,7 +18,7 @@
class MetadataResourceTypesTest(base.BaseV2ImageTest):
- """Test the Metadata definition ressource types basic functionality"""
+ """Test the Metadata definition resource types basic functionality"""
@test.idempotent_id('6f358a4e-5ef0-11e6-a795-080027d0d606')
def test_basic_meta_def_resource_type_association(self):
@@ -34,7 +34,7 @@
# NOTE(raiesmh08): Here intentionally I have not added addcleanup
# method for resource type dissociation because its a metadata add and
# being cleaned as soon as namespace is cleaned at test case level.
- # When namespace cleans, resource type associaion will automatically
+ # When namespace cleans, resource type association will automatically
# clean without any error or dependency.
# List resource type associations and validate creation
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index caf7f14..e5972a9 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -16,6 +16,7 @@
import socket
import netaddr
+import testtools
from tempest.api.network import base
from tempest.api.network import base_security_groups as sec_base
@@ -308,11 +309,17 @@
self.assertIn(security_group, port_show['security_groups'])
@test.idempotent_id('58091b66-4ff4-4cc1-a549-05d60c7acd1a')
+ @testtools.skipUnless(
+ test.is_extension_enabled('security-group', 'network'),
+ 'security-group extension not enabled.')
def test_update_port_with_security_group_and_extra_attributes(self):
self._update_port_with_security_groups(
[data_utils.rand_name('secgroup')])
@test.idempotent_id('edf6766d-3d40-4621-bc6e-2521a44c257d')
+ @testtools.skipUnless(
+ test.is_extension_enabled('security-group', 'network'),
+ 'security-group extension not enabled.')
def test_update_port_with_two_security_groups_and_extra_attributes(self):
self._update_port_with_security_groups(
[data_utils.rand_name('secgroup'),
@@ -337,6 +344,9 @@
@test.attr(type='smoke')
@test.idempotent_id('4179dcb9-1382-4ced-84fe-1b91c54f5735')
+ @testtools.skipUnless(
+ test.is_extension_enabled('security-group', 'network'),
+ 'security-group extension not enabled.')
def test_create_port_with_no_securitygroups(self):
network = self.create_network()
self.addCleanup(self.networks_client.delete_network, network['id'])
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index a0c0a5f..33e5852 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -79,7 +79,7 @@
# headers is checked without custom matcher.
#
# As the expected response is 204 No Content, Content-Length presence
- # is not checked here intensionally. According to RFC 7230 a server
+ # is not checked here intentionally. According to RFC 7230 a server
# MUST NOT send the header in such responses. Thus, clients should not
# depend on this header. However, the standard does not require them
# to validate the server's behavior. We leverage that to not refuse
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 8522269..dbe8b4a 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -63,7 +63,8 @@
# create container with metadata value
container_name = data_utils.rand_name(name='TestContainer')
- metadata = {'test-container-meta': 'Meta1'}
+ # metadata name using underscores should be converted to hyphens
+ metadata = {'test_container_meta': 'Meta1'}
resp, _ = self.container_client.create_container(
container_name,
metadata=metadata)
@@ -74,7 +75,7 @@
container_name)
self.assertIn('x-container-meta-test-container-meta', resp)
self.assertEqual(resp['x-container-meta-test-container-meta'],
- metadata['test-container-meta'])
+ metadata['test_container_meta'])
@test.idempotent_id('24d16451-1c0c-4e4f-b59c-9840a3aba40e')
def test_create_container_with_remove_metadata_key(self):
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 919f695..8736f9a 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -954,10 +954,7 @@
# When the file is not downloaded from Swift server, response does
# not contain 'X-Timestamp' header. This is the special case, therefore
# the existence of response headers is checked without custom matcher.
- self.assertIn('content-type', resp)
- self.assertIn('x-trans-id', resp)
self.assertIn('date', resp)
- self.assertIn('accept-ranges', resp)
# Check only the format of common headers with custom matcher
self.assertThat(resp, custom_matchers.AreAllWellFormatted())
diff --git a/tempest/api/volume/v3/admin/__init__.py b/tempest/api/volume/admin/v2/__init__.py
similarity index 100%
rename from tempest/api/volume/v3/admin/__init__.py
rename to tempest/api/volume/admin/v2/__init__.py
diff --git a/tempest/api/volume/admin/test_volume_pools.py b/tempest/api/volume/admin/v2/test_volume_pools.py
similarity index 100%
rename from tempest/api/volume/admin/test_volume_pools.py
rename to tempest/api/volume/admin/v2/test_volume_pools.py
diff --git a/tempest/api/volume/admin/test_volume_type_access.py b/tempest/api/volume/admin/v2/test_volume_type_access.py
similarity index 100%
rename from tempest/api/volume/admin/test_volume_type_access.py
rename to tempest/api/volume/admin/v2/test_volume_type_access.py
diff --git a/tempest/api/volume/admin/test_volumes_list.py b/tempest/api/volume/admin/v2/test_volumes_list.py
similarity index 100%
rename from tempest/api/volume/admin/test_volumes_list.py
rename to tempest/api/volume/admin/v2/test_volumes_list.py
diff --git a/tempest/api/volume/v3/admin/__init__.py b/tempest/api/volume/admin/v3/__init__.py
similarity index 100%
copy from tempest/api/volume/v3/admin/__init__.py
copy to tempest/api/volume/admin/v3/__init__.py
diff --git a/tempest/api/volume/v3/admin/test_user_messages.py b/tempest/api/volume/admin/v3/test_user_messages.py
similarity index 100%
rename from tempest/api/volume/v3/admin/test_user_messages.py
rename to tempest/api/volume/admin/v3/test_user_messages.py
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index e529538..ee1744d 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -26,11 +26,10 @@
super(VolumesV2MetadataTest, cls).resource_setup()
# Create a volume
cls.volume = cls.create_volume()
- cls.volume_id = cls.volume['id']
def tearDown(self):
# Update the metadata to {}
- self.volumes_client.update_volume_metadata(self.volume_id, {})
+ self.volumes_client.update_volume_metadata(self.volume['id'], {})
super(VolumesV2MetadataTest, self).tearDown()
@test.idempotent_id('6f5b125b-f664-44bf-910f-751591fe5769')
@@ -41,17 +40,17 @@
"key3": "value3",
"key4": "<value&special_chars>"}
- body = self.volumes_client.create_volume_metadata(self.volume_id,
+ body = self.volumes_client.create_volume_metadata(self.volume['id'],
metadata)['metadata']
# Get the metadata of the volume
body = self.volumes_client.show_volume_metadata(
- self.volume_id)['metadata']
+ self.volume['id'])['metadata']
self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
# Delete one item metadata of the volume
self.volumes_client.delete_volume_metadata_item(
- self.volume_id, "key1")
+ self.volume['id'], "key1")
body = self.volumes_client.show_volume_metadata(
- self.volume_id)['metadata']
+ self.volume['id'])['metadata']
self.assertNotIn("key1", body)
del metadata["key1"]
self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
@@ -68,17 +67,17 @@
# Create metadata for the volume
body = self.volumes_client.create_volume_metadata(
- self.volume_id, metadata)['metadata']
+ self.volume['id'], metadata)['metadata']
# Get the metadata of the volume
body = self.volumes_client.show_volume_metadata(
- self.volume_id)['metadata']
+ self.volume['id'])['metadata']
self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
# Update metadata
body = self.volumes_client.update_volume_metadata(
- self.volume_id, update)['metadata']
+ self.volume['id'], update)['metadata']
# Get the metadata of the volume
body = self.volumes_client.show_volume_metadata(
- self.volume_id)['metadata']
+ self.volume['id'])['metadata']
self.assertEqual(update, body)
@test.idempotent_id('862261c5-8df4-475a-8c21-946e50e36a20')
@@ -93,14 +92,14 @@
"key3": "value3_update"}
# Create metadata for the volume
body = self.volumes_client.create_volume_metadata(
- self.volume_id, metadata)['metadata']
+ self.volume['id'], metadata)['metadata']
self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
# Update metadata item
body = self.volumes_client.update_volume_metadata_item(
- self.volume_id, "key3", update_item)['meta']
+ self.volume['id'], "key3", update_item)['meta']
# Get the metadata of the volume
body = self.volumes_client.show_volume_metadata(
- self.volume_id)['metadata']
+ self.volume['id'])['metadata']
self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 8f7996a..6be569c 100755
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -14,7 +14,6 @@
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
@@ -32,12 +31,8 @@
def resource_setup(cls):
super(VolumesV2SnapshotTestJSON, cls).resource_setup()
cls.volume_origin = cls.create_volume()
-
cls.name_field = cls.special_fields['name_field']
cls.descrip_field = cls.special_fields['descrip_field']
- # Create 2 snapshots
- for _ in range(2):
- cls.create_snapshot(cls.volume_origin['id'])
def _detach(self, volume_id):
"""Detach volume."""
@@ -45,31 +40,6 @@
waiters.wait_for_volume_status(self.volumes_client,
volume_id, 'available')
- def _list_by_param_values_and_assert(self, with_detail=False, **params):
- """list or list_details with given params and validates result."""
-
- if with_detail:
- fetched_snap_list = self.snapshots_client.list_snapshots(
- detail=True, **params)['snapshots']
- else:
- fetched_snap_list = self.snapshots_client.list_snapshots(
- **params)['snapshots']
-
- # Validating params of fetched snapshots
- for snap in fetched_snap_list:
- for key in params:
- msg = "Failed to list snapshots %s by %s" % \
- ('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):
@@ -135,48 +105,6 @@
# Delete the snapshot
self.cleanup_snapshot(snapshot)
- @test.idempotent_id('59f41f43-aebf-48a9-ab5d-d76340fab32b')
- def test_snapshots_list_with_params(self):
- """list snapshots with params."""
- # Create a snapshot
- display_name = data_utils.rand_name(self.__class__.__name__ + '-snap')
- params = {self.name_field: display_name}
- snapshot = self.create_snapshot(self.volume_origin['id'], **params)
- self.addCleanup(self.cleanup_snapshot, snapshot)
-
- # Verify list snapshots by display_name filter
- params = {self.name_field: snapshot[self.name_field]}
- self._list_by_param_values_and_assert(**params)
-
- # Verify list snapshots by status filter
- params = {'status': 'available'}
- self._list_by_param_values_and_assert(**params)
-
- # Verify list snapshots by status and display name filter
- params = {'status': 'available',
- self.name_field: snapshot[self.name_field]}
- self._list_by_param_values_and_assert(**params)
-
- @test.idempotent_id('220a1022-1fcd-4a74-a7bd-6b859156cda2')
- def test_snapshots_list_details_with_params(self):
- """list snapshot details with params."""
- # Create a snapshot
- display_name = data_utils.rand_name(self.__class__.__name__ + '-snap')
- params = {self.name_field: display_name}
- snapshot = self.create_snapshot(self.volume_origin['id'], **params)
- self.addCleanup(self.cleanup_snapshot, snapshot)
-
- # Verify list snapshot details by display_name filter
- params = {self.name_field: snapshot[self.name_field]}
- self._list_by_param_values_and_assert(with_detail=True, **params)
- # Verify list snapshot details by status filter
- params = {'status': 'available'}
- self._list_by_param_values_and_assert(with_detail=True, **params)
- # Verify list snapshot details by status and display name filter
- params = {'status': 'available',
- self.name_field: snapshot[self.name_field]}
- self._list_by_param_values_and_assert(with_detail=True, **params)
-
@test.idempotent_id('677863d1-3142-456d-b6ac-9924f667a7f4')
def test_volume_from_snapshot(self):
# Creates a volume a snapshot passing a size different from the source
@@ -193,25 +121,6 @@
self.assertEqual(volume['snapshot_id'], src_snap['id'])
self.assertEqual(int(volume['size']), src_size + 1)
- @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/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
new file mode 100644
index 0000000..4416bef
--- /dev/null
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -0,0 +1,116 @@
+# 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 config
+from tempest.lib import decorators
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumesV2SnapshotListTestJSON(base.BaseVolumeTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(VolumesV2SnapshotListTestJSON, cls).skip_checks()
+ if not CONF.volume_feature_enabled.snapshot:
+ raise cls.skipException("Cinder volume snapshots are disabled")
+
+ @classmethod
+ def resource_setup(cls):
+ super(VolumesV2SnapshotListTestJSON, cls).resource_setup()
+ cls.volume_origin = cls.create_volume()
+ cls.name_field = cls.special_fields['name_field']
+ # Create snapshots with params
+ for _ in range(2):
+ cls.snapshot = cls.create_snapshot(cls.volume_origin['id'])
+
+ def _list_by_param_values_and_assert(self, with_detail=False, **params):
+ """list or list_details with given params and validates result."""
+
+ fetched_snap_list = self.snapshots_client.list_snapshots(
+ detail=with_detail, **params)['snapshots']
+
+ # Validating params of fetched snapshots
+ for snap in fetched_snap_list:
+ for key in params:
+ msg = "Failed to list snapshots %s by %s" % \
+ ('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('59f41f43-aebf-48a9-ab5d-d76340fab32b')
+ def test_snapshots_list_with_params(self):
+ """list snapshots with params."""
+ # Verify list snapshots by display_name filter
+ params = {self.name_field: self.snapshot[self.name_field]}
+ self._list_by_param_values_and_assert(**params)
+
+ # Verify list snapshots by status filter
+ params = {'status': 'available'}
+ self._list_by_param_values_and_assert(**params)
+
+ # Verify list snapshots by status and display name filter
+ params = {'status': 'available',
+ self.name_field: self.snapshot[self.name_field]}
+ self._list_by_param_values_and_assert(**params)
+
+ @test.idempotent_id('220a1022-1fcd-4a74-a7bd-6b859156cda2')
+ def test_snapshots_list_details_with_params(self):
+ """list snapshot details with params."""
+ # Verify list snapshot details by display_name filter
+ params = {self.name_field: self.snapshot[self.name_field]}
+ self._list_by_param_values_and_assert(with_detail=True, **params)
+ # Verify list snapshot details by status filter
+ params = {'status': 'available'}
+ self._list_by_param_values_and_assert(with_detail=True, **params)
+ # Verify list snapshot details by status and display name filter
+ params = {'status': 'available',
+ self.name_field: self.snapshot[self.name_field]}
+ self._list_by_param_values_and_assert(with_detail=True, **params)
+
+ @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'])
+ self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
+ self.snapshots.remove(snapshot)
+
+
+class VolumesV1SnapshotLimitTestJSON(VolumesV2SnapshotListTestJSON):
+ _api_version = 1
diff --git a/tempest/clients.py b/tempest/clients.py
index 4c677f0..edc34bd 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -14,7 +14,9 @@
# under the License.
import copy
+
from oslo_log import log as logging
+
from tempest.common import negative_rest_client
from tempest import config
from tempest import exceptions
@@ -115,7 +117,7 @@
configuration[service_for_config] = (
config.service_client_config(service_for_config))
except lib_exc.UnknownServiceClient:
- LOG.warn(
+ LOG.warning(
'Could not load configuration for service %s' % service)
return configuration
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index ba1f1fa..f577d9b 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -93,7 +93,7 @@
testr_conf_file.write(testr_conf)
def get_configparser(self, conf_path):
- config_parse = moves.configparser.SafeConfigParser()
+ config_parse = moves.configparser.ConfigParser()
config_parse.optionxform = str
# get any existing values if a config file already exists
if os.path.isfile(conf_path):
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index fef836c..236953c 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -264,8 +264,8 @@
run_thread = threading.Thread(target=run_argv_thread)
run_thread.start()
- returncodes['subunit-trace'] = subunit_trace.trace(subunit_r,
- sys.stdout)
+ returncodes['subunit-trace'] = subunit_trace.trace(
+ subunit_r, sys.stdout, post_fails=True, print_failures=True)
run_thread.join()
subunit_r.close()
# python version of pipefail
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index c36c9be..b2e72c5 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -370,10 +370,9 @@
replace = opts.replace_ext
global CONF_PARSER
- outfile = sys.stdout
if update:
conf_file = _get_config_file()
- CONF_PARSER = moves.configparser.SafeConfigParser()
+ CONF_PARSER = moves.configparser.ConfigParser()
CONF_PARSER.optionxform = str
CONF_PARSER.readfp(conf_file)
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 04c9645..b96b1c0 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -136,8 +136,6 @@
email = data_utils.rand_name(root) + "@example.com"
user = self.creds_client.create_user(
username, user_password, project, email)
- if 'user' in user:
- user = user['user']
role_assigned = False
if admin:
self.creds_client.assign_user_role(user, project, self.admin_role)
diff --git a/tempest/config.py b/tempest/config.py
index f6c89ae..b6fca7e 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -339,6 +339,10 @@
cfg.BoolOpt('suspend',
default=True,
help="Does the test environment support suspend/resume?"),
+ cfg.BoolOpt('cold_migration',
+ default=True,
+ help="Does the test environment support cold migration "
+ "available?"),
cfg.BoolOpt('live_migration',
default=True,
help="Does the test environment support live migration "
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index e2d6585..4123ae5 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -19,7 +19,7 @@
PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
- 'ironic', 'savanna', 'heat', 'sahara']
+ 'ironic', 'heat', 'sahara']
PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
TEST_DEFINITION = re.compile(r'^\s*def test.*')
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index be3aa49..1239ac5 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -69,7 +69,8 @@
lines[line_no - 1] = ''.join(('{%s:s}' % patch_id, lines[line_no - 1]))
self.source_files[filename] = self._quote('\n').join(lines)
- def _save_changes(self, filename, source):
+ @staticmethod
+ def _save_changes(filename, source):
print('%s fixed' % filename)
with open(filename, 'w') as f:
f.write(source)
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 70be40c..4095c77 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -19,6 +19,7 @@
import string
import uuid
+from debtcollector import removals
from oslo_utils import netutils
import six.moves
@@ -153,7 +154,7 @@
This generates a string with an arbitrary number of characters, generated
by looping the base_text string. If the size is smaller than the size of
- base_text, returning string is shrinked to the size.
+ base_text, returning string is shrunk to the size.
:param int size: a returning characters size
:param str base_text: a string you want to repeat
:return: size string
@@ -175,6 +176,10 @@
for i in range(size)])
+@removals.remove(
+ message="use get_ipv6_addr_by_EUI64 from oslo_utils.netutils",
+ version="Newton",
+ removal_version="Ocata")
def get_ipv6_addr_by_EUI64(cidr, mac):
"""Generate a IPv6 addr by EUI-64 with CIDR and MAC
diff --git a/tempest/services/volume/base/base_qos_client.py b/tempest/lib/services/volume/v1/qos_client.py
similarity index 96%
copy from tempest/services/volume/base/base_qos_client.py
copy to tempest/lib/services/volume/v1/qos_client.py
index 0ce76a7..65ae274 100644
--- a/tempest/services/volume/base/base_qos_client.py
+++ b/tempest/lib/services/volume/v1/qos_client.py
@@ -18,8 +18,13 @@
from tempest.lib import exceptions as lib_exc
-class BaseQosSpecsClient(rest_client.RestClient):
- """Client class to send CRUD QoS API requests"""
+class QosSpecsClient(rest_client.RestClient):
+ """Volume V1 QoS client.
+
+ Client class to send CRUD QoS API requests
+ """
+
+ api_version = "v1"
def is_resource_deleted(self, qos_id):
try:
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/lib/services/volume/v1/snapshots_client.py
old mode 100755
new mode 100644
similarity index 91%
copy from tempest/services/volume/base/base_snapshots_client.py
copy to tempest/lib/services/volume/v1/snapshots_client.py
index 38a6dc7..1881078
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/lib/services/volume/v1/snapshots_client.py
@@ -17,8 +17,8 @@
from tempest.lib import exceptions as lib_exc
-class BaseSnapshotsClient(rest_client.RestClient):
- """Base Client class to send CRUD Volume API requests."""
+class SnapshotsClient(rest_client.RestClient):
+ """Client class to send CRUD Volume V1 API requests."""
create_resp = 200
@@ -26,7 +26,7 @@
"""List all the snapshot.
Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#listSnapshots
+ api-ref-blockstorage-v1.html#listSnapshots
"""
url = 'snapshots'
if detail:
@@ -43,7 +43,7 @@
"""Returns the details of a single snapshot.
Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#showSnapshot
+ api-ref-blockstorage-v1.html#showSnapshot
"""
url = "snapshots/%s" % snapshot_id
resp, body = self.get(url)
@@ -55,7 +55,7 @@
"""Creates a new snapshot.
Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createSnapshot
+ api-ref-blockstorage-v1.html#createSnapshot
"""
post_body = json.dumps({'snapshot': kwargs})
resp, body = self.post('snapshots', post_body)
@@ -63,23 +63,11 @@
self.expected_success(self.create_resp, resp.status)
return rest_client.ResponseBody(resp, body)
- def update_snapshot(self, snapshot_id, **kwargs):
- """Updates a snapshot.
-
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateSnapshot
- """
- put_body = json.dumps({'snapshot': kwargs})
- resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
def delete_snapshot(self, snapshot_id):
"""Delete Snapshot.
Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#deleteSnapshot
+ api-ref-blockstorage-v1.html#deleteSnapshot
"""
resp, body = self.delete("snapshots/%s" % snapshot_id)
self.expected_success(202, resp.status)
@@ -126,11 +114,24 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+ def update_snapshot(self, snapshot_id, **kwargs):
+ """Updates a snapshot.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-blockstorage-v1.html#
+ updateSnapshotMetadata
+ """
+ put_body = json.dumps({'snapshot': kwargs})
+ resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
def show_snapshot_metadata(self, snapshot_id):
"""Get metadata of the snapshot.
Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
+ api-ref-blockstorage-v1.html#
showSnapshotMetadata
"""
url = "snapshots/%s/metadata" % snapshot_id
@@ -143,7 +144,7 @@
"""Update metadata for the snapshot.
Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
+ api-ref-blockstorage-v1.html#
updateSnapshotMetadata
"""
put_body = json.dumps(kwargs)
diff --git a/tempest/services/volume/base/base_qos_client.py b/tempest/lib/services/volume/v2/qos_client.py
similarity index 96%
rename from tempest/services/volume/base/base_qos_client.py
rename to tempest/lib/services/volume/v2/qos_client.py
index 0ce76a7..5fac00f 100644
--- a/tempest/services/volume/base/base_qos_client.py
+++ b/tempest/lib/services/volume/v2/qos_client.py
@@ -18,8 +18,13 @@
from tempest.lib import exceptions as lib_exc
-class BaseQosSpecsClient(rest_client.RestClient):
- """Client class to send CRUD QoS API requests"""
+class QosSpecsClient(rest_client.RestClient):
+ """Volume V2 QoS client.
+
+ Client class to send CRUD QoS API requests
+ """
+
+ api_version = "v2"
def is_resource_deleted(self, qos_id):
try:
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
old mode 100755
new mode 100644
similarity index 97%
rename from tempest/services/volume/base/base_snapshots_client.py
rename to tempest/lib/services/volume/v2/snapshots_client.py
index 38a6dc7..c84e557
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -17,10 +17,10 @@
from tempest.lib import exceptions as lib_exc
-class BaseSnapshotsClient(rest_client.RestClient):
- """Base Client class to send CRUD Volume API requests."""
-
- create_resp = 200
+class SnapshotsClient(rest_client.RestClient):
+ """Client class to send CRUD Volume V2 API requests."""
+ api_version = "v2"
+ create_resp = 202
def list_snapshots(self, detail=False, **params):
"""List all the snapshot.
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index 086b82d..43adfb1 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -36,7 +36,6 @@
super(TestAggregatesBasicOps, cls).setup_clients()
# Use admin client by default
cls.manager = cls.admin_manager
- super(TestAggregatesBasicOps, cls).resource_setup()
cls.aggregates_client = cls.manager.aggregates_client
cls.hosts_client = cls.manager.hosts_client
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index a67927a..3ac6759 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -47,12 +47,6 @@
10. Check SSH connection to instance after reboot
"""
-
- def nova_list(self):
- servers = self.servers_client.list_servers()
- # The list servers in the compute client is inconsistent...
- return servers['servers']
-
def nova_show(self, server):
got_server = (self.servers_client.show_server(server['id'])
['server'])
@@ -112,7 +106,7 @@
server = self.create_server(image_id=image,
key_name=keypair['name'],
wait_until='ACTIVE')
- servers = self.nova_list()
+ servers = self.servers_client.list_servers()['servers']
self.assertIn(server['id'], [x['id'] for x in servers])
self.nova_show(server)
@@ -155,10 +149,18 @@
# delete the floating IP, this should refresh the server addresses
self.compute_floating_ips_client.delete_floating_ip(floating_ip['id'])
- server = self.servers_client.show_server(server['id'])['server']
- address = self._get_floating_ip_in_server_addresses(
- floating_ip, server)
- self.assertIsNone(
- address,
- "Floating IP '%s' should not be in server addresses: %s" %
- (floating_ip['ip'], server['addresses']))
+
+ def is_floating_ip_detached_from_server():
+ server_info = self.servers_client.show_server(
+ server['id'])['server']
+ address = self._get_floating_ip_in_server_addresses(
+ floating_ip, server_info)
+ return (not address)
+
+ if not test_utils.call_until_true(
+ is_floating_ip_detached_from_server,
+ CONF.compute.build_timeout,
+ CONF.compute.build_interval):
+ msg = ("Floating IP '%s' should not be in server addresses: %s" %
+ (floating_ip['ip'], server['addresses']))
+ raise exceptions.TimeoutException(msg)
diff --git a/tempest/scenario/test_server_multinode.py b/tempest/scenario/test_server_multinode.py
index 0cf72c3..b323d2a 100644
--- a/tempest/scenario/test_server_multinode.py
+++ b/tempest/scenario/test_server_multinode.py
@@ -42,7 +42,6 @@
# this is needed so that we can use the availability_zone:host
# scheduler hint, which is admin_only by default
cls.servers_client = cls.admin_manager.servers_client
- super(TestServerMultinode, cls).resource_setup()
@test.idempotent_id('9cecbe35-b9d4-48da-a37e-7ce70aa43d30')
@test.attr(type='smoke')
diff --git a/tempest/services/volume/v1/__init__.py b/tempest/services/volume/v1/__init__.py
index a7d9270..e386faf 100644
--- a/tempest/services/volume/v1/__init__.py
+++ b/tempest/services/volume/v1/__init__.py
@@ -18,15 +18,16 @@
EncryptionTypesClient
from tempest.lib.services.volume.v1.extensions_client import ExtensionsClient
from tempest.lib.services.volume.v1.hosts_client import HostsClient
+from tempest.lib.services.volume.v1.qos_client import QosSpecsClient
from tempest.lib.services.volume.v1.quotas_client import QuotasClient
from tempest.lib.services.volume.v1.services_client import ServicesClient
+from tempest.lib.services.volume.v1.snapshots_client import SnapshotsClient
from tempest.lib.services.volume.v1.types_client import TypesClient
from tempest.services.volume.v1.json.backups_client import BackupsClient
-from tempest.services.volume.v1.json.qos_client import QosSpecsClient
-from tempest.services.volume.v1.json.snapshots_client import SnapshotsClient
from tempest.services.volume.v1.json.volumes_client import VolumesClient
__all__ = ['AvailabilityZoneClient', 'EncryptionTypesClient',
- 'ExtensionsClient', 'HostsClient', 'QuotasClient', 'ServicesClient',
- 'TypesClient', 'BackupsClient', 'QosSpecsClient', 'SnapshotsClient',
+ 'ExtensionsClient', 'HostsClient', 'QuotasClient',
+ 'QosSpecsClient', 'ServicesClient',
+ 'SnapshotsClient', 'TypesClient', 'BackupsClient',
'VolumesClient', ]
diff --git a/tempest/services/volume/v1/json/admin/__init__.py b/tempest/services/volume/v1/json/admin/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v1/json/admin/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/v1/json/qos_client.py b/tempest/services/volume/v1/json/qos_client.py
deleted file mode 100644
index b2b2195..0000000
--- a/tempest/services/volume/v1/json/qos_client.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.services.volume.base import base_qos_client
-
-
-class QosSpecsClient(base_qos_client.BaseQosSpecsClient):
- """Volume V1 QoS client."""
diff --git a/tempest/services/volume/v1/json/snapshots_client.py b/tempest/services/volume/v1/json/snapshots_client.py
deleted file mode 100644
index b039c2b..0000000
--- a/tempest/services/volume/v1/json/snapshots_client.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.services.volume.base import base_snapshots_client
-
-
-class SnapshotsClient(base_snapshots_client.BaseSnapshotsClient):
- """Client class to send CRUD Volume V1 API requests."""
diff --git a/tempest/services/volume/v2/__init__.py b/tempest/services/volume/v2/__init__.py
index c99a81a..b63e6f2 100644
--- a/tempest/services/volume/v2/__init__.py
+++ b/tempest/services/volume/v2/__init__.py
@@ -16,17 +16,17 @@
AvailabilityZoneClient
from tempest.lib.services.volume.v2.extensions_client import ExtensionsClient
from tempest.lib.services.volume.v2.hosts_client import HostsClient
+from tempest.lib.services.volume.v2.qos_client import QosSpecsClient
from tempest.lib.services.volume.v2.quotas_client import QuotasClient
from tempest.lib.services.volume.v2.services_client import ServicesClient
+from tempest.lib.services.volume.v2.snapshots_client import SnapshotsClient
from tempest.lib.services.volume.v2.types_client import TypesClient
from tempest.services.volume.v2.json.backups_client import BackupsClient
from tempest.services.volume.v2.json.encryption_types_client import \
EncryptionTypesClient
-from tempest.services.volume.v2.json.qos_client import QosSpecsClient
-from tempest.services.volume.v2.json.snapshots_client import SnapshotsClient
from tempest.services.volume.v2.json.volumes_client import VolumesClient
__all__ = ['AvailabilityZoneClient', 'ExtensionsClient', 'HostsClient',
- 'QuotasClient', 'ServicesClient', 'TypesClient', 'BackupsClient',
- 'EncryptionTypesClient', 'QosSpecsClient', 'SnapshotsClient',
- 'VolumesClient', ]
+ 'QosSpecsClient', 'QuotasClient', 'ServicesClient',
+ 'SnapshotsClient', 'TypesClient', 'BackupsClient',
+ 'EncryptionTypesClient', 'VolumesClient', ]
diff --git a/tempest/services/volume/v2/json/admin/__init__.py b/tempest/services/volume/v2/json/admin/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v2/json/admin/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/v2/json/qos_client.py b/tempest/services/volume/v2/json/qos_client.py
deleted file mode 100644
index 3c0f74f..0000000
--- a/tempest/services/volume/v2/json/qos_client.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.services.volume.base import base_qos_client
-
-
-class QosSpecsClient(base_qos_client.BaseQosSpecsClient):
- api_version = "v2"
diff --git a/tempest/services/volume/v2/json/snapshots_client.py b/tempest/services/volume/v2/json/snapshots_client.py
deleted file mode 100644
index a2d415f..0000000
--- a/tempest/services/volume/v2/json/snapshots_client.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.services.volume.base import base_snapshots_client
-
-
-class SnapshotsClient(base_snapshots_client.BaseSnapshotsClient):
- """Client class to send CRUD Volume V2 API requests."""
- api_version = "v2"
- create_resp = 202
diff --git a/test-requirements.txt b/test-requirements.txt
index 04c3d6d..567cf20 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -5,7 +5,7 @@
# needed for doc build
sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
python-subunit>=0.0.18 # Apache-2.0/BSD
-oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
+oslosphinx>=4.7.0 # Apache-2.0
reno>=1.8.0 # Apache2
mock>=2.0 # BSD
coverage>=3.6 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index 05fa326..7096e60 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = pep8,py35,py34,py27
+envlist = pep8,py35,py34,py27,pip-check-reqs
minversion = 2.3.1
skipsdist = True
@@ -16,6 +16,7 @@
setenv =
VIRTUAL_ENV={envdir}
OS_TEST_PATH=./tempest/tests
+ PYTHONWARNINGS=default::DeprecationWarning
passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH OS_TEST_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
usedevelop = True
install_command = pip install -U {opts} {packages}
@@ -88,7 +89,7 @@
# See the testrepository bug: https://bugs.launchpad.net/testrepository/+bug/1208610
commands =
find . -type f -name "*.pyc" -delete
- tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))' {posargs}
+ tempest run --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))' {posargs}
[testenv:smoke]
envdir = .tox/tempest
@@ -153,7 +154,18 @@
# Skipped because of new hacking 0.9: H405
ignore = E125,E123,E129
show-source = True
-exclude = .git,.venv,.tox,dist,doc,openstack,*egg
+exclude = .git,.venv,.tox,dist,doc,*egg
[testenv:releasenotes]
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
+
+[testenv:pip-check-reqs]
+# Do not install test-requirements as that will pollute the virtualenv for
+# determining missing packages.
+# This also means that pip-check-reqs must be installed separately, outside
+# of the requirements.txt files
+deps = pip_check_reqs
+ -r{toxinidir}/requirements.txt
+commands=
+ pip-extra-reqs -d --ignore-file=tempest/tests/* tempest
+ pip-missing-reqs -d --ignore-file=tempest/tests/* tempest