Merge "include domain_id when creating groups"
diff --git a/HACKING.rst b/HACKING.rst
index 0962f80..b82f8c9 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -18,6 +18,8 @@
- [T109] Cannot use testtools.skip decorator; instead use
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
- [N322] Method's default argument shouldn't be mutable
Test Data/Configuration
@@ -212,9 +214,9 @@
things to watch out for to try to avoid issues when running your tests in
parallel.
-- Resources outside of a tenant scope still have the potential to conflict. This
+- Resources outside of a project scope still have the potential to conflict. This
is a larger concern for the admin tests since most resources and actions that
- require admin privileges are outside of tenants.
+ require admin privileges are outside of projects.
- Races between methods in the same class are not a problem because
parallelization in tempest is at the test class level, but if there is a json
diff --git a/README.rst b/README.rst
index c859ddd..7da83cd 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,14 @@
Tempest - The OpenStack Integration Test Suite
==============================================
+.. image:: https://img.shields.io/pypi/v/tempest.svg
+ :target: https://pypi.python.org/pypi/tempest/
+ :alt: Latest Version
+
+.. image:: https://img.shields.io/pypi/dm/tempest.svg
+ :target: https://pypi.python.org/pypi/tempest/
+ :alt: Downloads
+
This is a set of integration tests to be run against a live OpenStack
cluster. Tempest has batteries of tests for OpenStack API validation,
Scenarios, and other specific tests useful in validating an OpenStack
diff --git a/data/tempest-plugins-registry.header b/data/tempest-plugins-registry.header
new file mode 100644
index 0000000..9821e8e
--- /dev/null
+++ b/data/tempest-plugins-registry.header
@@ -0,0 +1,23 @@
+..
+ Note to patch submitters: this file is covered by a periodic proposal
+ job. You should edit the files data/tempest-plugins-registry.footer
+ and data/tempest-plugins-registry.header instead of this one.
+
+==========================
+ Tempest Plugin Registry
+==========================
+
+Since we've created the external plugin mechanism, it's gotten used by
+a lot of projects. The following is a list of plugins that currently
+exist.
+
+Detected Plugins
+================
+
+The following are plugins that a script has found in the openstack/
+namespace, which includes but is not limited to official OpenStack
+projects.
+
++----------------------------+-------------------------------------------------------------------------+
+|Plugin Name |URL |
++----------------------------+-------------------------------------------------------------------------+
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 524c0fa..245386b 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -17,7 +17,7 @@
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/tenant name.
+project name.
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
@@ -59,13 +59,13 @@
Dynamic Credentials (formerly known as Tenant isolation) was originally created
to enable running Tempest in parallel. For each test class it creates a unique
set of user credentials to use for the tests in the class. It can create up to
-three sets of username, password, and tenant/project names for a primary user,
-an admin user, and an alternate user. To enable and use dynamic credentials you
+three sets of username, password, and project names for a primary user,
+an admin user, and an alternate user. To enable and use dynamic credentials you
only need to configure two things:
#. A set of admin credentials with permissions to create users and
- tenants/projects. This is specified in the ``auth`` section with the
- ``admin_username``, ``admin_tenant_name``, ``admin_domain_name`` and
+ projects. This is specified in the ``auth`` section with the
+ ``admin_username``, ``admin_project_name``, ``admin_domain_name`` and
``admin_password`` options
#. To enable dynamic credentials in the ``auth`` section with the
``use_dynamic_credentials`` option.
@@ -112,7 +112,7 @@
#. Set ``use_dynamic_credentials = False`` in the ``auth`` group
It is worth pointing out that each set of credentials in the accounts.yaml
-should have a unique tenant. This is required to provide proper isolation
+should have a unique project. This is required to provide proper isolation
to the tests using the credentials, and failure to do this will likely cause
unexpected failures in some tests.
@@ -123,7 +123,7 @@
removed in a future release.**
When Tempest was refactored to allow for locking test accounts, the original
-non-tenant isolated case was converted to internally work similarly to the
+non-project isolated case was converted to internally work similarly to the
accounts.yaml file. This mechanism was then called the legacy test accounts
provider. To use the legacy test accounts provider you can specify the sets of
credentials in the configuration file as detailed above with following nine
@@ -131,13 +131,13 @@
#. ``username``
#. ``password``
- #. ``tenant_name``
+ #. ``project_name``
#. ``admin_username``
#. ``admin_password``
- #. ``admin_tenant_name``
+ #. ``admin_project_name``
#. ``alt_username``
#. ``alt_password``
- #. ``alt_tenant_name``
+ #. ``alt_project_name``
If using Identity API v3, use the ``domain_name`` option to specify a
domain other than the default domain. The ``auth_version`` setting is
@@ -246,11 +246,26 @@
run. This section covers the different methods of configuring Tempest to provide
a network when creating servers.
+The ``validation`` group gathers all the connection options to remotely access the
+created servers.
+
+To enable remote access to servers, at least the three following options need to be
+set:
+
+* The ``run_validation`` option needs be set to ``true``.
+
+* The ``connect_method`` option. Two connect methods are available: ``fixed`` and
+ ``floating``, the later being set by default.
+
+* The ``auth_method`` option. Currently, only authentication by keypair is
+ available.
+
+
Fixed Network Name
""""""""""""""""""
This is the simplest method of specifying how networks should be used. You can
just specify a single network name/label to use for all server creations. The
-limitation with this is that all tenants/projects and users must be able to see
+limitation with this is that all projects and users must be able to see
that network name/label if they are to perform a network list and be able to use
it.
@@ -272,10 +287,10 @@
"""""""""""""
If you are using an accounts file to provide credentials for running Tempest
then you can leverage it to also specify which network should be used with
-server creations on a per tenant/project and user pair basis. This provides
+server creations on a per project and user pair basis. This provides
the necessary flexibility to work with more intricate networking configurations
by enabling the user to specify exactly which network to use for which
-tenants/projects. You can refer to the accounts.yaml.sample file included in
+projects. You can refer to the accounts.yaml.sample file included in
the Tempest repo for the syntax around specifying networks in the file.
However, specifying a network is not required when using an accounts file. If
@@ -296,7 +311,7 @@
With Dynamic Credentials
""""""""""""""""""""""""
With dynamic credentials enabled and using nova-network, your only option for
-configuration is to either set a fixed network name or not. However, in most
+configuration is to either set a fixed network name or not. However, in most
cases it shouldn't matter because nova-network should have no problem booting a
server with multiple networks. If this is not the case for your cloud then using
an accounts file is recommended because it provides the necessary flexibility to
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 17def1c..10364db 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -11,7 +11,9 @@
HACKING
REVIEWING
plugin
+ plugin-registry
library
+ microversion_testing
------------
Field Guides
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
new file mode 100644
index 0000000..63ec04b
--- /dev/null
+++ b/doc/source/microversion_testing.rst
@@ -0,0 +1,207 @@
+===================================
+How To Implement Microversion Tests
+===================================
+
+Tempest provides stable interfaces to test API Microversion.
+For Details, see: `API Microversion testing Framework`_
+This document explains how to implement Microversion tests using those
+interfaces.
+
+.. _API Microversion testing Framework: http://docs.openstack.org/developer/tempest/library/api_microversion_testing.html
+
+
+Configuration options for Microversion
+""""""""""""""""""""""""""""""""""""""
+
+* Add configuration options for specifying test target Microversions.
+ We need to specify test target Microversions because the supported
+ Microversions may be different between OpenStack clouds. For operating
+ multiple Microversion tests in a single Tempest operation, configuration
+ options should represent the range of test target Microversions.
+ New configuration options are:
+ * min_microversion
+ * max_microversion
+
+ Those should be defined under respective section of each service.
+ For example::
+ [compute]
+ min_microversion = None
+ max_microversion = latest
+
+
+How To Implement Microversion Tests
+"""""""""""""""""""""""""""""""""""
+
+Step1: Add skip logic based on configured Microversion range
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Add logic to skip the tests based on Tests class and configured Microversion
+range.
+api_version_utils.check_skip_with_microversion function can be used
+to automatically skip the tests which do not fall under configured
+Microversion range.
+For example::
+
+ class BaseTestCase1(api_version_utils.BaseMicroversionTest):
+
+ [..]
+ @classmethod
+ def skip_checks(cls):
+ super(BaseTestCase1, cls).skip_checks()
+ api_version_utils.check_skip_with_microversion(cls.min_microversion,
+ cls.max_microversion,
+ CONF.compute.min_microversion,
+ CONF.compute.max_microversion)
+
+Skip logic can be added in tests base class or any specific test class depends on
+tests class structure.
+
+Step2: Selected API request microversion
+''''''''''''''''''''''''''''''''''''''''
+
+Select appropriate Microversion which needs to be used
+to send with API request.
+api_version_utils.select_request_microversion function can be used
+to select the appropriate Microversion which will be used for API request.
+For example::
+
+ @classmethod
+ def resource_setup(cls):
+ super(BaseTestCase1, cls).resource_setup()
+ cls.request_microversion = (
+ api_version_utils.select_request_microversion(
+ cls.min_microversion,
+ CONF.compute.min_microversion))
+
+
+Step3: Set Microversion on Service Clients
+''''''''''''''''''''''''''''''''''''''''''
+
+Microversion selected by Test Class in previous step needs to be set on
+service clients so that APIs can be requested with selected Microversion.
+
+Microversion can be defined as global variable on service clients which
+can be set using fixture.
+Also Microversion header name needs to be defined on service clients which
+should be constant because it is not supposed to be changed by project
+as per API contract.
+For example::
+
+ COMPUTE_MICROVERSION = None
+
+ class BaseClient1(rest_client.RestClient):
+ api_microversion_header_name = 'X-OpenStack-Nova-API-Version'
+
+Now test class can set the selected Microversion on required service clients
+using fixture which can take care of resetting the same once tests is completed.
+For example::
+
+ def setUp(self):
+ super(BaseTestCase1, self).setUp()
+ self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+ self.request_microversion))
+
+Service clients needs to add set Microversion in API request header which
+can be done by overriding the get_headers() method of rest_client.
+For example::
+
+ COMPUTE_MICROVERSION = None
+
+ class BaseClient1(rest_client.RestClient):
+ api_microversion_header_name = 'X-OpenStack-Nova-API-Version'
+
+ def get_headers(self):
+ headers = super(BaseClient1, self).get_headers()
+ if COMPUTE_MICROVERSION:
+ headers[self.api_microversion_header_name] = COMPUTE_MICROVERSION
+ return headers
+
+
+Step4: Separate Test classes for each Microversion
+''''''''''''''''''''''''''''''''''''''''''''''''''
+
+This is last step to implement Microversion test class.
+
+For any Microversion tests, basically we need to implement a
+separate test class. In addition, each test class defines its
+Microversion range with class variable like min_microversion
+and max_microversion. Tests will be valid for that defined range.
+If that range is out of configured Microversion range then, test
+will be skipped.
+
+*NOTE: Microversion testing is supported at test class level not at individual
+test case level.*
+For example:
+
+Below test is applicable for Microversion from 2.2 till 2.9::
+
+ class BaseTestCase1(api_version_utils.BaseMicroversionTest,
+ tempest.test.BaseTestCase):
+
+ [..]
+
+
+ class Test1(BaseTestCase1):
+ min_microversion = '2.2'
+ max_microversion = '2.9'
+
+ [..]
+
+Below test is applicable for Microversion from 2.10 till latest::
+
+ class Test2(BaseTestCase1):
+ min_microversion = '2.10'
+ max_microversion = 'latest'
+
+ [..]
+
+
+
+
+Notes about Compute Microversion Tests
+"""""""""""""""""""""""""""""""""""
+Some of the compute Microversion tests have been already implemented
+with the Microversion testing framework. So for further tests only
+step 4 is needed.
+
+Along with that JSON response schema might need versioning if needed.
+
+Compute service clients strictly validate the response against defined JSON
+schema and does not allow additional elements in response.
+So if that Microversion changed the API response then schema needs to be versioned.
+New JSON schema file needs to be defined with new response attributes and service
+client methods will select the schema based on requested microversion.
+
+If Microversion tests are implemented randomly meaning not
+in sequence order(v2.20 tests added and previous Microversion tests are not yet added)
+then, still schema might need to be version for older Microversion if they changed
+the response.
+This is because Nova Microversion includes all the previous Microversions behavior.
+
+For Example:
+ Implementing the v2.20 Microversion tests before v2.9 and 2.19-
+ v2.20 API request will respond as latest behavior of Nova till v2.20,
+ and in v2.9 and 2.19, server response has been changed so response schema needs
+ to be versioned accordingly.
+
+That can be done by using the get_schema method in below module:
+
+The base_compute_client module
+''''''''''''''''''''''''''''''
+
+.. automodule:: tempest.lib.services.compute.base_compute_client
+ :members:
+
+
+Microversion tests implemented in Tempest
+"""""""""""""""""""""""""""""""""""""""""
+
+* Compute
+
+ * `2.1`_
+
+ .. _2.1: http://docs.openstack.org/developer/nova/api_microversion_history.html#id1
+
+ * `2.2`_
+
+ .. _2.2: http://docs.openstack.org/developer/nova/api_microversion_history.html#id2
diff --git a/doc/source/plugin-registry.rst b/doc/source/plugin-registry.rst
new file mode 100644
index 0000000..517e5b8
--- /dev/null
+++ b/doc/source/plugin-registry.rst
@@ -0,0 +1,23 @@
+..
+ Note to patch submitters: this file is covered by a periodic proposal
+ job. You should edit the files data/tempest-plugins-registry.footer
+ data/tempest-plugins-registry.header instead of this one.
+
+==========================
+ Tempest Plugin Registry
+==========================
+
+Since we've created the external plugin mechanism, it's gotten used by
+a lot of projects. The following is a list of plugins that currently
+exist.
+
+Detected Plugins
+================
+
+The following will list plugins that a script has found in the openstack/
+namespace, which includes but is not limited to official OpenStack
+projects.
+
++----------------------------+-------------------------------------------------------------------------+
+|Plugin Name |URL |
++----------------------------+-------------------------------------------------------------------------+
diff --git a/releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml b/releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
new file mode 100644
index 0000000..09ff15d
--- /dev/null
+++ b/releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+ This release is marking the start of Mitaka release support in tempest
+other:
+ - OpenStack Releases Supported at this time are **Kilo**, **Liberty**,
+ **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/notes/compute-microversion-support-e0b23f960f894b9b.yaml b/releasenotes/notes/compute-microversion-support-e0b23f960f894b9b.yaml
new file mode 100644
index 0000000..de1b35e
--- /dev/null
+++ b/releasenotes/notes/compute-microversion-support-e0b23f960f894b9b.yaml
@@ -0,0 +1,3 @@
+---
+features:
+ - Compute Microversion testing support in Service Clients.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 584540b..5bd9176 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -5,6 +5,7 @@
.. toctree::
:maxdepth: 1
+ v10.0.0
unreleased
Indices and tables
diff --git a/releasenotes/source/v10.0.0.rst b/releasenotes/source/v10.0.0.rst
new file mode 100644
index 0000000..38ed2ef
--- /dev/null
+++ b/releasenotes/source/v10.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v10.0.0 Release Notes
+=====================
+
+.. release-notes:: 10.0.0 Release Notes
+ :version: 10.0.0
diff --git a/tempest/api/compute/admin/test_keypairs_v210.py b/tempest/api/compute/admin/test_keypairs_v210.py
new file mode 100644
index 0000000..dfa7c39
--- /dev/null
+++ b/tempest/api/compute/admin/test_keypairs_v210.py
@@ -0,0 +1,76 @@
+# 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.
+
+from tempest.api.compute.keypairs import base
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class KeyPairsV210TestJSON(base.BaseKeypairTest):
+ credentials = ['primary', 'admin']
+ min_microversion = '2.10'
+
+ @classmethod
+ def setup_clients(cls):
+ super(KeyPairsV210TestJSON, cls).setup_clients()
+ cls.client = cls.os_adm.keypairs_client
+ cls.non_admin_client = cls.os.keypairs_client
+
+ def _create_and_check_keypairs(self, user_id):
+ key_list = list()
+ for i in range(2):
+ k_name = data_utils.rand_name('keypair')
+ keypair = self._create_keypair(k_name,
+ keypair_type='ssh',
+ user_id=user_id)
+ self.assertEqual(k_name, keypair['name'],
+ "The created keypair name is not equal "
+ "to the requested name!")
+ self.assertEqual(user_id, keypair['user_id'],
+ "The created keypair is not for requested user!")
+ keypair.pop('private_key', None)
+ keypair.pop('user_id')
+ key_list.append(keypair)
+ return key_list
+
+ @test.idempotent_id('3c8484af-cfb3-48f6-b8ba-d5d58bbf3eac')
+ def test_admin_manage_keypairs_for_other_users(self):
+ user_id = self.non_admin_client.user_id
+ key_list = self._create_and_check_keypairs(user_id)
+ first_keyname = key_list[0]['name']
+ keypair_detail = self.client.show_keypair(first_keyname,
+ user_id=user_id)['keypair']
+ self.assertEqual(first_keyname, keypair_detail['name'])
+ self.assertEqual(user_id, keypair_detail['user_id'],
+ "The fetched keypair is not for requested user!")
+ # Create a admin keypair
+ admin_k_name = data_utils.rand_name('keypair')
+ admin_keypair = self._create_keypair(admin_k_name, keypair_type='ssh')
+ admin_keypair.pop('private_key', None)
+ admin_keypair.pop('user_id')
+
+ # Admin fetch keypairs list of non admin user
+ keypairs = self.client.list_keypairs(user_id=user_id)['keypairs']
+ fetched_list = [keypair['keypair'] for keypair in keypairs]
+
+ # Check admin keypair is not present in non admin user keypairs list
+ self.assertNotIn(admin_keypair, fetched_list,
+ "The fetched user keypairs has admin keypair!")
+
+ # Now check if all the created keypairs are in the fetched list
+ missing_kps = [kp for kp in key_list if kp not in fetched_list]
+ self.assertFalse(missing_kps,
+ "Failed to find keypairs %s in fetched list"
+ % ', '.join(m_key['name'] for m_key in missing_kps))
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index ead6db3..94635ff 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -81,7 +81,8 @@
body = self.volumes_client.show_volume(volume_id)['volume']
if body['status'] == 'in-use':
self.servers_client.detach_volume(server_id, volume_id)
- self.volumes_client.wait_for_volume_status(volume_id, 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume_id, 'available')
self.volumes_client.delete_volume(volume_id)
def _test_live_migration(self, state='ACTIVE', volume_backed=False):
@@ -152,14 +153,15 @@
volume = self.volumes_client.create_volume(
display_name='test')['volume']
- self.volumes_client.wait_for_volume_status(volume['id'],
- 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume['id'], 'available')
self.addCleanup(self._volume_clean_up, server_id, volume['id'])
# Attach the volume to the server
self.servers_client.attach_volume(server_id, volumeId=volume['id'],
device='/dev/xvdb')
- self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume['id'], 'in-use')
self._migrate_server_to(server_id, target_host)
waiters.wait_for_server_status(self.servers_client,
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 85fd4ab..3d7f4f8 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -16,9 +16,12 @@
from tempest.api.compute.floating_ips import base
from tempest.common.utils import data_utils
from tempest.common import waiters
+from tempest import config
from tempest.lib import exceptions as lib_exc
from tempest import test
+CONF = config.CONF
+
class FloatingIPsTestJSON(base.BaseFloatingIPsTest):
server_id = None
@@ -38,7 +41,8 @@
server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
# Floating IP creation
- body = cls.client.create_floating_ip()['floating_ip']
+ body = cls.client.create_floating_ip(
+ pool=CONF.network.floating_network_name)['floating_ip']
cls.floating_ip_id = body['id']
cls.floating_ip = body['ip']
@@ -62,7 +66,8 @@
def test_allocate_floating_ip(self):
# Positive test:Allocation of a new floating IP to a project
# should be successful
- body = self.client.create_floating_ip()['floating_ip']
+ body = self.client.create_floating_ip(
+ pool=CONF.network.floating_network_name)['floating_ip']
floating_ip_id_allocated = body['id']
self.addCleanup(self.client.delete_floating_ip,
floating_ip_id_allocated)
@@ -78,7 +83,8 @@
# Positive test:Deletion of valid floating IP from project
# should be successful
# Creating the floating IP that is to be deleted in this method
- floating_ip_body = self.client.create_floating_ip()['floating_ip']
+ floating_ip_body = self.client.create_floating_ip(
+ pool=CONF.network.floating_network_name)['floating_ip']
self.addCleanup(self._try_delete_floating_ip, floating_ip_body['id'])
# Deleting the floating IP from the project
self.client.delete_floating_ip(floating_ip_body['id'])
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index d003967..5738293 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -14,8 +14,11 @@
# under the License.
from tempest.api.compute import base
+from tempest import config
from tempest import test
+CONF = config.CONF
+
class FloatingIPDetailsTestJSON(base.BaseV2ComputeTest):
@@ -31,7 +34,8 @@
cls.floating_ip = []
cls.floating_ip_id = []
for i in range(3):
- body = cls.client.create_floating_ip()['floating_ip']
+ body = cls.client.create_floating_ip(
+ pool=CONF.network.floating_network_name)['floating_ip']
cls.floating_ip.append(body)
cls.floating_ip_id.append(body['id'])
@@ -57,7 +61,8 @@
def test_get_floating_ip_details(self):
# Positive test:Should be able to GET the details of floatingIP
# Creating a floating IP for which details are to be checked
- body = self.client.create_floating_ip()['floating_ip']
+ body = self.client.create_floating_ip(
+ pool=CONF.network.floating_network_name)['floating_ip']
floating_ip_id = body['id']
self.addCleanup(self.client.delete_floating_ip,
floating_ip_id)
diff --git a/tempest/api/compute/keypairs/base.py b/tempest/api/compute/keypairs/base.py
index ebfb724..ad7f958 100644
--- a/tempest/api/compute/keypairs/base.py
+++ b/tempest/api/compute/keypairs/base.py
@@ -24,15 +24,21 @@
super(BaseKeypairTest, cls).setup_clients()
cls.client = cls.keypairs_client
- def _delete_keypair(self, keypair_name):
- self.client.delete_keypair(keypair_name)
+ def _delete_keypair(self, keypair_name, **params):
+ self.client.delete_keypair(keypair_name, **params)
- def _create_keypair(self, keypair_name, pub_key=None, keypair_type=None):
+ def _create_keypair(self, keypair_name,
+ pub_key=None, keypair_type=None,
+ user_id=None):
kwargs = {'name': keypair_name}
+ delete_params = {}
if pub_key:
kwargs.update({'public_key': pub_key})
if keypair_type:
kwargs.update({'type': keypair_type})
+ if user_id:
+ kwargs.update({'user_id': user_id})
+ delete_params['user_id'] = user_id
body = self.client.create_keypair(**kwargs)['keypair']
- self.addCleanup(self._delete_keypair, keypair_name)
+ self.addCleanup(self._delete_keypair, keypair_name, **delete_params)
return body
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index f719bfc..87f3c86 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -38,7 +38,6 @@
def setup_clients(cls):
super(ServersTestJSON, cls).setup_clients()
cls.client = cls.servers_client
- cls.network_client = cls.os.network_client
cls.networks_client = cls.os.networks_client
cls.subnets_client = cls.os.subnets_client
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 12b824f..32faf7d 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -41,7 +41,8 @@
super(ServerRescueTestJSON, cls).resource_setup()
# Floating IP creation
- body = cls.floating_ips_client.create_floating_ip()['floating_ip']
+ body = cls.floating_ips_client.create_floating_ip(
+ pool=CONF.network.floating_network_name)['floating_ip']
cls.floating_ip_id = str(body['id']).strip()
cls.floating_ip = str(body['ip']).strip()
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index 2f79d47..e91857a 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -80,7 +80,7 @@
self.assertEqual(key_name, server['key_name'])
def _update_server_name(self, server_id, status, prefix_name='server'):
- # The server name should be changed to the the provided value
+ # The server name should be changed to the provided value
new_name = data_utils.rand_name(prefix_name)
# Update the server with a new name
@@ -95,7 +95,7 @@
@test.idempotent_id('5e6ccff8-349d-4852-a8b3-055df7988dd2')
def test_update_server_name(self):
- # The server name should be changed to the the provided value
+ # The server name should be changed to the provided value
server = self.create_test_server(wait_until='ACTIVE')
# Update instance name with non-ASCII characters
prefix_name = u'\u00CD\u00F1st\u00E1\u00F1c\u00E9'
@@ -103,7 +103,7 @@
@test.idempotent_id('6ac19cb1-27a3-40ec-b350-810bdc04c08e')
def test_update_server_name_in_stop_state(self):
- # The server name should be changed to the the provided value
+ # The server name should be changed to the provided value
server = self.create_test_server(wait_until='ACTIVE')
self.client.stop_server(server['id'])
waiters.wait_for_server_status(self.client, server['id'], 'SHUTOFF')
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
deleted file mode 100644
index 133502c..0000000
--- a/tempest/api/compute/test_authorization.py
+++ /dev/null
@@ -1,451 +0,0 @@
-# Copyright 2012 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.
-
-import six
-
-from oslo_log import log as logging
-
-from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-
-
-class AuthorizationTestJSON(base.BaseV2ComputeTest):
-
- credentials = ['primary', 'alt']
-
- @classmethod
- def skip_checks(cls):
- super(AuthorizationTestJSON, cls).skip_checks()
- if not CONF.service_available.glance:
- raise cls.skipException('Glance is not available.')
-
- @classmethod
- def setup_credentials(cls):
- # No network resources required for this test
- cls.set_network_resources()
- super(AuthorizationTestJSON, cls).setup_credentials()
-
- @classmethod
- def setup_clients(cls):
- super(AuthorizationTestJSON, cls).setup_clients()
- cls.client = cls.os.servers_client
- cls.compute_images_client = cls.os.compute_images_client
- cls.glance_client = cls.os.image_client
- cls.keypairs_client = cls.os.keypairs_client
- cls.security_client = cls.os.compute_security_groups_client
- cls.rule_client = cls.os.compute_security_group_rules_client
-
- cls.alt_client = cls.alt_manager.servers_client
- cls.alt_compute_images_client = cls.alt_manager.compute_images_client
- cls.alt_keypairs_client = cls.alt_manager.keypairs_client
- cls.alt_security_client = (
- cls.alt_manager.compute_security_groups_client)
- cls.alt_rule_client = (
- cls.alt_manager.compute_security_group_rules_client)
-
- @classmethod
- def resource_setup(cls):
- super(AuthorizationTestJSON, cls).resource_setup()
- server = cls.create_test_server(wait_until='ACTIVE')
- cls.server = cls.client.show_server(server['id'])['server']
-
- name = data_utils.rand_name('image')
- body = cls.glance_client.create_image(name=name,
- container_format='bare',
- disk_format='raw',
- is_public=False)['image']
- image_id = body['id']
- image_file = six.StringIO(('*' * 1024))
- body = cls.glance_client.update_image(image_id,
- data=image_file)['image']
- cls.glance_client.wait_for_image_status(image_id, 'active')
- cls.image = cls.compute_images_client.show_image(image_id)['image']
-
- cls.keypairname = data_utils.rand_name('keypair')
- cls.keypairs_client.create_keypair(name=cls.keypairname)
-
- name = data_utils.rand_name('security')
- description = data_utils.rand_name('description')
- cls.security_group = cls.security_client.create_security_group(
- name=name, description=description)['security_group']
-
- parent_group_id = cls.security_group['id']
- ip_protocol = 'tcp'
- from_port = 22
- to_port = 22
- cls.rule = cls.rule_client.create_security_group_rule(
- parent_group_id=parent_group_id, ip_protocol=ip_protocol,
- from_port=from_port, to_port=to_port)['security_group_rule']
-
- @classmethod
- def resource_cleanup(cls):
- if hasattr(cls, 'image'):
- cls.compute_images_client.delete_image(cls.image['id'])
- if hasattr(cls, 'keypairname'):
- cls.keypairs_client.delete_keypair(cls.keypairname)
- if hasattr(cls, 'security_group'):
- cls.security_client.delete_security_group(cls.security_group['id'])
- super(AuthorizationTestJSON, cls).resource_cleanup()
-
- @test.idempotent_id('56816e4a-bd34-47b5-aee9-268c3efeb5d4')
- def test_get_server_for_alt_account_fails(self):
- # A GET request for a server on another user's account should fail
- self.assertRaises(lib_exc.NotFound, self.alt_client.show_server,
- self.server['id'])
-
- @test.idempotent_id('fb8a4870-6d9d-44ad-8375-95d52e98d9f6')
- def test_delete_server_for_alt_account_fails(self):
- # A DELETE request for another user's server should fail
- self.assertRaises(lib_exc.NotFound, self.alt_client.delete_server,
- self.server['id'])
-
- @test.idempotent_id('d792f91f-1d49-4eb5-b1ff-b229c4b9dc64')
- def test_update_server_for_alt_account_fails(self):
- # An update server request for another user's server should fail
- self.assertRaises(lib_exc.NotFound, self.alt_client.update_server,
- self.server['id'], name='test')
-
- @test.idempotent_id('488f24df-d7f7-4207-949a-f17fcb8e8769')
- def test_list_server_addresses_for_alt_account_fails(self):
- # A list addresses request for another user's server should fail
- self.assertRaises(lib_exc.NotFound, self.alt_client.list_addresses,
- self.server['id'])
-
- @test.idempotent_id('00b442d0-2e72-40e7-9b1f-31772e36da01')
- def test_list_server_addresses_by_network_for_alt_account_fails(self):
- # A list address/network request for another user's server should fail
- server_id = self.server['id']
- self.assertRaises(lib_exc.NotFound,
- self.alt_client.list_addresses_by_network, server_id,
- 'public')
-
- @test.idempotent_id('cc90b35a-19f0-45d2-b680-2aabf934aa22')
- def test_list_servers_with_alternate_tenant(self):
- # A list on servers from one tenant should not
- # show on alternate tenant
- # Listing servers from alternate tenant
- alt_server_ids = []
- body = self.alt_client.list_servers()
- alt_server_ids = [s['id'] for s in body['servers']]
- self.assertNotIn(self.server['id'], alt_server_ids)
-
- @test.idempotent_id('376dbc16-0779-4384-a723-752774799641')
- def test_change_password_for_alt_account_fails(self):
- # A change password request for another user's server should fail
- self.assertRaises(lib_exc.NotFound, self.alt_client.change_password,
- self.server['id'], adminPass='newpass')
-
- @test.idempotent_id('14cb5ff5-f646-45ca-8f51-09081d6c0c24')
- def test_reboot_server_for_alt_account_fails(self):
- # A reboot request for another user's server should fail
- self.assertRaises(lib_exc.NotFound, self.alt_client.reboot_server,
- self.server['id'], type='HARD')
-
- @test.idempotent_id('8a0bce51-cd00-480b-88ba-dbc7d8408a37')
- def test_rebuild_server_for_alt_account_fails(self):
- # A rebuild request for another user's server should fail
- self.assertRaises(lib_exc.NotFound, self.alt_client.rebuild_server,
- self.server['id'], self.image_ref_alt)
-
- @test.idempotent_id('e4da647e-f982-4e61-9dad-1d1abebfb933')
- def test_resize_server_for_alt_account_fails(self):
- # A resize request for another user's server should fail
- self.assertRaises(lib_exc.NotFound, self.alt_client.resize_server,
- self.server['id'], self.flavor_ref_alt)
-
- @test.idempotent_id('a9fe8112-0ffa-4902-b061-f892bd5fe0d3')
- def test_create_image_for_alt_account_fails(self):
- # A create image request for another user's server should fail
- self.assertRaises(lib_exc.NotFound,
- self.alt_compute_images_client.create_image,
- self.server['id'], name='testImage')
-
- @test.idempotent_id('95d445f6-babc-4f2e-aea3-aa24ec5e7f0d')
- def test_create_server_with_unauthorized_image(self):
- # Server creation with another user's image should fail
- self.assertRaises(lib_exc.BadRequest, self.alt_client.create_server,
- name='test', imageRef=self.image['id'],
- flavorRef=self.flavor_ref)
-
- @test.idempotent_id('acf8724b-142b-4044-82c3-78d31a533f24')
- def test_create_server_fails_when_tenant_incorrect(self):
- # BUG(sdague): this test should fail because of bad auth url,
- # which means that when we run with a service catalog without
- # project_id in the urls, it should fail to fail, and thus
- # fail the test. It does not.
- #
- # The 400 BadRequest is clearly ambiguous, and something else
- # is wrong about this request. This should be fixed.
- #
- # A create server request should fail if the tenant id does not match
- # the current user
- # Change the base URL to impersonate another user
- self.alt_client.auth_provider.set_alt_auth_data(
- request_part='url',
- auth_data=self.client.auth_provider.auth_data
- )
- self.assertRaises(lib_exc.BadRequest,
- self.alt_client.create_server, name='test',
- imageRef=self.image['id'], flavorRef=self.flavor_ref)
-
- @test.idempotent_id('f03d1ded-7fd4-4d29-bc13-e2391f29c625')
- def test_create_keypair_in_analt_user_tenant(self):
- """create keypair should not function for alternate tenant
-
- POST {alt_service_url}/os-keypairs
-
- Attempt to create a keypair against an alternate tenant by
- changing using a different tenant's service url. This should
- return a BadRequest. This tests basic tenant isolation protections.
-
- NOTE(sdague): if the environment does not use project_id in
- the service urls, this test is not valid. Skip under these
- conditions.
-
- """
- if self.alt_keypairs_client.base_url == self.keypairs_client.base_url:
- raise self.skipException("Service urls don't include project_id")
-
- k_name = data_utils.rand_name('keypair')
- try:
- # Change the base URL to impersonate another user
- self.alt_keypairs_client.auth_provider.set_alt_auth_data(
- request_part='url',
- auth_data=self.keypairs_client.auth_provider.auth_data
- )
- resp = {}
- resp['status'] = None
- self.assertRaises(lib_exc.BadRequest,
- self.alt_keypairs_client.create_keypair,
- name=k_name)
- finally:
- # Next request the base_url is back to normal
- if (resp['status'] is not None):
- self.alt_keypairs_client.delete_keypair(k_name)
- LOG.error("Create keypair request should not happen "
- "if the tenant id does not match the current user")
-
- @test.idempotent_id('85bcdd8f-56b4-4868-ae56-63fbf6f7e405')
- def test_get_keypair_of_alt_account_fails(self):
- # A GET request for another user's keypair should fail
- self.assertRaises(lib_exc.NotFound,
- self.alt_keypairs_client.show_keypair,
- self.keypairname)
-
- @test.idempotent_id('6d841683-a8e0-43da-a1b8-b339f7692b61')
- def test_delete_keypair_of_alt_account_fails(self):
- # A DELETE request for another user's keypair should fail
- self.assertRaises(lib_exc.NotFound,
- self.alt_keypairs_client.delete_keypair,
- self.keypairname)
-
- @test.idempotent_id('fcb2e144-36e3-4dfb-9f9f-e72fcdec5656')
- def test_get_image_for_alt_account_fails(self):
- # A GET request for an image on another user's account should fail
- self.assertRaises(lib_exc.NotFound,
- self.alt_compute_images_client.show_image,
- self.image['id'])
-
- @test.idempotent_id('9facb962-f043-4a9d-b9ee-166a32dea098')
- def test_delete_image_for_alt_account_fails(self):
- # A DELETE request for another user's image should fail
- self.assertRaises(lib_exc.NotFound,
- self.alt_compute_images_client.delete_image,
- self.image['id'])
-
- @test.idempotent_id('752c917e-83be-499d-a422-3559127f7d3c')
- def test_create_security_group_in_analt_user_tenant(self):
- """create security group should not function for alternate tenant
-
- POST {alt_service_url}/os-security-groups
-
- Attempt to create a security group against an alternate tenant
- by changing using a different tenant's service url. This
- should return a BadRequest. This tests basic tenant isolation
- protections.
-
- NOTE(sdague): if the environment does not use project_id in
- the service urls, this test is not valid. Skip under these
- conditions.
-
- """
- if self.alt_security_client.base_url == self.security_client.base_url:
- raise self.skipException("Service urls don't include project_id")
-
- s_name = data_utils.rand_name('security')
- s_description = data_utils.rand_name('security')
- try:
- # Change the base URL to impersonate another user
- self.alt_security_client.auth_provider.set_alt_auth_data(
- request_part='url',
- auth_data=self.security_client.auth_provider.auth_data
- )
- resp = {}
- resp['status'] = None
- self.assertRaises(lib_exc.BadRequest,
- self.alt_security_client.create_security_group,
- name=s_name, description=s_description)
- finally:
- # Next request the base_url is back to normal
- if resp['status'] is not None:
- self.alt_security_client.delete_security_group(resp['id'])
- LOG.error("Create Security Group request should not happen if"
- "the tenant id does not match the current user")
-
- @test.idempotent_id('9db3590f-4d15-4e5f-985e-b28514919a6f')
- def test_get_security_group_of_alt_account_fails(self):
- # A GET request for another user's security group should fail
- self.assertRaises(lib_exc.NotFound,
- self.alt_security_client.show_security_group,
- self.security_group['id'])
-
- @test.idempotent_id('155387a5-2bbc-4acf-ab06-698dae537ea5')
- def test_delete_security_group_of_alt_account_fails(self):
- # A DELETE request for another user's security group should fail
- self.assertRaises(lib_exc.NotFound,
- self.alt_security_client.delete_security_group,
- self.security_group['id'])
-
- @test.idempotent_id('b2b76de0-210a-4089-b921-591c9ec552f6')
- def test_create_security_group_rule_in_analt_user_tenant(self):
- """create security group rule should not function for alternate tenant
-
- POST {alt_service_url}/os-security-group-rules
-
- Attempt to create a security group rule against an alternate
- tenant by changing using a different tenant's service
- url. This should return a BadRequest. This tests basic tenant
- isolation protections.
-
- NOTE(sdague): if the environment does not use project_id in
- the service urls, this test is not valid. Skip under these
- conditions.
-
- """
- if self.alt_security_client.base_url == self.security_client.base_url:
- raise self.skipException("Service urls don't include project_id")
-
- parent_group_id = self.security_group['id']
- ip_protocol = 'icmp'
- from_port = -1
- to_port = -1
- try:
- # Change the base URL to impersonate another user
- self.alt_rule_client.auth_provider.set_alt_auth_data(
- request_part='url',
- auth_data=self.rule_client.auth_provider.auth_data
- )
- resp = {}
- resp['status'] = None
- self.assertRaises(lib_exc.BadRequest,
- self.alt_rule_client.
- create_security_group_rule,
- parent_group_id=parent_group_id,
- ip_protocol=ip_protocol,
- from_port=from_port, to_port=to_port)
- finally:
- # Next request the base_url is back to normal
- if resp['status'] is not None:
- self.alt_rule_client.delete_security_group_rule(resp['id'])
- LOG.error("Create security group rule request should not "
- "happen if the tenant id does not match the"
- " current user")
-
- @test.idempotent_id('c6044177-37ef-4ce4-b12c-270ddf26d7da')
- def test_delete_security_group_rule_of_alt_account_fails(self):
- # A DELETE request for another user's security group rule
- # should fail
- self.assertRaises(lib_exc.NotFound,
- self.alt_rule_client.delete_security_group_rule,
- self.rule['id'])
-
- @test.idempotent_id('c5f52351-53d9-4fc9-83e5-917f7f5e3d71')
- def test_set_metadata_of_alt_account_server_fails(self):
- # A set metadata for another user's server should fail
- req_metadata = {'meta1': 'data1', 'meta2': 'data2'}
- self.assertRaises(lib_exc.NotFound,
- self.alt_client.set_server_metadata,
- self.server['id'],
- req_metadata)
-
- @test.idempotent_id('fb6f51e9-df15-4939-898d-1aca38c258f0')
- def test_set_metadata_of_alt_account_image_fails(self):
- # A set metadata for another user's image should fail
- req_metadata = {'meta1': 'value1', 'meta2': 'value2'}
- self.assertRaises(lib_exc.NotFound,
- self.alt_compute_images_client.set_image_metadata,
- self.image['id'], req_metadata)
-
- @test.idempotent_id('dea1936a-473d-49f2-92ad-97bb7aded22e')
- def test_get_metadata_of_alt_account_server_fails(self):
- # A get metadata for another user's server should fail
- req_metadata = {'meta1': 'data1'}
- self.client.set_server_metadata(self.server['id'], req_metadata)
- self.addCleanup(self.client.delete_server_metadata_item,
- self.server['id'], 'meta1')
- self.assertRaises(lib_exc.NotFound,
- self.alt_client.show_server_metadata_item,
- self.server['id'], 'meta1')
-
- @test.idempotent_id('16b2d724-0d3b-4216-a9fa-97bd4d9cf670')
- def test_get_metadata_of_alt_account_image_fails(self):
- # A get metadata for another user's image should fail
- req_metadata = {'meta1': 'value1'}
- self.addCleanup(self.compute_images_client.delete_image_metadata_item,
- self.image['id'], 'meta1')
- self.compute_images_client.set_image_metadata(self.image['id'],
- req_metadata)
- self.assertRaises(
- lib_exc.NotFound,
- self.alt_compute_images_client.show_image_metadata_item,
- self.image['id'], 'meta1')
-
- @test.idempotent_id('79531e2e-e721-493c-8b30-a35db36fdaa6')
- def test_delete_metadata_of_alt_account_server_fails(self):
- # A delete metadata for another user's server should fail
- req_metadata = {'meta1': 'data1'}
- self.addCleanup(self.client.delete_server_metadata_item,
- self.server['id'], 'meta1')
- self.client.set_server_metadata(self.server['id'], req_metadata)
- self.assertRaises(lib_exc.NotFound,
- self.alt_client.delete_server_metadata_item,
- self.server['id'], 'meta1')
-
- @test.idempotent_id('a5175dcf-cef8-43d6-9b77-3cb707d62e94')
- def test_delete_metadata_of_alt_account_image_fails(self):
- # A delete metadata for another user's image should fail
- req_metadata = {'meta1': 'data1'}
- self.addCleanup(self.compute_images_client.delete_image_metadata_item,
- self.image['id'], 'meta1')
- self.compute_images_client.set_image_metadata(self.image['id'],
- req_metadata)
- self.assertRaises(
- lib_exc.NotFound,
- self.alt_compute_images_client.delete_image_metadata_item,
- self.image['id'], 'meta1')
-
- @test.idempotent_id('b0c1e7a0-8853-40fd-8384-01f93d116cae')
- def test_get_console_output_of_alt_account_server_fails(self):
- # A Get Console Output for another user's server should fail
- self.assertRaises(lib_exc.NotFound,
- self.alt_client.get_console_output,
- self.server['id'], length=10)
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 43f4c97..122e7cc 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -79,8 +79,8 @@
@test.idempotent_id('cd65d997-f7e4-4966-a7e9-d5001b674fdc')
def test_compare_tenant_quotas_with_default_quotas(self):
# Tenants are created with the default quota values
- defualt_quota_set = \
+ default_quota_set = \
self.client.show_default_quota_set(self.tenant_id)['quota_set']
tenant_quota_set = (self.client.show_quota_set(self.tenant_id)
['quota_set'])
- self.assertEqual(defualt_quota_set, tenant_quota_set)
+ self.assertEqual(default_quota_set, tenant_quota_set)
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 01a8e58..e9c8e30 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -52,7 +52,8 @@
def _detach(self, server_id, volume_id):
if self.attachment:
self.servers_client.detach_volume(server_id, volume_id)
- self.volumes_client.wait_for_volume_status(volume_id, 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume_id, 'available')
def _delete_volume(self):
# Delete the created Volumes
@@ -77,15 +78,16 @@
self.volume = self.volumes_client.create_volume(
size=CONF.volume.volume_size, display_name='test')['volume']
self.addCleanup(self._delete_volume)
- self.volumes_client.wait_for_volume_status(self.volume['id'],
- 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ self.volume['id'], 'available')
# Attach the volume to the server
self.attachment = self.servers_client.attach_volume(
self.server['id'],
volumeId=self.volume['id'],
device='/dev/%s' % self.device)['volumeAttachment']
- self.volumes_client.wait_for_volume_status(self.volume['id'], 'in-use')
+ waiters.wait_for_volume_status(self.volumes_client,
+ self.volume['id'], 'in-use')
self.addCleanup(self._detach, self.server['id'], self.volume['id'])
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
index f75b867..bdb4383 100644
--- a/tempest/api/database/flavors/test_flavors.py
+++ b/tempest/api/database/flavors/test_flavors.py
@@ -14,6 +14,7 @@
# under the License.
from tempest.api.database import base
+from tempest.lib import decorators
from tempest import test
@@ -58,6 +59,7 @@
@test.attr(type='smoke')
@test.idempotent_id('afb2667f-4ec2-4925-bcb7-313fdcffb80d')
@test.services('compute')
+ @decorators.skip_because(bug='1567134')
def test_compare_db_flavors_with_os(self):
db_flavors = self.client.list_db_flavors()['flavors']
os_flavors = (self.os_flavors_client.list_flavors(detail=True)
diff --git a/tempest/api/network/admin/test_dhcp_agent_scheduler.py b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
index fcb6fce..d2ab237 100644
--- a/tempest/api/network/admin/test_dhcp_agent_scheduler.py
+++ b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
@@ -36,12 +36,12 @@
@test.idempotent_id('5032b1fe-eb42-4a64-8f3b-6e189d8b5c7d')
def test_list_dhcp_agent_hosting_network(self):
- self.admin_client.list_dhcp_agent_hosting_network(
+ self.admin_networks_client.list_dhcp_agents_on_hosting_network(
self.network['id'])
@test.idempotent_id('30c48f98-e45d-4ffb-841c-b8aad57c7587')
def test_list_networks_hosted_by_one_dhcp(self):
- body = self.admin_client.list_dhcp_agent_hosting_network(
+ body = self.admin_networks_client.list_dhcp_agents_on_hosting_network(
self.network['id'])
agents = body['agents']
self.assertIsNotNone(agents)
diff --git a/tempest/api/network/admin/test_floating_ips_admin_actions.py b/tempest/api/network/admin/test_floating_ips_admin_actions.py
index 6ad374b..baeaa0c 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -28,7 +28,6 @@
@classmethod
def setup_clients(cls):
super(FloatingIPAdminTestJSON, cls).setup_clients()
- cls.alt_client = cls.alt_manager.network_client
cls.alt_floating_ips_client = cls.alt_manager.floating_ips_client
@classmethod
@@ -68,7 +67,7 @@
body = self.floating_ips_client.list_floatingips()
floating_ip_ids = [f['id'] for f in body['floatingips']]
# Check that nonadmin user doesn't see floating ip created from admin
- # and floating ip that is created in another tenant (alt user)
+ # and floating ip that is created in another project (alt user)
self.assertIn(self.floating_ip['id'], floating_ip_ids)
self.assertNotIn(floating_ip_admin['floatingip']['id'],
floating_ip_ids)
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index c1cdbf2..7b037d5 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -24,7 +24,7 @@
set network quota and exceed this quota
v2.0 of the API is assumed.
- It is also assumed that the per-tenant quota extension API is configured
+ It is also assumed that the per-project quota extension API is configured
in /etc/neutron/neutron.conf as follows:
quota_driver = neutron.db.quota_db.DbQuotaDriver
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index d72e960..ea3d59a 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -24,13 +24,13 @@
class QuotasTest(base.BaseAdminNetworkTest):
"""Tests the following operations in the Neutron API:
- list quotas for tenants who have non-default quota values
- show quotas for a specified tenant
- update quotas for a specified tenant
- reset quotas to default values for a specified tenant
+ list quotas for projects who have non-default quota values
+ show quotas for a specified project
+ update quotas for a specified project
+ reset quotas to default values for a specified project
v2.0 of the API is assumed.
- It is also assumed that the per-tenant quota extension API is configured
+ It is also assumed that the per-project quota extension API is configured
in /etc/neutron/neutron.conf as follows:
quota_driver = neutron.db.quota_db.DbQuotaDriver
@@ -49,7 +49,7 @@
cls.identity_admin_client = cls.os_adm.identity_client
def _check_quotas(self, new_quotas):
- # Add a tenant to conduct the test
+ # Add a project to conduct the test
project = data_utils.rand_name('test_project_')
description = data_utils.rand_name('desc_')
project = self.identity_utils.create_project(name=project,
@@ -57,14 +57,14 @@
project_id = project['id']
self.addCleanup(self.identity_utils.delete_project, project_id)
- # Change quotas for tenant
+ # Change quotas for project
quota_set = self.admin_quotas_client.update_quotas(
project_id, **new_quotas)['quota']
self.addCleanup(self._cleanup_quotas, project_id)
for key, value in six.iteritems(new_quotas):
self.assertEqual(value, quota_set[key])
- # Confirm our tenant is listed among tenants with non default quotas
+ # Confirm our project is listed among projects with non default quotas
non_default_quotas = self.admin_quotas_client.list_quotas()
found = False
for qs in non_default_quotas['quotas']:
@@ -72,7 +72,7 @@
found = True
self.assertTrue(found)
- # Confirm from API quotas were changed as requested for tenant
+ # Confirm from API quotas were changed as requested for project
quota_set = self.admin_quotas_client.show_quotas(project_id)
quota_set = quota_set['quota']
for key, value in six.iteritems(new_quotas):
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index d78fc04..9823345 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -32,11 +32,11 @@
Therefore, v2.x of the Neutron API is assumed. It is also assumed that the
following options are defined in the [network] section of etc/tempest.conf:
- tenant_network_cidr with a block of cidr's from which smaller blocks
- can be allocated for tenant networks
+ project_network_cidr with a block of cidr's from which smaller blocks
+ can be allocated for project networks
- tenant_network_mask_bits with the mask bits to be used to partition the
- block defined by tenant-network_cidr
+ project_network_mask_bits with the mask bits to be used to partition
+ the block defined by project-network_cidr
Finally, it is assumed that the following option is defined in the
[service_available] section of etc/tempest.conf
@@ -67,7 +67,6 @@
@classmethod
def setup_clients(cls):
super(BaseNetworkTest, cls).setup_clients()
- cls.client = cls.os.network_client
cls.agents_client = cls.os.network_agents_client
cls.network_extensions_client = cls.os.network_extensions_client
cls.networks_client = cls.os.networks_client
@@ -176,12 +175,12 @@
ip_version = ip_version if ip_version is not None else cls._ip_version
gateway_not_set = gateway == ''
if ip_version == 4:
- cidr = cidr or netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = mask_bits or CONF.network.tenant_network_mask_bits
+ cidr = cidr or netaddr.IPNetwork(CONF.network.project_network_cidr)
+ mask_bits = mask_bits or CONF.network.project_network_mask_bits
elif ip_version == 6:
- cidr = (
- cidr or netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr))
- mask_bits = mask_bits or CONF.network.tenant_network_v6_mask_bits
+ cidr = (cidr or
+ netaddr.IPNetwork(CONF.network.project_network_v6_cidr))
+ mask_bits = mask_bits or CONF.network.project_network_v6_mask_bits
# Find a cidr that is not in use yet and create a subnet with it
for subnet_cidr in cidr.subnet(mask_bits):
if gateway_not_set:
@@ -257,7 +256,7 @@
@classmethod
def delete_router(cls, router):
- body = cls.client.list_router_interfaces(router['id'])
+ body = cls.ports_client.list_ports(device_id=router['id'])
interfaces = body['ports']
for i in interfaces:
try:
@@ -276,7 +275,6 @@
@classmethod
def setup_clients(cls):
super(BaseAdminNetworkTest, cls).setup_clients()
- cls.admin_client = cls.os_adm.network_client
cls.admin_agents_client = cls.os_adm.network_agents_client
cls.admin_networks_client = cls.os_adm.networks_client
cls.admin_routers_client = cls.os_adm.routers_client
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index 394aec1..b2892e5 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -100,7 +100,7 @@
@test.idempotent_id('4d6d178f-34f6-4bff-a01c-0a2f8fe909e4')
def test_update_port_with_cidr_address_pair(self):
# Update allowed address pair with cidr
- cidr = str(netaddr.IPNetwork(CONF.network.tenant_network_cidr))
+ cidr = str(netaddr.IPNetwork(CONF.network.project_network_cidr))
self._update_port_with_address(cidr)
@test.idempotent_id('b3f20091-6cd5-472b-8487-3516137df933')
diff --git a/tempest/api/network/test_extensions.py b/tempest/api/network/test_extensions.py
index d71d600..2c981a1 100644
--- a/tempest/api/network/test_extensions.py
+++ b/tempest/api/network/test_extensions.py
@@ -31,7 +31,7 @@
@test.attr(type='smoke')
@test.idempotent_id('ef28c7e6-e646-4979-9d67-deb207bc5564')
def test_list_show_extensions(self):
- # List available extensions for the tenant
+ # List available extensions for the project
expected_alias = ['security-group', 'l3_agent_scheduler',
'ext-gw-mode', 'binding', 'quotas',
'agent', 'dhcp_agent_scheduler', 'provider',
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index fa1ed6a..b6f9da7 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -29,12 +29,12 @@
class NetworksTest(base.BaseNetworkTest):
"""Tests the following operations in the Neutron API:
- create a network for a tenant
- list tenant's networks
- show a tenant network details
- create a subnet for a tenant
- list tenant's subnets
- show a tenant subnet details
+ create a network for a project
+ list project's networks
+ show a project network details
+ create a subnet for a project
+ list project's subnets
+ show a project subnet details
network update
subnet update
delete a network also deletes its subnets
@@ -45,15 +45,15 @@
v2.0 of the Neutron API is assumed. It is also assumed that the following
options are defined in the [network] section of etc/tempest.conf:
- tenant_network_cidr with a block of cidr's from which smaller blocks
- can be allocated for tenant ipv4 subnets
+ project_network_cidr with a block of cidr's from which smaller blocks
+ can be allocated for project ipv4 subnets
- tenant_network_v6_cidr is the equivalent for ipv6 subnets
+ project_network_v6_cidr is the equivalent for ipv6 subnets
- tenant_network_mask_bits with the mask bits to be used to partition the
- block defined by tenant_network_cidr
+ project_network_mask_bits with the mask bits to be used to partition
+ the block defined by project_network_cidr
- tenant_network_v6_mask_bits is the equivalent for ipv6 subnets
+ project_network_v6_mask_bits is the equivalent for ipv6 subnets
"""
@classmethod
@@ -92,14 +92,14 @@
@classmethod
def _create_subnet_with_last_subnet_block(cls, network, ip_version):
- # Derive last subnet CIDR block from tenant CIDR and
+ # Derive last subnet CIDR block from project CIDR and
# create the subnet with that derived CIDR
if ip_version == 4:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = CONF.network.tenant_network_mask_bits
+ cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+ mask_bits = CONF.network.project_network_mask_bits
elif ip_version == 6:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
- mask_bits = CONF.network.tenant_network_v6_mask_bits
+ cidr = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
+ mask_bits = CONF.network.project_network_v6_mask_bits
subnet_cidr = list(cidr.subnet(mask_bits))[-1]
gateway_ip = str(netaddr.IPAddress(subnet_cidr) + 1)
@@ -110,11 +110,11 @@
def _get_gateway_from_tempest_conf(cls, ip_version):
"""Return first subnet gateway for configured CIDR """
if ip_version == 4:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = CONF.network.tenant_network_mask_bits
+ cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+ mask_bits = CONF.network.project_network_mask_bits
elif ip_version == 6:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
- mask_bits = CONF.network.tenant_network_v6_mask_bits
+ cidr = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
+ mask_bits = CONF.network.project_network_v6_mask_bits
if mask_bits >= cidr.prefixlen:
return netaddr.IPAddress(cidr) + 1
@@ -403,16 +403,16 @@
bulk network creation
bulk subnet creation
bulk port creation
- list tenant's networks
+ list project's networks
v2.0 of the Neutron API is assumed. It is also assumed that the following
options are defined in the [network] section of etc/tempest.conf:
- tenant_network_cidr with a block of cidr's from which smaller blocks
- can be allocated for tenant networks
+ project_network_cidr with a block of cidr's from which smaller blocks
+ can be allocated for project networks
- tenant_network_mask_bits with the mask bits to be used to partition the
- block defined by tenant-network_cidr
+ project_network_mask_bits with the mask bits to be used to partition
+ the block defined by project-network_cidr
"""
def _delete_networks(self, created_networks):
@@ -464,11 +464,11 @@
networks = [self.create_network(), self.create_network()]
# Creates 2 subnets in one request
if self._ip_version == 4:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = CONF.network.tenant_network_mask_bits
+ cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+ mask_bits = CONF.network.project_network_mask_bits
else:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
- mask_bits = CONF.network.tenant_network_v6_mask_bits
+ cidr = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
+ mask_bits = CONF.network.project_network_v6_mask_bits
cidrs = [subnet_cidr for subnet_cidr in cidr.subnet(mask_bits)]
@@ -529,7 +529,7 @@
@test.idempotent_id('e41a4888-65a6-418c-a095-f7c2ef4ad59a')
def test_create_delete_subnet_with_gw(self):
- net = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
+ net = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
gateway = str(netaddr.IPAddress(net.first + 2))
name = data_utils.rand_name('network-')
network = self.create_network(network_name=name)
@@ -539,7 +539,7 @@
@test.idempotent_id('ebb4fd95-524f-46af-83c1-0305b239338f')
def test_create_delete_subnet_with_default_gw(self):
- net = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
+ net = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
gateway_ip = str(netaddr.IPAddress(net.first + 1))
name = data_utils.rand_name('network-')
network = self.create_network(network_name=name)
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 5ff23c6..caf7f14 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -90,12 +90,12 @@
def _get_ipaddress_from_tempest_conf(cls):
"""Return subnet with mask bits for configured CIDR """
if cls._ip_version == 4:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- cidr.prefixlen = CONF.network.tenant_network_mask_bits
+ cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+ cidr.prefixlen = CONF.network.project_network_mask_bits
elif cls._ip_version == 6:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
- cidr.prefixlen = CONF.network.tenant_network_v6_mask_bits
+ cidr = netaddr.IPNetwork(CONF.network.project_network_v6_cidr)
+ cidr.prefixlen = CONF.network.project_network_v6_mask_bits
return cidr
@@ -131,12 +131,15 @@
body = self.ports_client.show_port(self.port['id'])
port = body['port']
self.assertIn('id', port)
- # TODO(Santosh)- This is a temporary workaround to compare create_port
- # and show_port dict elements.Remove this once extra_dhcp_opts issue
- # gets fixed in neutron.( bug - 1365341.)
+ # NOTE(rfolco): created_at and updated_at may get inconsistent values
+ # due to possible delay between POST request and resource creation.
+ # TODO(rfolco): Neutron Bug #1365341 is fixed, can remove the key
+ # extra_dhcp_opts in the O release (K/L gate jobs still need it).
self.assertThat(self.port,
custom_matchers.MatchesDictExceptForKeys
- (port, excluded_keys=['extra_dhcp_opts']))
+ (port, excluded_keys=['extra_dhcp_opts',
+ 'created_at',
+ 'updated_at']))
@test.idempotent_id('45fcdaf2-dab0-4c13-ac6c-fcddfb579dbd')
def test_show_port_fields(self):
@@ -425,11 +428,7 @@
class PortsIpV6TestJSON(PortsTestJSON):
_ip_version = 6
- _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
- _tenant_network_mask_bits = CONF.network.tenant_network_v6_mask_bits
class PortsAdminExtendedAttrsIpV6TestJSON(PortsAdminExtendedAttrsTestJSON):
_ip_version = 6
- _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
- _tenant_network_mask_bits = CONF.network.tenant_network_v6_mask_bits
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 11f7fc6..3654b2e 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -41,9 +41,9 @@
@classmethod
def resource_setup(cls):
super(RoutersTest, cls).resource_setup()
- cls.tenant_cidr = (CONF.network.tenant_network_cidr
+ cls.tenant_cidr = (CONF.network.project_network_cidr
if cls._ip_version == 4 else
- CONF.network.tenant_network_v6_cidr)
+ CONF.network.project_network_v6_cidr)
@test.attr(type='smoke')
@test.idempotent_id('f64403e2-8483-4b34-8ccd-b09a87bcc68c')
@@ -274,7 +274,7 @@
@test.requires_ext(extension='extraroute', service='network')
def test_update_delete_extra_route(self):
# Create different cidr for each subnet to avoid cidr duplicate
- # The cidr starts from tenant_cidr
+ # The cidr starts from project_cidr
next_cidr = netaddr.IPNetwork(self.tenant_cidr)
# Prepare to build several routes
test_routes = []
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index 36aaf2d..cd9f6ad 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -39,9 +39,9 @@
cls.router = cls.create_router(data_utils.rand_name('router-'))
cls.network = cls.create_network()
cls.subnet = cls.create_subnet(cls.network)
- cls.tenant_cidr = (CONF.network.tenant_network_cidr
+ cls.tenant_cidr = (CONF.network.project_network_cidr
if cls._ip_version == 4 else
- CONF.network.tenant_network_v6_cidr)
+ CONF.network.project_network_v6_cidr)
@test.attr(type=['negative'])
@test.idempotent_id('37a94fc0-a834-45b9-bd23-9a81d2fd1e22')
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index 7d0765e..5312979 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -24,7 +24,7 @@
class SecGroupTest(base.BaseSecGroupTest):
- _tenant_network_cidr = CONF.network.tenant_network_cidr
+ _project_network_cidr = CONF.network.project_network_cidr
@classmethod
def skip_checks(cls):
@@ -71,7 +71,7 @@
@test.attr(type='smoke')
@test.idempotent_id('e30abd17-fef9-4739-8617-dc26da88e686')
def test_list_security_groups(self):
- # Verify the that security group belonging to tenant exist in list
+ # Verify the that security group belonging to project exist in list
body = self.security_groups_client.list_security_groups()
security_groups = body['security_groups']
found = None
@@ -210,7 +210,7 @@
protocol = 'tcp'
port_range_min = 76
port_range_max = 77
- ip_prefix = self._tenant_network_cidr
+ ip_prefix = self._project_network_cidr
self._create_verify_security_group_rule(sg_id, direction,
self.ethertype, protocol,
port_range_min,
@@ -239,4 +239,4 @@
class SecGroupIPv6Test(SecGroupTest):
_ip_version = 6
- _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+ _project_network_cidr = CONF.network.project_network_v6_cidr
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index 401fa3b..86d0b46 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -24,7 +24,7 @@
class NegativeSecGroupTest(base.BaseSecGroupTest):
- _tenant_network_cidr = CONF.network.tenant_network_cidr
+ _project_network_cidr = CONF.network.project_network_cidr
@classmethod
def skip_checks(cls):
@@ -110,7 +110,7 @@
sg2_body, _ = self._create_security_group()
# Create rule specifying both remote_ip_prefix and remote_group_id
- prefix = self._tenant_network_cidr
+ prefix = self._project_network_cidr
self.assertRaises(
lib_exc.BadRequest,
self.security_group_rules_client.create_security_group_rule,
@@ -214,7 +214,7 @@
class NegativeSecGroupIPv6Test(NegativeSecGroupTest):
_ip_version = 6
- _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+ _project_network_cidr = CONF.network.project_network_v6_cidr
@test.attr(type=['negative'])
@test.idempotent_id('7607439c-af73-499e-bf64-f687fd12a842')
@@ -223,9 +223,9 @@
# Create rule with bad remote_ip_prefix
pairs = ({'ethertype': 'IPv6',
- 'ip_prefix': CONF.network.tenant_network_cidr},
+ 'ip_prefix': CONF.network.project_network_cidr},
{'ethertype': 'IPv4',
- 'ip_prefix': CONF.network.tenant_network_v6_cidr})
+ 'ip_prefix': CONF.network.project_network_v6_cidr})
for pair in pairs:
self.assertRaisesRegex(
lib_exc.BadRequest,
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 26ace81..d813263 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -46,7 +46,6 @@
cls.client = cls.orchestration_client
cls.servers_client = cls.os.servers_client
cls.keypairs_client = cls.os.keypairs_client
- cls.network_client = cls.os.network_client
cls.networks_client = cls.os.networks_client
cls.volumes_client = cls.os.volumes_client
cls.images_v2_client = cls.os.image_client_v2
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index 5483361..3f45634 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -44,7 +44,6 @@
@classmethod
def setup_clients(cls):
super(NeutronResourcesTestJSON, cls).setup_clients()
- cls.network_client = cls.os.network_client
cls.subnets_client = cls.os.subnets_client
cls.ports_client = cls.os.ports_client
@@ -58,8 +57,8 @@
cls._create_keypair()['name'])
cls.external_network_id = CONF.network.public_network_id
- tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = CONF.network.tenant_network_mask_bits
+ tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+ mask_bits = CONF.network.project_network_mask_bits
cls.subnet_cidr = tenant_cidr.subnet(mask_bits).next()
# create the stack
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 60e6e6c..f19717e 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -13,6 +13,7 @@
import six
from tempest.api.volume import base
from tempest.common.utils import data_utils
+from tempest.common import waiters
from tempest import config
from tempest import test
@@ -80,8 +81,8 @@
else:
self.volume_id_list_without_prefix.append(
self.volume['id'])
- self.admin_volume_client.wait_for_volume_status(
- self.volume['id'], 'available')
+ waiters.wait_for_volume_status(self.admin_volume_client,
+ self.volume['id'], 'available')
@classmethod
def resource_cleanup(cls):
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index f2bf613..26a5a45 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -15,6 +15,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
+from tempest.common import waiters
from tempest import config
from tempest import test
@@ -41,18 +42,17 @@
vol_name = data_utils.rand_name(cls.__name__ + '-Volume')
cls.name_field = cls.special_fields['name_field']
params = {cls.name_field: vol_name}
- cls.volume = \
- cls.volumes_client.create_volume(**params)['volume']
- cls.volumes_client.wait_for_volume_status(cls.volume['id'],
- 'available')
+ cls.volume = cls.volumes_client.create_volume(**params)['volume']
+ waiters.wait_for_volume_status(cls.volumes_client,
+ cls.volume['id'], 'available')
# Create a test shared snapshot for tests
snap_name = data_utils.rand_name(cls.__name__ + '-Snapshot')
params = {cls.name_field: snap_name}
cls.snapshot = cls.client.create_snapshot(
volume_id=cls.volume['id'], **params)['snapshot']
- cls.client.wait_for_snapshot_status(cls.snapshot['id'],
- 'available')
+ waiters.wait_for_snapshot_status(cls.client,
+ cls.snapshot['id'], 'available')
@classmethod
def resource_cleanup(cls):
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index c032d9c..7202881 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -15,6 +15,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
+from tempest.common import waiters
from tempest import config
from tempest import test
@@ -66,12 +67,14 @@
"to the requested name")
self.assertIsNotNone(volume['id'],
"Field volume id is empty or not found.")
- self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume['id'], 'available')
# Update volume with new volume_type
self.volumes_client.retype_volume(volume['id'],
new_type=volume_types[1]['id'])
- self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume['id'], 'available')
# Get volume details and Verify
fetched_volume = self.volumes_client.show_volume(
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 253a3e1..bdb313f 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -15,6 +15,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils as utils
+from tempest.common import waiters
from tempest import test
@@ -35,7 +36,8 @@
params = {cls.name_field: vol_name}
cls.volume = cls.client.create_volume(**params)['volume']
- cls.client.wait_for_volume_status(cls.volume['id'], 'available')
+ waiters.wait_for_volume_status(cls.client,
+ cls.volume['id'], 'available')
@classmethod
def resource_cleanup(cls):
@@ -61,7 +63,8 @@
vol_name = utils.rand_name('Volume')
params = {self.name_field: vol_name}
temp_volume = self.client.create_volume(**params)['volume']
- self.client.wait_for_volume_status(temp_volume['id'], 'available')
+ waiters.wait_for_volume_status(self.client,
+ temp_volume['id'], 'available')
return temp_volume
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 30c6a15..b09cd2c 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -15,6 +15,7 @@
from tempest.api.volume import base
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
@@ -50,8 +51,8 @@
self.addCleanup(self.backups_adm_client.delete_backup,
backup['id'])
self.assertEqual(backup_name, backup['name'])
- self.admin_volume_client.wait_for_volume_status(
- self.volume['id'], 'available')
+ waiters.wait_for_volume_status(self.admin_volume_client,
+ self.volume['id'], 'available')
self.backups_adm_client.wait_for_backup_status(backup['id'],
'available')
@@ -74,8 +75,8 @@
self.assertEqual(backup['id'], restore['backup_id'])
self.backups_adm_client.wait_for_backup_status(backup['id'],
'available')
- self.admin_volume_client.wait_for_volume_status(
- restore['volume_id'], 'available')
+ waiters.wait_for_volume_status(self.admin_volume_client,
+ restore['volume_id'], 'available')
@decorators.skip_because(bug='1455043')
@test.idempotent_id('a99c54a1-dd80-4724-8a13-13bf58d4068d')
@@ -117,8 +118,8 @@
self.addCleanup(self.admin_volume_client.delete_volume,
restore['volume_id'])
self.assertEqual(import_backup['id'], restore['backup_id'])
- self.admin_volume_client.wait_for_volume_status(restore['volume_id'],
- 'available')
+ waiters.wait_for_volume_status(self.admin_volume_client,
+ restore['volume_id'], 'available')
# Verify if restored volume is there in volume list
volumes = self.admin_volume_client.list_volumes()['volumes']
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 82dc2c9..14819e3 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -15,6 +15,7 @@
from tempest.common import compute
from tempest.common.utils import data_utils
+from tempest.common import waiters
from tempest import config
from tempest import exceptions
from tempest.lib import exceptions as lib_exc
@@ -112,7 +113,8 @@
volume = cls.volumes_client.create_volume(**kwargs)['volume']
cls.volumes.append(volume)
- cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
+ waiters.wait_for_volume_status(cls.volumes_client,
+ volume['id'], 'available')
return volume
@classmethod
@@ -121,8 +123,8 @@
snapshot = cls.snapshots_client.create_snapshot(
volume_id=volume_id, **kwargs)['snapshot']
cls.snapshots.append(snapshot)
- cls.snapshots_client.wait_for_snapshot_status(snapshot['id'],
- 'available')
+ waiters.wait_for_snapshot_status(cls.snapshots_client,
+ snapshot['id'], 'available')
return snapshot
# NOTE(afazekas): these create_* and clean_* could be defined
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 7046dcf..866db3d 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -16,6 +16,7 @@
from testtools import matchers
from tempest.api.volume import base
+from tempest.common import waiters
from tempest import config
from tempest import test
@@ -51,8 +52,8 @@
volume_id=volume['id'])['transfer']
transfer_id = transfer['id']
auth_key = transfer['auth_key']
- self.client.wait_for_volume_status(volume['id'],
- 'awaiting-transfer')
+ waiters.wait_for_volume_status(self.client,
+ volume['id'], 'awaiting-transfer')
# Get a volume transfer
body = self.client.show_volume_transfer(transfer_id)['transfer']
@@ -66,7 +67,8 @@
# Accept a volume transfer by alt_tenant
body = self.alt_client.accept_volume_transfer(
transfer_id, auth_key=auth_key)['transfer']
- self.alt_client.wait_for_volume_status(volume['id'], 'available')
+ waiters.wait_for_volume_status(self.alt_client,
+ volume['id'], 'available')
@test.idempotent_id('ab526943-b725-4c07-b875-8e8ef87a2c30')
def test_create_list_delete_volume_transfer(self):
@@ -78,8 +80,8 @@
body = self.client.create_volume_transfer(
volume_id=volume['id'])['transfer']
transfer_id = body['id']
- self.client.wait_for_volume_status(volume['id'],
- 'awaiting-transfer')
+ waiters.wait_for_volume_status(self.client,
+ volume['id'], 'awaiting-transfer')
# List all volume transfers (looking for the one we created)
body = self.client.list_volume_transfers()['transfers']
@@ -91,7 +93,7 @@
# Delete a volume transfer
self.client.delete_volume_transfer(transfer_id)
- self.client.wait_for_volume_status(volume['id'], 'available')
+ waiters.wait_for_volume_status(self.client, volume['id'], 'available')
class VolumesV1TransfersTest(VolumesV2TransfersTest):
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 5f9ea7f..9c67579 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -43,7 +43,8 @@
# Create a test shared volume for attach/detach tests
cls.volume = cls.create_volume()
- cls.client.wait_for_volume_status(cls.volume['id'], 'available')
+ waiters.wait_for_volume_status(cls.client,
+ cls.volume['id'], 'available')
@classmethod
def resource_cleanup(cls):
@@ -64,9 +65,11 @@
self.client.attach_volume(self.volume['id'],
instance_uuid=self.server['id'],
mountpoint=mountpoint)
- self.client.wait_for_volume_status(self.volume['id'], 'in-use')
+ waiters.wait_for_volume_status(self.client,
+ self.volume['id'], 'in-use')
self.client.detach_volume(self.volume['id'])
- self.client.wait_for_volume_status(self.volume['id'], 'available')
+ waiters.wait_for_volume_status(self.client,
+ self.volume['id'], 'available')
@test.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
@testtools.skipUnless(CONF.volume_feature_enabled.bootable,
@@ -91,10 +94,11 @@
self.client.attach_volume(self.volume['id'],
instance_uuid=self.server['id'],
mountpoint=mountpoint)
- self.client.wait_for_volume_status(self.volume['id'], 'in-use')
+ waiters.wait_for_volume_status(self.client,
+ self.volume['id'], 'in-use')
# NOTE(gfidente): added in reverse order because functions will be
# called in reverse order to the order they are added (LIFO)
- self.addCleanup(self.client.wait_for_volume_status,
+ self.addCleanup(waiters.wait_for_volume_status, self.client,
self.volume['id'],
'available')
self.addCleanup(self.client.detach_volume, self.volume['id'])
@@ -120,7 +124,8 @@
image_id = body["image_id"]
self.addCleanup(self.image_client.delete_image, image_id)
self.image_client.wait_for_image_status(image_id, 'active')
- self.client.wait_for_volume_status(self.volume['id'], 'available')
+ waiters.wait_for_volume_status(self.client,
+ self.volume['id'], 'available')
@test.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
def test_reserve_unreserve_volume(self):
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index ed1e5c5..1947779 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -14,6 +14,7 @@
# under the License.
from tempest.api.volume import base
+from tempest.common import waiters
from tempest import config
from tempest import test
@@ -33,7 +34,8 @@
self.volume = self.create_volume()
extend_size = int(self.volume['size']) + 1
self.client.extend_volume(self.volume['id'], new_size=extend_size)
- self.client.wait_for_volume_status(self.volume['id'], 'available')
+ waiters.wait_for_volume_status(self.client,
+ self.volume['id'], 'available')
volume = self.client.show_volume(self.volume['id'])['volume']
self.assertEqual(int(volume['size']), extend_size)
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index aa3ef2f..5d83bb0 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -18,6 +18,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
+from tempest.common import waiters
from tempest import config
from tempest import test
@@ -53,7 +54,7 @@
volume = self.client.create_volume(**kwargs)['volume']
self.assertIn('id', volume)
self.addCleanup(self._delete_volume, volume['id'])
- self.client.wait_for_volume_status(volume['id'], 'available')
+ waiters.wait_for_volume_status(self.client, volume['id'], 'available')
self.assertIn(self.name_field, volume)
self.assertEqual(volume[self.name_field], v_name,
"The created volume name is not equal "
@@ -113,7 +114,8 @@
new_volume = self.client.create_volume(**params)['volume']
self.assertIn('id', new_volume)
self.addCleanup(self._delete_volume, new_volume['id'])
- self.client.wait_for_volume_status(new_volume['id'], 'available')
+ waiters.wait_for_volume_status(self.client,
+ new_volume['id'], 'available')
params = {self.name_field: volume[self.name_field],
self.descrip_field: volume[self.descrip_field]}
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index c79235a..347877c 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -12,6 +12,7 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
+from tempest.common import waiters
from tempest import config
from tempest import test
@@ -37,7 +38,8 @@
def _detach(self, volume_id):
"""Detach volume."""
self.volumes_client.detach_volume(volume_id)
- self.volumes_client.wait_for_volume_status(volume_id, 'available')
+ 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."""
@@ -70,9 +72,9 @@
self.servers_client.attach_volume(
server['id'], volumeId=self.volume_origin['id'],
device=mountpoint)
- self.volumes_client.wait_for_volume_status(self.volume_origin['id'],
- 'in-use')
- self.addCleanup(self.volumes_client.wait_for_volume_status,
+ waiters.wait_for_volume_status(self.volumes_client,
+ self.volume_origin['id'], 'in-use')
+ self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
self.volume_origin['id'], 'available')
self.addCleanup(self.servers_client.detach_volume, server['id'],
self.volume_origin['id'])
@@ -171,7 +173,8 @@
# NOTE(gfidente): size is required also when passing snapshot_id
volume = self.volumes_client.create_volume(
snapshot_id=snapshot['id'])['volume']
- self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume['id'], 'available')
self.volumes_client.delete_volume(volume['id'])
self.volumes_client.wait_for_resource_deletion(volume['id'])
self.cleanup_snapshot(snapshot)
diff --git a/tempest/clients.py b/tempest/clients.py
index 8931706..141d1c6 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -87,6 +87,8 @@
from tempest.lib.services.network.ports_client import PortsClient
from tempest.lib.services.network.quotas_client import QuotasClient \
as NetworkQuotasClient
+from tempest.lib.services.network.security_group_rules_client import \
+ SecurityGroupRulesClient
from tempest.lib.services.network.security_groups_client import \
SecurityGroupsClient
from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
@@ -130,10 +132,7 @@
UsersClient as UsersV3Client
from tempest.services.image.v1.json.images_client import ImagesClient
from tempest.services.image.v2.json.images_client import ImagesClientV2
-from tempest.services.network.json.network_client import NetworkClient
from tempest.services.network.json.routers_client import RoutersClient
-from tempest.services.network.json.security_group_rules_client import \
- SecurityGroupRulesClient
from tempest.services.object_storage.account_client import AccountClient
from tempest.services.object_storage.container_client import ContainerClient
from tempest.services.object_storage.object_client import ObjectClient
@@ -237,14 +236,6 @@
build_interval=CONF.network.build_interval,
build_timeout=CONF.network.build_timeout,
**self.default_params)
- self.network_client = NetworkClient(
- self.auth_provider,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type=CONF.network.endpoint_type,
- build_interval=CONF.network.build_interval,
- build_timeout=CONF.network.build_timeout,
- **self.default_params)
self.networks_client = NetworksClient(
self.auth_provider,
CONF.network.catalog_type,
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 6124676..a154d0b 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -103,7 +103,6 @@
from tempest.services.identity.v2.json import roles_client
from tempest.services.identity.v2.json import tenants_client
from tempest.services.identity.v2.json import users_client
-from tempest.services.network.json import network_client
from tempest.services.network.json import routers_client
LOG = None
@@ -170,7 +169,6 @@
endpoint_type='adminURL',
**params
)
- network_admin = None
networks_admin = None
routers_admin = None
subnets_admin = None
@@ -178,12 +176,6 @@
if (CONF.service_available.neutron and
CONF.auth.create_isolated_networks):
neutron_iso_networks = True
- network_admin = network_client.NetworkClient(
- _auth,
- CONF.network.catalog_type,
- CONF.network.region or CONF.identity.region,
- endpoint_type='adminURL',
- **params)
networks_admin = networks_client.NetworksClient(
_auth,
CONF.network.catalog_type,
@@ -203,13 +195,13 @@
endpoint_type='adminURL',
**params)
return (identity_admin, tenants_admin, roles_admin, users_admin,
- neutron_iso_networks, network_admin, networks_admin, routers_admin,
+ neutron_iso_networks, networks_admin, routers_admin,
subnets_admin)
def create_resources(opts, resources):
(identity_admin, tenants_admin, roles_admin, users_admin,
- neutron_iso_networks, network_admin, networks_admin, routers_admin,
+ neutron_iso_networks, networks_admin, routers_admin,
subnets_admin) = get_admin_clients(opts)
roles = roles_admin.list_roles()['roles']
for u in resources['users']:
@@ -255,7 +247,7 @@
for u in resources['users']:
tenant = identity.get_tenant_by_name(tenants_admin, u['tenant'])
network_name, router_name = create_network_resources(
- network_admin, networks_admin, routers_admin, subnets_admin,
+ networks_admin, routers_admin, subnets_admin,
tenant['id'], u['name'])
u['network'] = network_name
u['router'] = router_name
@@ -282,7 +274,7 @@
LOG.info('Resources deployed successfully!')
-def create_network_resources(network_admin_client, networks_admin_client,
+def create_network_resources(networks_admin_client,
routers_admin_client, subnets_admin_client,
tenant_id, name):
@@ -292,8 +284,8 @@
return resp_body['network']
def _create_subnet(subnet_name, network_id):
- base_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = CONF.network.tenant_network_mask_bits
+ base_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+ mask_bits = CONF.network.project_network_mask_bits
for subnet_cidr in base_cidr.subnet(mask_bits):
try:
resp_body = subnets_admin_client.\
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index a0676b6..3c09600 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -382,7 +382,6 @@
class NetworkService(BaseService):
def __init__(self, manager, **kwargs):
super(NetworkService, self).__init__(kwargs)
- self.client = manager.network_client
self.networks_client = manager.networks_client
self.subnets_client = manager.subnets_client
self.ports_client = manager.ports_client
@@ -462,12 +461,13 @@
def delete(self):
client = self.routers_client
+ ports_client = self.ports_client
routers = self.list()
for router in routers:
try:
rid = router['id']
ports = [port for port
- in client.list_router_interfaces(rid)['ports']
+ in ports_client.list_ports(device_id=rid)['ports']
if port["device_owner"] == "network:router_interface"]
for port in ports:
client.remove_router_interface(rid, port_id=port['id'])
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 057c227..2a4e314 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -127,13 +127,14 @@
from tempest.lib.services.compute import security_group_rules_client
from tempest.lib.services.compute import security_groups_client
from tempest.lib.services.compute import servers_client
+from tempest.lib.services.network import networks_client
+from tempest.lib.services.network import ports_client
from tempest.lib.services.network import subnets_client
from tempest.services.identity.v2.json import identity_client
from tempest.services.identity.v2.json import roles_client
from tempest.services.identity.v2.json import tenants_client
from tempest.services.identity.v2.json import users_client
from tempest.services.image.v2.json import images_client
-from tempest.services.network.json import network_client
from tempest.services.network.json import routers_client
from tempest.services.object_storage import container_client
from tempest.services.object_storage import object_client
@@ -263,7 +264,15 @@
build_interval=CONF.volume.build_interval,
build_timeout=CONF.volume.build_timeout,
**default_params)
- self.networks = network_client.NetworkClient(
+ self.networks = networks_client.NetworksClient(
+ _auth,
+ CONF.network.catalog_type,
+ CONF.network.region or CONF.identity.region,
+ endpoint_type=CONF.network.endpoint_type,
+ build_interval=CONF.network.build_interval,
+ build_timeout=CONF.network.build_timeout,
+ **default_params)
+ self.ports = ports_client.PortsClient(
_auth,
CONF.network.catalog_type,
CONF.network.region or CONF.identity.region,
@@ -751,7 +760,7 @@
n_body = client.routers.list_routers()
for router in n_body['routers']:
router_id = router['id']
- r_body = client.networks.list_router_interfaces(router_id)
+ r_body = client.ports.list_ports(device_id=router_id)
for port in r_body['ports']:
if port['network_id'] == network_id:
return "qrouter-%s" % router_id
@@ -1022,7 +1031,7 @@
v_name = volume['name']
body = client.volumes.create_volume(size=size,
display_name=v_name)['volume']
- client.volumes.wait_for_volume_status(body['id'], 'available')
+ waiters.wait_for_volume_status(client.volumes, body['id'], 'available')
def destroy_volumes(volumes):
diff --git a/tempest/cmd/run_stress.py b/tempest/cmd/run_stress.py
old mode 100644
new mode 100755
index 6fe3928..9c8552f
--- a/tempest/cmd/run_stress.py
+++ b/tempest/cmd/run_stress.py
@@ -23,6 +23,7 @@
# unittest in python 2.6 does not contain loader, so uses unittest2
from unittest2 import loader
import traceback
+import warnings
from cliff import command
from oslo_log import log as logging
@@ -74,7 +75,17 @@
class TempestRunStress(command.Command):
+ @staticmethod
+ def display_deprecation_warning():
+ warnings.simplefilter('once', category=DeprecationWarning)
+ warnings.warn(
+ 'Stress tests are deprecated and will be removed from Tempest '
+ 'in the Newton release.',
+ DeprecationWarning)
+ warnings.resetwarnings()
+
def get_parser(self, prog_name):
+ self.display_deprecation_warning()
pa = super(TempestRunStress, self).get_parser(prog_name)
pa = add_arguments(pa)
return pa
@@ -146,6 +157,7 @@
def main():
+ TempestRunStress.display_deprecation_warning()
parser = argparse.ArgumentParser(description='Run stress tests')
pa = add_arguments(parser)
ns = pa.parse_args()
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 9049886..0ba322d 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -97,7 +97,14 @@
ca_certs=ca_certs)
__, body = raw_http.request(endpoint, 'GET')
client_dict[service].reset_path()
- body = json.loads(body)
+ try:
+ body = json.loads(body)
+ except ValueError:
+ LOG.error(
+ 'Failed to get a JSON response from unversioned endpoint %s '
+ '(versioned endpoint was %s). Response is:\n%s',
+ endpoint, client_dict[service].base_url, body[:100])
+ raise
if service == 'keystone':
versions = map(lambda x: x['id'], body['versions']['values'])
else:
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 2d2909a..b5c4547 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -98,8 +98,8 @@
volume = volumes_client.create_volume(
display_name=volume_name,
imageRef=image_id)
- volumes_client.wait_for_volume_status(volume['volume']['id'],
- 'available')
+ waiters.wait_for_volume_status(volumes_client,
+ volume['volume']['id'], 'available')
bd_map_v2 = [{
'uuid': volume['volume']['id'],
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 0ca14a9..58157ef 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -256,7 +256,7 @@
if credential_type not in CREDENTIAL_TYPES:
raise exceptions.InvalidCredentials()
- conf_attributes = ['username', 'password', 'tenant_name']
+ conf_attributes = ['username', 'password', 'project_name']
if identity_version == 'v3':
conf_attributes.append('domain_name')
@@ -269,6 +269,11 @@
params[attr] = getattr(_section, attr)
else:
params[attr] = getattr(_section, prefix + "_" + attr)
+ # NOTE(andreaf) v2 API still uses tenants, so we must translate project
+ # to tenant before building the Credentials object
+ if identity_version == 'v2':
+ params['tenant_name'] = params.get('project_name')
+ params.pop('project_name', None)
# Build and validate credentials. We are reading configured credentials,
# so validate them even if fill_in is False
credentials = get_credentials(fill_in=fill_in,
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 5f6c8b8..d374be4 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -62,7 +62,6 @@
self.users_admin_client,
self.roles_admin_client,
self.domains_admin_client,
- self.network_admin_client,
self.networks_admin_client,
self.routers_admin_client,
self.subnets_admin_client,
@@ -93,13 +92,13 @@
os = clients.Manager(self.default_admin_creds)
if self.identity_version == 'v2':
return (os.identity_client, os.tenants_client, os.users_client,
- os.roles_client, None, os.network_client,
+ os.roles_client, None,
os.networks_client, os.routers_client, os.subnets_client,
os.ports_client, os.security_groups_client)
else:
return (os.identity_v3_client, os.projects_client,
os.users_v3_client, os.roles_v3_client, os.domains_client,
- os.network_client, os.networks_client, os.routers_client,
+ os.networks_client, os.routers_client,
os.subnets_client, os.ports_client,
os.security_groups_client)
@@ -208,8 +207,8 @@
return resp_body['network']
def _create_subnet(self, subnet_name, tenant_id, network_id):
- base_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = CONF.network.tenant_network_mask_bits
+ base_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+ mask_bits = CONF.network.project_network_mask_bits
for subnet_cidr in base_cidr.subnet(mask_bits):
try:
if self.network_resources:
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index baf796d..00062de 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -306,13 +306,32 @@
def connect(self):
"""Connect to SSL port and apply per-connection parameters."""
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- if self.timeout is not None:
- # '0' microseconds
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
- struct.pack('LL', self.timeout, 0))
- self.sock = OpenSSLConnectionDelegator(self.context, sock)
- self.sock.connect((self.host, self.port))
+ try:
+ addresses = socket.getaddrinfo(self.host,
+ self.port,
+ socket.AF_UNSPEC,
+ socket.SOCK_STREAM)
+ except OSError as msg:
+ raise exc.RestClientException(msg)
+ for res in addresses:
+ af, socktype, proto, canonname, sa = res
+ sock = socket.socket(af, socket.SOCK_STREAM)
+
+ if self.timeout is not None:
+ # '0' microseconds
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
+ struct.pack('LL', self.timeout, 0))
+ self.sock = OpenSSLConnectionDelegator(self.context, sock)
+ try:
+ self.sock.connect(sa)
+ except OSError as msg:
+ if self.sock:
+ self.sock = None
+ continue
+ break
+ if self.sock is None:
+ # Happen only when all results have failed.
+ raise exc.RestClientException('Cannot connect to %s' % self.host)
def close(self):
if self.sock:
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 97cc2ec..f3df387 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -331,6 +331,7 @@
def _wrap_creds_with_network(self, hash):
creds_dict = self.hash_dict['creds'][hash]
# Make sure a domain scope if defined for users in case of V3
+ # Make sure a tenant is available in case of V2
creds_dict = self._extend_credentials(creds_dict)
# This just builds a Credentials object, it does not validate
# nor fill with missing fields.
@@ -356,4 +357,17 @@
user_domain_fields = set(['user_domain_name', 'user_domain_id'])
if not user_domain_fields.intersection(set(creds_dict.keys())):
creds_dict['user_domain_name'] = self.credentials_domain
+ # NOTE(andreaf) In case of v2, replace project with tenant if project
+ # is provided and tenant is not
+ if self.identity_version == 'v2':
+ if ('project_name' in creds_dict and
+ 'tenant_name' in creds_dict and
+ creds_dict['project_name'] != creds_dict['tenant_name']):
+ clean_creds = self._sanitize_creds(creds_dict)
+ msg = 'Cannot specify project and tenant at the same time %s'
+ raise exceptions.InvalidCredentials(msg % clean_creds)
+ if ('project_name' in creds_dict and
+ 'tenant_name' not in creds_dict):
+ creds_dict['tenant_name'] = creds_dict['project_name']
+ creds_dict.pop('project_name')
return creds_dict
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 36e3e3a..3a215a0 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -118,7 +118,7 @@
def assign_static_ip(self, nic, addr):
cmd = "sudo ip addr add {ip}/{mask} dev {nic}".format(
- ip=addr, mask=CONF.network.tenant_network_mask_bits,
+ ip=addr, mask=CONF.network.project_network_mask_bits,
nic=nic
)
return self.exec_command(cmd)
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index 84d0143..c3c9a41 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -59,7 +59,9 @@
create_ssh_security_group(os, add_rule)
if validation_resources['floating_ip']:
floating_client = os.compute_floating_ips_client
- validation_data.update(floating_client.create_floating_ip())
+ validation_data.update(
+ floating_client.create_floating_ip(
+ pool=CONF.network.floating_network_name))
return validation_data
diff --git a/tempest/config.py b/tempest/config.py
index 4c3b04b..b787b19 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -56,7 +56,7 @@
"number of concurrent test processes."),
cfg.BoolOpt('use_dynamic_credentials',
default=True,
- help="Allows test cases to create/destroy tenants and "
+ help="Allows test cases to create/destroy projects and "
"users. This option requires that OpenStack Identity "
"API admin credentials are known. If false, isolated "
"test cases and parallel execution, can still be "
@@ -81,23 +81,26 @@
default=True,
help="If use_dynamic_credentials is set to True and Neutron "
"is enabled Tempest will try to create a usable network, "
- "subnet, and router when needed for each tenant it "
+ "subnet, and router when needed for each project it "
"creates. However in some neutron configurations, like "
"with VLAN provider networks, this doesn't work. So if "
"set to False the isolated networks will not be created"),
cfg.StrOpt('admin_username',
help="Username for an administrative user. This is needed for "
- "authenticating requests made by tenant isolation to "
+ "authenticating requests made by project isolation to "
"create users and projects",
deprecated_group='identity'),
- cfg.StrOpt('admin_tenant_name',
- help="Tenant name to use for an administrative user. This is "
- "needed for authenticating requests made by tenant "
+ cfg.StrOpt('admin_project_name',
+ 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_group='identity'),
+ deprecated_opts=[cfg.DeprecatedOpt('admin_tenant_name',
+ group='auth'),
+ cfg.DeprecatedOpt('admin_tenant_name',
+ group='identity')]),
cfg.StrOpt('admin_password',
help="Password to use for an administrative user. This is "
- "needed for authenticating requests made by tenant "
+ "needed for authenticating requests made by project "
"isolation to create users and projects",
secret=True,
deprecated_group='identity'),
@@ -158,8 +161,9 @@
cfg.StrOpt('username',
help="Username to use for Nova API requests.",
deprecated_for_removal=True),
- cfg.StrOpt('tenant_name',
- help="Tenant name to use for Nova API requests.",
+ cfg.StrOpt('project_name',
+ deprecated_name='tenant_name',
+ help="Project name to use for Nova API requests.",
deprecated_for_removal=True),
cfg.StrOpt('admin_role',
default='admin',
@@ -176,8 +180,9 @@
help="Username of alternate user to use for Nova API "
"requests.",
deprecated_for_removal=True),
- cfg.StrOpt('alt_tenant_name',
- help="Alternate user's Tenant name to use for Nova API "
+ cfg.StrOpt('alt_project_name',
+ deprecated_name='alt_tenant_name',
+ help="Alternate user's Project name to use for Nova API "
"requests.",
deprecated_for_removal=True),
cfg.StrOpt('alt_password',
@@ -246,10 +251,10 @@
"no OS-EXT-STS extension available"),
cfg.StrOpt('fixed_network_name',
help="Name of the fixed network that is visible to all test "
- "tenants. If multiple networks are available for a tenant"
- " this is the network which will be used for creating "
- "servers if tempest does not create a network or a "
- "network is not specified elsewhere. It may be used for "
+ "projects. If multiple networks are available for a "
+ "project, this is the network which will be used for "
+ "creating servers if tempest does not create a network or "
+ "s network is not specified elsewhere. It may be used for "
"ssh validation only if floating IPs are disabled."),
cfg.StrOpt('catalog_type',
default='compute',
@@ -493,21 +498,26 @@
choices=['public', 'admin', 'internal',
'publicURL', 'adminURL', 'internalURL'],
help="The endpoint type to use for the network service."),
- cfg.StrOpt('tenant_network_cidr',
+ cfg.StrOpt('project_network_cidr',
+ deprecated_name='tenant_network_cidr',
default="10.100.0.0/16",
- help="The cidr block to allocate tenant ipv4 subnets from"),
- cfg.IntOpt('tenant_network_mask_bits',
+ help="The cidr block to allocate project ipv4 subnets from"),
+ cfg.IntOpt('project_network_mask_bits',
+ deprecated_name='tenant_network_mask_bits',
default=28,
- help="The mask bits for tenant ipv4 subnets"),
- cfg.StrOpt('tenant_network_v6_cidr',
+ help="The mask bits for project ipv4 subnets"),
+ cfg.StrOpt('project_network_v6_cidr',
+ deprecated_name='tenant_network_v6_cidr',
default="2003::/48",
- help="The cidr block to allocate tenant ipv6 subnets from"),
- cfg.IntOpt('tenant_network_v6_mask_bits',
+ help="The cidr block to allocate project ipv6 subnets from"),
+ cfg.IntOpt('project_network_v6_mask_bits',
+ deprecated_name='tenant_network_v6_mask_bits',
default=64,
- help="The mask bits for tenant ipv6 subnets"),
- cfg.BoolOpt('tenant_networks_reachable',
+ help="The mask bits for project ipv6 subnets"),
+ cfg.BoolOpt('project_networks_reachable',
+ deprecated_name='tenant_networks_reachable',
default=False,
- help="Whether tenant networks can be reached directly from "
+ help="Whether project networks can be reached directly from "
"the test client. This must be set to True when the "
"'fixed' ssh_connect_method is selected."),
cfg.StrOpt('public_network_id',
@@ -1003,7 +1013,7 @@
default=False,
help='Allows a full cleaning process after a stress test.'
' Caution : this cleanup will remove every objects of'
- ' every tenant.')
+ ' every project.')
]
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index c666c96..f4b76e5 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -225,6 +225,27 @@
yield (0, msg)
+def dont_import_local_tempest_into_lib(logical_line, filename):
+ """Check that tempest.lib should not import local tempest code
+
+ T112
+ """
+ if 'tempest/lib/' not in filename:
+ return
+
+ if not ('from tempest' in logical_line
+ or 'import tempest' in logical_line):
+ return
+
+ if ('from tempest.lib' in logical_line
+ or 'import tempest.lib' in logical_line):
+ return
+
+ msg = ("T112: tempest.lib should not import local tempest code to avoid "
+ "circular dependency")
+ yield (0, msg)
+
+
def factory(register):
register(import_no_clients_in_api_and_scenario_tests)
register(scenario_tests_need_service_tags)
@@ -236,3 +257,4 @@
register(no_testtools_skip_decorator)
register(get_resources_on_service_clients)
register(delete_resources_on_service_clients)
+ register(dont_import_local_tempest_into_lib)
diff --git a/tempest/hacking/ignored_list_T110.txt b/tempest/hacking/ignored_list_T110.txt
index 5d3fc93..380c173 100644
--- a/tempest/hacking/ignored_list_T110.txt
+++ b/tempest/hacking/ignored_list_T110.txt
@@ -4,5 +4,4 @@
./tempest/services/volume/base/base_qos_client.py
./tempest/services/volume/base/base_backups_client.py
./tempest/services/baremetal/base.py
-./tempest/services/network/json/network_client.py
./tempest/services/network/json/routers_client.py
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 7ce05e3..7d2eda0 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -548,7 +548,7 @@
:param str method: The HTTP verb to use for the request
:param str headers: Headers to use for the request if none are specifed
the headers
- :param str body: Body to to send with the request
+ :param str body: Body to send with the request
:rtype: tuple
:return: a tuple with the first entry containing the response headers
and the second the response body
@@ -584,7 +584,7 @@
specifed the headers returned from the
get_headers() method are used. If the request
explicitly requires no headers use an empty dict.
- :param str body: Body to to send with the request
+ :param str body: Body to send with the request
:rtype: tuple
:return: a tuple with the first entry containing the response headers
and the second the response body
diff --git a/tempest/lib/services/compute/agents_client.py b/tempest/lib/services/compute/agents_client.py
index 8b11e64..6d3a817 100644
--- a/tempest/lib/services/compute/agents_client.py
+++ b/tempest/lib/services/compute/agents_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import agents as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class AgentsClient(rest_client.RestClient):
+class AgentsClient(base_compute_client.BaseComputeClient):
"""Tests Agents API"""
def list_agents(self, **params):
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
index b481674..168126c 100644
--- a/tempest/lib/services/compute/aggregates_client.py
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import aggregates as schema
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
-class AggregatesClient(rest_client.RestClient):
+class AggregatesClient(base_compute_client.BaseComputeClient):
def list_aggregates(self):
"""Get aggregate list."""
diff --git a/tempest/lib/services/compute/availability_zone_client.py b/tempest/lib/services/compute/availability_zone_client.py
index 00f66d6..a911191 100644
--- a/tempest/lib/services/compute/availability_zone_client.py
+++ b/tempest/lib/services/compute/availability_zone_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import availability_zone \
as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class AvailabilityZoneClient(rest_client.RestClient):
+class AvailabilityZoneClient(base_compute_client.BaseComputeClient):
def list_availability_zones(self, detail=False):
url = 'os-availability-zone'
diff --git a/tempest/lib/services/compute/baremetal_nodes_client.py b/tempest/lib/services/compute/baremetal_nodes_client.py
index d9a712e..06dc369 100644
--- a/tempest/lib/services/compute/baremetal_nodes_client.py
+++ b/tempest/lib/services/compute/baremetal_nodes_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import baremetal_nodes \
as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class BaremetalNodesClient(rest_client.RestClient):
+class BaremetalNodesClient(base_compute_client.BaseComputeClient):
"""Tests Baremetal API"""
def list_baremetal_nodes(self, **params):
diff --git a/tempest/lib/services/compute/certificates_client.py b/tempest/lib/services/compute/certificates_client.py
index 76d830e..822756c 100644
--- a/tempest/lib/services/compute/certificates_client.py
+++ b/tempest/lib/services/compute/certificates_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import certificates as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class CertificatesClient(rest_client.RestClient):
+class CertificatesClient(base_compute_client.BaseComputeClient):
def show_certificate(self, certificate_id):
url = "os-certificates/%s" % certificate_id
diff --git a/tempest/lib/services/compute/extensions_client.py b/tempest/lib/services/compute/extensions_client.py
index 85f8f0c..afaf282 100644
--- a/tempest/lib/services/compute/extensions_client.py
+++ b/tempest/lib/services/compute/extensions_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import extensions as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class ExtensionsClient(rest_client.RestClient):
+class ExtensionsClient(base_compute_client.BaseComputeClient):
def list_extensions(self):
url = 'extensions'
diff --git a/tempest/lib/services/compute/fixed_ips_client.py b/tempest/lib/services/compute/fixed_ips_client.py
index 76ec59f..c25ac2c 100644
--- a/tempest/lib/services/compute/fixed_ips_client.py
+++ b/tempest/lib/services/compute/fixed_ips_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import fixed_ips as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class FixedIPsClient(rest_client.RestClient):
+class FixedIPsClient(base_compute_client.BaseComputeClient):
def show_fixed_ip(self, fixed_ip):
url = "os-fixed-ips/%s" % fixed_ip
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index 50f1dcc..6869f02 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -22,9 +22,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import flavors_extra_specs \
as schema_extra_specs
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class FlavorsClient(rest_client.RestClient):
+class FlavorsClient(base_compute_client.BaseComputeClient):
def list_flavors(self, detail=False, **params):
url = 'flavors'
diff --git a/tempest/lib/services/compute/floating_ip_pools_client.py b/tempest/lib/services/compute/floating_ip_pools_client.py
index d4a0193..d3af050 100644
--- a/tempest/lib/services/compute/floating_ip_pools_client.py
+++ b/tempest/lib/services/compute/floating_ip_pools_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class FloatingIPPoolsClient(rest_client.RestClient):
+class FloatingIPPoolsClient(base_compute_client.BaseComputeClient):
def list_floating_ip_pools(self, params=None):
"""Gets all floating IP Pools list."""
diff --git a/tempest/lib/services/compute/floating_ips_bulk_client.py b/tempest/lib/services/compute/floating_ips_bulk_client.py
index bfcf74b..5f06009 100644
--- a/tempest/lib/services/compute/floating_ips_bulk_client.py
+++ b/tempest/lib/services/compute/floating_ips_bulk_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class FloatingIPsBulkClient(rest_client.RestClient):
+class FloatingIPsBulkClient(base_compute_client.BaseComputeClient):
def create_floating_ips_bulk(self, ip_range, pool, interface):
"""Allocate floating IPs in bulk."""
diff --git a/tempest/lib/services/compute/floating_ips_client.py b/tempest/lib/services/compute/floating_ips_client.py
index 2569bf9..03e4894 100644
--- a/tempest/lib/services/compute/floating_ips_client.py
+++ b/tempest/lib/services/compute/floating_ips_client.py
@@ -19,9 +19,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
-class FloatingIPsClient(rest_client.RestClient):
+class FloatingIPsClient(base_compute_client.BaseComputeClient):
def list_floating_ips(self, **params):
"""Returns a list of all floating IPs filtered by any parameters."""
diff --git a/tempest/lib/services/compute/hosts_client.py b/tempest/lib/services/compute/hosts_client.py
index 269160e..0143765 100644
--- a/tempest/lib/services/compute/hosts_client.py
+++ b/tempest/lib/services/compute/hosts_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import hosts as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class HostsClient(rest_client.RestClient):
+class HostsClient(base_compute_client.BaseComputeClient):
def list_hosts(self, **params):
"""List all hosts."""
diff --git a/tempest/lib/services/compute/hypervisor_client.py b/tempest/lib/services/compute/hypervisor_client.py
index 2e6df1f..5dcecc9 100644
--- a/tempest/lib/services/compute/hypervisor_client.py
+++ b/tempest/lib/services/compute/hypervisor_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import hypervisors as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class HypervisorClient(rest_client.RestClient):
+class HypervisorClient(base_compute_client.BaseComputeClient):
def list_hypervisors(self, detail=False):
"""List hypervisors information."""
diff --git a/tempest/lib/services/compute/images_client.py b/tempest/lib/services/compute/images_client.py
index 30ff484..4a55ce7 100644
--- a/tempest/lib/services/compute/images_client.py
+++ b/tempest/lib/services/compute/images_client.py
@@ -19,9 +19,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import images as schema
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
-class ImagesClient(rest_client.RestClient):
+class ImagesClient(base_compute_client.BaseComputeClient):
def create_image(self, server_id, **kwargs):
"""Create an image of the original server.
diff --git a/tempest/lib/services/compute/instance_usage_audit_log_client.py b/tempest/lib/services/compute/instance_usage_audit_log_client.py
index 4651b2a..1b94306 100644
--- a/tempest/lib/services/compute/instance_usage_audit_log_client.py
+++ b/tempest/lib/services/compute/instance_usage_audit_log_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import \
instance_usage_audit_logs as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class InstanceUsagesAuditLogClient(rest_client.RestClient):
+class InstanceUsagesAuditLogClient(base_compute_client.BaseComputeClient):
def list_instance_usage_audit_logs(self):
url = 'os-instance_usage_audit_log'
diff --git a/tempest/lib/services/compute/interfaces_client.py b/tempest/lib/services/compute/interfaces_client.py
index e7da5a1..80192a1 100644
--- a/tempest/lib/services/compute/interfaces_client.py
+++ b/tempest/lib/services/compute/interfaces_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import interfaces as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class InterfacesClient(rest_client.RestClient):
+class InterfacesClient(base_compute_client.BaseComputeClient):
def list_interfaces(self, server_id):
resp, body = self.get('servers/%s/os-interface' % server_id)
diff --git a/tempest/lib/services/compute/keypairs_client.py b/tempest/lib/services/compute/keypairs_client.py
index 3e3cf8d..0361b9d 100644
--- a/tempest/lib/services/compute/keypairs_client.py
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import keypairs as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class KeyPairsClient(rest_client.RestClient):
+class KeyPairsClient(base_compute_client.BaseComputeClient):
def list_keypairs(self):
resp, body = self.get("os-keypairs")
diff --git a/tempest/lib/services/compute/limits_client.py b/tempest/lib/services/compute/limits_client.py
index c7eba4e..efe9889 100644
--- a/tempest/lib/services/compute/limits_client.py
+++ b/tempest/lib/services/compute/limits_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import limits as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class LimitsClient(rest_client.RestClient):
+class LimitsClient(base_compute_client.BaseComputeClient):
def show_limits(self):
resp, body = self.get("limits")
diff --git a/tempest/lib/services/compute/migrations_client.py b/tempest/lib/services/compute/migrations_client.py
index 21bc37a..5eae8aa 100644
--- a/tempest/lib/services/compute/migrations_client.py
+++ b/tempest/lib/services/compute/migrations_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import migrations as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class MigrationsClient(rest_client.RestClient):
+class MigrationsClient(base_compute_client.BaseComputeClient):
def list_migrations(self, **params):
"""List all migrations.
diff --git a/tempest/lib/services/compute/networks_client.py b/tempest/lib/services/compute/networks_client.py
index c0eb5ff..6c8c943 100644
--- a/tempest/lib/services/compute/networks_client.py
+++ b/tempest/lib/services/compute/networks_client.py
@@ -16,9 +16,10 @@
from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class NetworksClient(rest_client.RestClient):
+class NetworksClient(base_compute_client.BaseComputeClient):
def list_networks(self):
resp, body = self.get("os-networks")
diff --git a/tempest/lib/services/compute/quota_classes_client.py b/tempest/lib/services/compute/quota_classes_client.py
index ff4eec0..9dc04ad 100644
--- a/tempest/lib/services/compute/quota_classes_client.py
+++ b/tempest/lib/services/compute/quota_classes_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1\
import quota_classes as classes_schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class QuotaClassesClient(rest_client.RestClient):
+class QuotaClassesClient(base_compute_client.BaseComputeClient):
def show_quota_class_set(self, quota_class_id):
"""List the quota class set for a quota class."""
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
index 697d004..184a3d7 100644
--- a/tempest/lib/services/compute/quotas_client.py
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -17,9 +17,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import quotas as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class QuotasClient(rest_client.RestClient):
+class QuotasClient(base_compute_client.BaseComputeClient):
def show_quota_set(self, tenant_id, user_id=None):
"""List the quota set for a tenant."""
diff --git a/tempest/lib/services/compute/security_group_default_rules_client.py b/tempest/lib/services/compute/security_group_default_rules_client.py
index e5f291c..d57c8e0 100644
--- a/tempest/lib/services/compute/security_group_default_rules_client.py
+++ b/tempest/lib/services/compute/security_group_default_rules_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import \
security_group_default_rule as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class SecurityGroupDefaultRulesClient(rest_client.RestClient):
+class SecurityGroupDefaultRulesClient(base_compute_client.BaseComputeClient):
def create_security_default_group_rule(self, **kwargs):
"""Create security group default rule.
diff --git a/tempest/lib/services/compute/security_group_rules_client.py b/tempest/lib/services/compute/security_group_rules_client.py
index c0e1245..c969b81 100644
--- a/tempest/lib/services/compute/security_group_rules_client.py
+++ b/tempest/lib/services/compute/security_group_rules_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import \
security_groups as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class SecurityGroupRulesClient(rest_client.RestClient):
+class SecurityGroupRulesClient(base_compute_client.BaseComputeClient):
def create_security_group_rule(self, **kwargs):
"""Create a new security group rule.
diff --git a/tempest/lib/services/compute/security_groups_client.py b/tempest/lib/services/compute/security_groups_client.py
index 4db98c9..6b9c7e1 100644
--- a/tempest/lib/services/compute/security_groups_client.py
+++ b/tempest/lib/services/compute/security_groups_client.py
@@ -20,9 +20,10 @@
security_groups as schema
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
-class SecurityGroupsClient(rest_client.RestClient):
+class SecurityGroupsClient(base_compute_client.BaseComputeClient):
def list_security_groups(self, **params):
"""List all security groups for a user."""
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
index ea60e98..e370457 100644
--- a/tempest/lib/services/compute/server_groups_client.py
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class ServerGroupsClient(rest_client.RestClient):
+class ServerGroupsClient(base_compute_client.BaseComputeClient):
def create_server_group(self, **kwargs):
"""Create the server group.
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 46c4a49..a37f167 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -21,9 +21,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class ServersClient(rest_client.RestClient):
+class ServersClient(base_compute_client.BaseComputeClient):
def __init__(self, auth_provider, service, region,
enable_instance_password=True, **kwargs):
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
index 06aad77..a190e5f 100644
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -19,9 +19,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import services as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class ServicesClient(rest_client.RestClient):
+class ServicesClient(base_compute_client.BaseComputeClient):
def list_services(self, **params):
url = 'os-services'
diff --git a/tempest/lib/services/compute/snapshots_client.py b/tempest/lib/services/compute/snapshots_client.py
index de776bd..be41957 100644
--- a/tempest/lib/services/compute/snapshots_client.py
+++ b/tempest/lib/services/compute/snapshots_client.py
@@ -19,9 +19,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import snapshots as schema
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
-class SnapshotsClient(rest_client.RestClient):
+class SnapshotsClient(base_compute_client.BaseComputeClient):
def create_snapshot(self, volume_id, **kwargs):
"""Create a snapshot.
diff --git a/tempest/lib/services/compute/tenant_networks_client.py b/tempest/lib/services/compute/tenant_networks_client.py
index 44a97a9..04d8bab 100644
--- a/tempest/lib/services/compute/tenant_networks_client.py
+++ b/tempest/lib/services/compute/tenant_networks_client.py
@@ -16,9 +16,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import tenant_networks
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class TenantNetworksClient(rest_client.RestClient):
+class TenantNetworksClient(base_compute_client.BaseComputeClient):
def list_tenant_networks(self):
resp, body = self.get("os-tenant-networks")
diff --git a/tempest/lib/services/compute/tenant_usages_client.py b/tempest/lib/services/compute/tenant_usages_client.py
index e8da465..5a748c7 100644
--- a/tempest/lib/services/compute/tenant_usages_client.py
+++ b/tempest/lib/services/compute/tenant_usages_client.py
@@ -18,9 +18,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import tenant_usages
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class TenantUsagesClient(rest_client.RestClient):
+class TenantUsagesClient(base_compute_client.BaseComputeClient):
def list_tenant_usages(self, **params):
url = 'os-simple-tenant-usage'
diff --git a/tempest/lib/services/compute/versions_client.py b/tempest/lib/services/compute/versions_client.py
index ed82c74..eb4e7e9 100644
--- a/tempest/lib/services/compute/versions_client.py
+++ b/tempest/lib/services/compute/versions_client.py
@@ -19,9 +19,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import versions as schema
from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
-class VersionsClient(rest_client.RestClient):
+class VersionsClient(base_compute_client.BaseComputeClient):
def _get_base_version_url(self):
# NOTE: The URL which is got from keystone's catalog contains
diff --git a/tempest/lib/services/compute/volumes_client.py b/tempest/lib/services/compute/volumes_client.py
index 45a44de..41d9af2 100644
--- a/tempest/lib/services/compute/volumes_client.py
+++ b/tempest/lib/services/compute/volumes_client.py
@@ -19,9 +19,10 @@
from tempest.lib.api_schema.response.compute.v2_1 import volumes as schema
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.compute import base_compute_client
-class VolumesClient(rest_client.RestClient):
+class VolumesClient(base_compute_client.BaseComputeClient):
def list_volumes(self, detail=False, **params):
"""List all the volumes created."""
diff --git a/tempest/lib/services/network/networks_client.py b/tempest/lib/services/network/networks_client.py
index 0926634..24c2ec5 100644
--- a/tempest/lib/services/network/networks_client.py
+++ b/tempest/lib/services/network/networks_client.py
@@ -45,3 +45,7 @@
"""
uri = '/networks'
return self.create_resource(uri, kwargs)
+
+ def list_dhcp_agents_on_hosting_network(self, network_id):
+ uri = '/networks/%s/dhcp-agents' % network_id
+ return self.list_resources(uri)
diff --git a/tempest/lib/services/network/ports_client.py b/tempest/lib/services/network/ports_client.py
index 1793d6a..eba11d3 100644
--- a/tempest/lib/services/network/ports_client.py
+++ b/tempest/lib/services/network/ports_client.py
@@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib import exceptions as lib_exc
from tempest.lib.services.network import base
@@ -45,3 +46,10 @@
"""
uri = '/ports'
return self.create_resource(uri, kwargs)
+
+ def is_resource_deleted(self, id):
+ try:
+ self.show_port(id)
+ except lib_exc.NotFound:
+ return True
+ return False
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 8ba5f9a..988ee1a 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -63,7 +63,6 @@
cls.servers_client = cls.manager.servers_client
cls.interface_client = cls.manager.interfaces_client
# Neutron network client
- cls.network_client = cls.manager.network_client
cls.networks_client = cls.manager.networks_client
cls.ports_client = cls.manager.ports_client
cls.routers_client = cls.manager.routers_client
@@ -217,7 +216,7 @@
networks = kwargs.pop('networks')
# If there are no networks passed to us we look up
- # for the tenant's private networks and create a port
+ # for the project's private networks and create a port
# if there is only one private network. The same behaviour
# as we would expect when passing the call to the clients
# with no networks
@@ -283,7 +282,8 @@
self.assertEqual(name, volume['display_name'])
else:
self.assertEqual(name, volume['name'])
- self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume['id'], 'available')
# The volume retrieved on creation has a non-up-to-date status.
# Retrieval after it becomes active ensures correct details.
volume = self.volumes_client.show_volume(volume['id'])['volume']
@@ -478,8 +478,8 @@
self.addCleanup(
self.delete_wrapper, self.snapshots_client.delete_snapshot,
snapshot_id)
- self.snapshots_client.wait_for_snapshot_status(snapshot_id,
- 'available')
+ waiters.wait_for_snapshot_status(self.snapshots_client,
+ snapshot_id, 'available')
image_name = snapshot_image['name']
self.assertEqual(name, image_name)
@@ -492,14 +492,16 @@
server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
% CONF.compute.volume_device_name)['volumeAttachment']
self.assertEqual(volume_to_attach['id'], volume['id'])
- self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume['id'], 'in-use')
# Return the updated volume after the attachment
return self.volumes_client.show_volume(volume['id'])['volume']
def nova_volume_detach(self, server, volume):
self.servers_client.detach_volume(server['id'], volume['id'])
- self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume['id'], 'available')
volume = self.volumes_client.show_volume(volume['id'])['volume']
self.assertEqual('available', volume['status'])
@@ -600,6 +602,8 @@
def create_floating_ip(self, thing, pool_name=None):
"""Create a floating IP and associates to a server on Nova"""
+ if not pool_name:
+ pool_name = CONF.network.floating_network_name
floating_ip = (self.compute_floating_ips_client.
create_floating_ip(pool=pool_name)['floating_ip'])
self.addCleanup(self.delete_wrapper,
@@ -682,17 +686,15 @@
super(NetworkScenarioTest, cls).resource_setup()
cls.tenant_id = cls.manager.identity_client.tenant_id
- def _create_network(self, client=None, networks_client=None,
+ def _create_network(self, networks_client=None,
routers_client=None, tenant_id=None,
namestart='network-smoke-'):
- if not client:
- client = self.network_client
if not networks_client:
networks_client = self.networks_client
if not routers_client:
routers_client = self.routers_client
if not tenant_id:
- tenant_id = client.tenant_id
+ 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(
@@ -732,15 +734,13 @@
*args, **kwargs)
return agents_list['agents']
- def _create_subnet(self, network, client=None, subnets_client=None,
+ def _create_subnet(self, network, subnets_client=None,
routers_client=None, namestart='subnet-smoke',
**kwargs):
"""Create a subnet for the given network
within the cidr block configured for tenant networks.
"""
- if not client:
- client = self.network_client
if not subnets_client:
subnets_client = self.subnets_client
if not routers_client:
@@ -759,11 +759,11 @@
if ip_version == 6:
tenant_cidr = netaddr.IPNetwork(
- CONF.network.tenant_network_v6_cidr)
- num_bits = CONF.network.tenant_network_v6_mask_bits
+ CONF.network.project_network_v6_cidr)
+ num_bits = CONF.network.project_network_v6_mask_bits
else:
- tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- num_bits = CONF.network.tenant_network_mask_bits
+ tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
+ num_bits = CONF.network.project_network_mask_bits
result = None
str_cidr = None
@@ -791,7 +791,7 @@
raise
self.assertIsNotNone(result, 'Unable to allocate tenant network')
subnet = net_resources.DeletableSubnet(
- network_client=client, subnets_client=subnets_client,
+ subnets_client=subnets_client,
routers_client=routers_client, **result['subnet'])
self.assertEqual(subnet.cidr, str_cidr)
self.addCleanup(self.delete_wrapper, subnet.delete)
@@ -903,7 +903,7 @@
private_key,
should_connect=True,
servers_for_debug=None):
- if not CONF.network.tenant_networks_reachable:
+ if not CONF.network.project_networks_reachable:
msg = 'Tenant networks not configured to be reachable.'
LOG.info(msg)
return
@@ -1164,7 +1164,7 @@
router.update(admin_state_up=admin_state_up)
self.assertEqual(admin_state_up, router.admin_state_up)
- def create_networks(self, client=None, networks_client=None,
+ def create_networks(self, networks_client=None,
routers_client=None, subnets_client=None,
tenant_id=None, dns_nameservers=None):
"""Create a network with a subnet connected to a router.
@@ -1172,7 +1172,6 @@
The baremetal driver is a special case since all nodes are
on the same shared network.
- :param client: network client to create resources with.
:param tenant_id: id of tenant to create resources in.
:param dns_nameservers: list of dns servers to send to subnet.
:returns: network, subnet, router
@@ -1192,12 +1191,12 @@
subnet = None
else:
network = self._create_network(
- client=client, networks_client=networks_client,
+ networks_client=networks_client,
tenant_id=tenant_id)
router = self._get_router(client=routers_client,
tenant_id=tenant_id)
- subnet_kwargs = dict(network=network, client=client,
+ subnet_kwargs = dict(network=network,
subnets_client=subnets_client,
routers_client=routers_client)
# use explicit check because empty list is a valid option
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 2cbe6dc..4c2d31b 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -38,9 +38,9 @@
@classmethod
def skip_checks(cls):
super(TestNetworkAdvancedServerOps, cls).skip_checks()
- if not (CONF.network.tenant_networks_reachable
+ if not (CONF.network.project_networks_reachable
or CONF.network.public_network_id):
- msg = ('Either tenant_networks_reachable must be "true", or '
+ msg = ('Either project_networks_reachable must be "true", or '
'public_network_id must be defined.')
raise cls.skipException(msg)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 9e2477e..dfa4815 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -59,7 +59,7 @@
Determine which types of networks to test as follows:
* Configure tenant network checks (via the
- 'tenant_networks_reachable' key) if the Tempest host should
+ 'project_networks_reachable' key) if the Tempest host should
have direct connectivity to tenant networks. This is likely to
be the case if Tempest is running on the same host as a
single-node devstack installation with IP namespaces disabled.
@@ -81,9 +81,9 @@
@classmethod
def skip_checks(cls):
super(TestNetworkBasicOps, cls).skip_checks()
- if not (CONF.network.tenant_networks_reachable
+ if not (CONF.network.project_networks_reachable
or CONF.network.public_network_id):
- msg = ('Either tenant_networks_reachable must be "true", or '
+ msg = ('Either project_networks_reachable must be "true", or '
'public_network_id must be defined.')
raise cls.skipException(msg)
for ext in ['router', 'security-group']:
@@ -250,9 +250,8 @@
interface = self.interface_client.create_interface(
server_id=server['id'],
net_id=self.new_net.id)['interfaceAttachment']
- self.addCleanup(self.network_client.wait_for_resource_deletion,
- 'port',
- interface['port_id'], client=self.ports_client)
+ self.addCleanup(self.ports_client.wait_for_resource_deletion,
+ interface['port_id'])
self.addCleanup(self.delete_wrapper,
self.interface_client.delete_interface,
server['id'], interface['port_id'])
@@ -692,7 +691,7 @@
self._setup_network_and_servers()
# NOTE(kevinbenton): we have to use the admin credentials to check
- # for the distributed flag because self.router only has a tenant view.
+ # for the distributed flag because self.router only has a project view.
admin = self.admin_manager.routers_client.show_router(self.router.id)
if admin['router'].get('distributed', False):
msg = "Rescheduling test does not apply to distributed routers."
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 66c8ade..a52d8f9 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -44,9 +44,9 @@
if not (CONF.network_feature_enabled.ipv6
and CONF.network_feature_enabled.ipv6_subnet_attributes):
raise cls.skipException('IPv6 or its attributes not supported')
- if not (CONF.network.tenant_networks_reachable
+ if not (CONF.network.project_networks_reachable
or CONF.network.public_network_id):
- msg = ('Either tenant_networks_reachable must be "true", or '
+ msg = ('Either project_networks_reachable must be "true", or '
'public_network_id must be defined.')
raise cls.skipException(msg)
if CONF.baremetal.driver_enabled:
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 058f43b..d3f45c7 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -130,9 +130,9 @@
msg = ('Not currently supported when using vnic_type'
' direct or macvtap')
raise cls.skipException(msg)
- if not (CONF.network.tenant_networks_reachable or
+ if not (CONF.network.project_networks_reachable or
CONF.network.public_network_id):
- msg = ('Either tenant_networks_reachable must be "true", or '
+ msg = ('Either project_networks_reachable must be "true", or '
'public_network_id must be defined.')
raise cls.skipException(msg)
if not test.is_extension_enabled('security-group', 'network'):
@@ -194,7 +194,7 @@
client=tenant.manager.security_groups_client
)
- # don't use default secgroup since it allows in-tenant traffic
+ # don't use default secgroup since it allows in-project traffic
def_sg = self._create_empty_security_group(
namestart='secgroup_general-',
tenant_id=tenant.creds.tenant_id,
@@ -301,7 +301,7 @@
def _set_access_point(self, tenant):
# creates a server in a secgroup with rule allowing external ssh
- # in order to access tenant internal network
+ # in order to access project internal network
# workaround ip namespace
secgroups = tenant.security_groups.values()
name = 'server-{tenant}-access_point'.format(
@@ -321,7 +321,6 @@
def _create_tenant_network(self, tenant):
network, subnet, router = self.create_networks(
- client=tenant.manager.network_client,
networks_client=tenant.manager.networks_client,
routers_client=tenant.manager.routers_client,
subnets_client=tenant.manager.subnets_client)
@@ -468,7 +467,7 @@
if not self.credentials_provider.is_multi_tenant():
raise self.skipException("No secondary tenant defined")
try:
- # deploy new tenant
+ # deploy new project
self._deploy_tenant(self.alt_tenant)
self._verify_network_details(self.alt_tenant)
self._verify_mac_addr(self.alt_tenant)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index dcb095b..a9f2dff 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -119,7 +119,7 @@
@test.idempotent_id('7fff3fb3-91d8-4fd0-bd7d-0204f1f180ba')
@test.attr(type='smoke')
@test.services('compute', 'network')
- def test_server_basicops(self):
+ def test_server_basic_ops(self):
keypair = self.create_keypair()
self.security_group = self._create_security_group()
security_groups = [{'name': self.security_group['name']}]
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 6121a90..e7223c7 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -19,6 +19,7 @@
import testtools
from tempest.common.utils import data_utils
+from tempest.common import waiters
from tempest import config
from tempest import exceptions
from tempest.lib import decorators
@@ -67,14 +68,15 @@
self.snapshots_client.delete_snapshot(snapshot['id'])
try:
while self.snapshots_client.show_snapshot(
- snapshot['id'])['snapshot']:
+ snapshot['id'])['snapshot']:
time.sleep(1)
except lib_exc.NotFound:
pass
self.addCleanup(cleaner)
- self.volumes_client.wait_for_volume_status(volume['id'], 'available')
- self.snapshots_client.wait_for_snapshot_status(snapshot['id'],
- 'available')
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume['id'], 'available')
+ waiters.wait_for_snapshot_status(self.snapshots_client,
+ snapshot['id'], 'available')
self.assertEqual(snapshot_name, snapshot['display_name'])
return snapshot
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 71bb50e..25d825a 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -87,7 +87,8 @@
self.addCleanup(
self.snapshots_client.wait_for_resource_deletion, snap['id'])
self.addCleanup(self.snapshots_client.delete_snapshot, snap['id'])
- self.snapshots_client.wait_for_snapshot_status(snap['id'], 'available')
+ waiters.wait_for_snapshot_status(self.snapshots_client,
+ snap['id'], 'available')
# NOTE(e0ne): Cinder API v2 uses name instead of display_name
if 'display_name' in snap:
diff --git a/tempest/services/compute/json/keypairs_client.py b/tempest/services/compute/json/keypairs_client.py
index 2af55b2..fcb4f2a 100644
--- a/tempest/services/compute/json/keypairs_client.py
+++ b/tempest/services/compute/json/keypairs_client.py
@@ -14,6 +14,7 @@
# under the License.
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.api_schema.response.compute.v2_1 import keypairs as schemav21
from tempest.api_schema.response.compute.v2_2 import keypairs as schemav22
@@ -26,15 +27,21 @@
schema_versions_info = [{'min': None, 'max': '2.1', 'schema': schemav21},
{'min': '2.2', 'max': None, 'schema': schemav22}]
- def list_keypairs(self):
- resp, body = self.get("os-keypairs")
+ def list_keypairs(self, **params):
+ url = 'os-keypairs'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
body = json.loads(body)
schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.list_keypairs, resp, body)
return rest_client.ResponseBody(resp, body)
- def show_keypair(self, keypair_name):
- resp, body = self.get("os-keypairs/%s" % keypair_name)
+ def show_keypair(self, keypair_name, **params):
+ url = "os-keypairs/%s" % keypair_name
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
body = json.loads(body)
schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_keypair, resp, body)
@@ -48,8 +55,11 @@
self.validate_response(schema.create_keypair, resp, body)
return rest_client.ResponseBody(resp, body)
- def delete_keypair(self, keypair_name):
- resp, body = self.delete("os-keypairs/%s" % keypair_name)
+ def delete_keypair(self, keypair_name, **params):
+ url = "os-keypairs/%s" % keypair_name
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.delete(url)
schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.delete_keypair, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
deleted file mode 100644
index bcef36b..0000000
--- a/tempest/services/network/json/network_client.py
+++ /dev/null
@@ -1,102 +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.
-
-import time
-
-from tempest import exceptions
-from tempest.lib.common.utils import misc
-from tempest.lib import exceptions as lib_exc
-from tempest.lib.services.network import base
-
-
-class NetworkClient(base.BaseNetworkClient):
-
- """Tempest REST client for Neutron.
-
- Uses v2 of the Neutron API, since the V1 API has been removed from the
- code base.
-
- Implements create, delete, update, list and show for the basic Neutron
- abstractions (networks, sub-networks, routers, ports and floating IP):
-
- Implements add/remove interface to router using subnet ID / port ID
-
- It also implements list, show, update and reset for OpenStack Networking
- quotas
- """
-
- def wait_for_resource_deletion(self, resource_type, id, client=None):
- """Waits for a resource to be deleted."""
- start_time = int(time.time())
- while True:
- if self.is_resource_deleted(resource_type, id, client=client):
- return
- if int(time.time()) - start_time >= self.build_timeout:
- raise exceptions.TimeoutException
- time.sleep(self.build_interval)
-
- def is_resource_deleted(self, resource_type, id, client=None):
- if client is None:
- client = self
- method = 'show_' + resource_type
- try:
- getattr(client, method)(id)
- except AttributeError:
- raise Exception("Unknown resource type %s " % resource_type)
- except lib_exc.NotFound:
- return True
- return False
-
- def wait_for_resource_status(self, fetch, status, interval=None,
- timeout=None):
- """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
- @param status: the status that the resource has to reach
- @type status: String
- @param interval: the number of seconds to wait between each status
- query
- @type interval: Integer
- @param timeout: the maximum number of seconds to wait for the resource
- to reach the desired status
- @type timeout: Integer
- """
- if not interval:
- interval = self.build_interval
- if not timeout:
- timeout = self.build_timeout
- start_time = time.time()
-
- while time.time() - start_time <= timeout:
- resource = fetch()
- if resource['status'] == status:
- return
- time.sleep(interval)
-
- # At this point, the wait has timed out
- message = 'Resource %s' % (str(resource))
- message += ' failed to reach status %s' % status
- message += ' (current: %s)' % resource['status']
- message += ' within the required time %s' % timeout
- caller = misc.find_test_caller()
- if caller:
- message = '(%s) %s' % (caller, message)
- raise exceptions.TimeoutException(message)
-
- def list_router_interfaces(self, uuid):
- uri = '/ports?device_id=%s' % uuid
- return self.list_resources(uri)
-
- def list_dhcp_agent_hosting_network(self, network_id):
- uri = '/networks/%s/dhcp-agents' % network_id
- return self.list_resources(uri)
diff --git a/tempest/services/network/json/security_group_rules_client.py b/tempest/services/network/json/security_group_rules_client.py
deleted file mode 100644
index 944eba6..0000000
--- a/tempest/services/network/json/security_group_rules_client.py
+++ /dev/null
@@ -1,33 +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.lib.services.network import base
-
-
-class SecurityGroupRulesClient(base.BaseNetworkClient):
-
- def create_security_group_rule(self, **kwargs):
- uri = '/security-group-rules'
- post_data = {'security_group_rule': kwargs}
- return self.create_resource(uri, post_data)
-
- def show_security_group_rule(self, security_group_rule_id, **fields):
- uri = '/security-group-rules/%s' % security_group_rule_id
- return self.show_resource(uri, **fields)
-
- def delete_security_group_rule(self, security_group_rule_id):
- uri = '/security-group-rules/%s' % security_group_rule_id
- return self.delete_resource(uri)
-
- def list_security_group_rules(self, **filters):
- uri = '/security-group-rules'
- return self.list_resources(uri, **filters)
diff --git a/tempest/services/network/resources.py b/tempest/services/network/resources.py
index 5512075..329c54d 100644
--- a/tempest/services/network/resources.py
+++ b/tempest/services/network/resources.py
@@ -14,9 +14,13 @@
# under the License.
import abc
+import time
import six
+from tempest import exceptions
+from tempest.lib.common.utils import misc
+
class AttributeDict(dict):
"""Provide attribute access (dict.key) to dictionary values."""
@@ -37,7 +41,6 @@
def __init__(self, *args, **kwargs):
self.client = kwargs.pop('client', None)
- self.network_client = kwargs.pop('network_client', None)
self.networks_client = kwargs.pop('networks_client', None)
self.routers_client = kwargs.pop('routers_client', None)
self.subnets_client = kwargs.pop('subnets_client', None)
@@ -67,7 +70,35 @@
self.refresh()
return self
- return self.client.wait_for_resource_status(helper_get, status)
+ return self.wait_for_resource_status(helper_get, status)
+
+ def wait_for_resource_status(self, fetch, status):
+ """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
+ @param status: the status that the resource has to reach
+ @type status: String
+ """
+ interval = self.build_interval
+ timeout = self.build_timeout
+ start_time = time.time()
+
+ while time.time() - start_time <= timeout:
+ resource = fetch()
+ if resource['status'] == status:
+ return
+ time.sleep(interval)
+
+ # At this point, the wait has timed out
+ message = 'Resource %s' % (str(resource))
+ message += ' failed to reach status %s' % status
+ message += ' (current: %s)' % resource['status']
+ message += ' within the required time %s' % timeout
+ caller = misc.find_test_caller()
+ if caller:
+ message = '(%s) %s' % (caller, message)
+ raise exceptions.TimeoutException(message)
class DeletableNetwork(DeletableResource):
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 9ad8c27..78bda5d 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -210,7 +210,7 @@
:param contents: a string or a file like object to read object data
from; if None, a zero-byte put will be done
:param chunk_size: chunk size of data to write; it defaults to 65536;
- used only if the the contents object has a 'read'
+ used only if the contents object has a 'read'
method, eg. file-like objects, ignored otherwise
:param headers: additional headers to include in the request, if any
:param query_string: if set will be appended with '?' to generated path
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 6019cf5..ea5dbe5 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -151,42 +151,6 @@
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp)
- def wait_for_resource_status(self, stack_identifier, resource_name,
- status, failure_pattern='^.*_FAILED$'):
- """Waits for a Resource to reach a given status."""
- start = int(time.time())
- fail_regexp = re.compile(failure_pattern)
-
- while True:
- try:
- body = self.show_resource(
- stack_identifier, resource_name)['resource']
- except lib_exc.NotFound:
- # ignore this, as the resource may not have
- # been created yet
- pass
- else:
- resource_name = body['resource_name']
- resource_status = body['resource_status']
- if resource_status == status:
- return
- if fail_regexp.search(resource_status):
- raise exceptions.StackResourceBuildErrorException(
- resource_name=resource_name,
- stack_identifier=stack_identifier,
- resource_status=resource_status,
- resource_status_reason=body['resource_status_reason'])
-
- if int(time.time()) - start >= self.build_timeout:
- message = ('Resource %s failed to reach %s status '
- '(current %s) within the required time (%s s).' %
- (resource_name,
- status,
- resource_status,
- self.build_timeout))
- raise exceptions.TimeoutException(message)
- time.sleep(self.build_interval)
-
def wait_for_stack_status(self, stack_identifier, status,
failure_pattern='^.*_FAILED$'):
"""Waits for a Stack to reach a given status."""
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/services/volume/base/base_snapshots_client.py
index 5e5637a..68503dd 100644
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/services/volume/base/base_snapshots_client.py
@@ -10,13 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-import time
-
from oslo_log import log as logging
from oslo_serialization import jsonutils as json
from six.moves.urllib import parse as urllib
-from tempest import exceptions
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
@@ -70,43 +67,6 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
- # NOTE(afazekas): just for the wait function
- def _get_snapshot_status(self, snapshot_id):
- body = self.show_snapshot(snapshot_id)['snapshot']
- status = body['status']
- # NOTE(afazekas): snapshot can reach an "error"
- # state in a "normal" lifecycle
- if (status == 'error'):
- raise exceptions.SnapshotBuildErrorException(
- snapshot_id=snapshot_id)
-
- return status
-
- # NOTE(afazkas): Wait reinvented again. It is not in the correct layer
- def wait_for_snapshot_status(self, snapshot_id, status):
- """Waits for a Snapshot to reach a given status."""
- start_time = time.time()
- old_value = value = self._get_snapshot_status(snapshot_id)
- while True:
- dtime = time.time() - start_time
- time.sleep(self.build_interval)
- if value != old_value:
- LOG.info('Value transition from "%s" to "%s"'
- 'in %d second(s).', old_value,
- value, dtime)
- if (value == status):
- return value
-
- if dtime > self.build_timeout:
- message = ('Time Limit Exceeded! (%ds)'
- 'while waiting for %s, '
- 'but we got %s.' %
- (self.build_timeout, status, value))
- raise exceptions.TimeoutException(message)
- time.sleep(self.build_interval)
- old_value = value
- value = self._get_snapshot_status(snapshot_id)
-
def delete_snapshot(self, snapshot_id):
"""Delete Snapshot."""
resp, body = self.delete("snapshots/%s" % str(snapshot_id))
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
index f638bb6..4344802 100644
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -17,7 +17,6 @@
import six
from six.moves.urllib import parse as urllib
-from tempest.common import waiters
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
@@ -148,10 +147,6 @@
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
- def wait_for_volume_status(self, volume_id, status):
- """Waits for a Volume to reach a given status."""
- waiters.wait_for_volume_status(self, volume_id, status)
-
def is_resource_deleted(self, id):
try:
self.show_volume(id)
diff --git a/tempest/stress/__init__.py b/tempest/stress/__init__.py
index 987a023..e69de29 100644
--- a/tempest/stress/__init__.py
+++ b/tempest/stress/__init__.py
@@ -1,22 +0,0 @@
-# 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 warnings
-
-warnings.simplefilter('once', category=DeprecationWarning)
-warnings.warn(
- 'Stress tests are deprecated and will be removed from Tempest '
- 'in the Newton release.',
- DeprecationWarning)
-warnings.resetwarnings()
diff --git a/tempest/stress/cleanup.py b/tempest/stress/cleanup.py
index 1c1fb46..3b0a937 100644
--- a/tempest/stress/cleanup.py
+++ b/tempest/stress/cleanup.py
@@ -88,8 +88,8 @@
LOG.info("Cleanup::remove %s snapshots" % len(snaps))
for v in snaps:
try:
- admin_manager.snapshots_client.\
- wait_for_snapshot_status(v['id'], 'available')
+ waiters.wait_for_snapshot_status(
+ admin_manager.snapshots_client, v['id'], 'available')
admin_manager.snapshots_client.delete_snapshot(v['id'])
except Exception:
pass
@@ -105,8 +105,8 @@
LOG.info("Cleanup::remove %s volumes" % len(vols))
for v in vols:
try:
- admin_manager.volumes_client.\
- wait_for_volume_status(v['id'], 'available')
+ waiters.wait_for_volume_status(
+ admin_manager.volumes_client, v['id'], 'available')
admin_manager.volumes_client.delete_volume(v['id'])
except Exception:
pass
diff --git a/tempest/test.py b/tempest/test.py
index fe3c770..6ba4962 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -611,13 +611,25 @@
'dhcp': dhcp}
@classmethod
- def get_tenant_network(cls):
+ def get_tenant_network(cls, credentials_type='primary'):
"""Get the network to be used in testing
+ :param credentials_type: The type of credentials for which to get the
+ tenant network
+
:return: network dict including 'id' and 'name'
"""
+ # Get a manager for the given credentials_type, but at least
+ # always fall back on getting the manager for primary credentials
+ if isinstance(credentials_type, six.string_types):
+ manager = cls.get_client_manager(credential_type=credentials_type)
+ elif isinstance(credentials_type, list):
+ manager = cls.get_client_manager(roles=credentials_type[1:])
+ else:
+ manager = cls.get_client_manager()
+
# Make sure cred_provider exists and get a network client
- networks_client = cls.get_client_manager().compute_networks_client
+ networks_client = manager.compute_networks_client
cred_provider = cls._get_credentials_provider()
# In case of nova network, isolated tenants are not able to list the
# network configured in fixed_network_name, even if they can use it
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
index 57cfe97..2d0256a 100644
--- a/tempest/tests/cmd/test_javelin.py
+++ b/tempest/tests/cmd/test_javelin.py
@@ -213,8 +213,8 @@
name=self.fake_object['name'],
ip_version=fake_version)
- def test_create_volumes(self):
-
+ @mock.patch("tempest.common.waiters.wait_for_volume_status")
+ def test_create_volumes(self, mock_wait_for_volume_status):
self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
return_value=self.fake_client))
self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
@@ -228,12 +228,12 @@
mocked_function.assert_called_once_with(
size=self.fake_object['gb'],
display_name=self.fake_object['name'])
- mocked_function = self.fake_client.volumes.wait_for_volume_status
- mocked_function.assert_called_once_with(
- self.fake_object.body['volume']['id'],
+ mock_wait_for_volume_status.assert_called_once_with(
+ self.fake_client.volumes, self.fake_object.body['volume']['id'],
'available')
- def test_create_volume_existing(self):
+ @mock.patch("tempest.common.waiters.wait_for_volume_status")
+ def test_create_volume_existing(self, mock_wait_for_volume_status):
self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
return_value=self.fake_client))
self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
@@ -245,8 +245,7 @@
mocked_function = self.fake_client.volumes.create_volume
self.assertFalse(mocked_function.called)
- mocked_function = self.fake_client.volumes.wait_for_volume_status
- self.assertFalse(mocked_function.called)
+ self.assertFalse(mock_wait_for_volume_status.called)
def test_create_router(self):
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 193abc7..dc0ba6f 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -82,6 +82,28 @@
self.assertIn('v2.0', versions)
self.assertIn('v3.0', versions)
+ def test_get_versions_invalid_response(self):
+ # When the response doesn't contain a JSON response, an error is
+ # logged.
+ mock_log_error = self.useFixture(mockpatch.PatchObject(
+ verify_tempest_config.LOG, 'error')).mock
+
+ self.useFixture(mockpatch.PatchObject(
+ verify_tempest_config, '_get_unversioned_endpoint'))
+
+ # Simulated response is not JSON.
+ sample_body = (
+ '<html><head>Sample Response</head><body>This is the sample page '
+ 'for the web server. Why are you requesting it?</body></html>')
+ self.useFixture(mockpatch.Patch('httplib2.Http.request',
+ return_value=(None, sample_body)))
+
+ # service value doesn't matter, just needs to match what
+ # _get_api_versions puts in its client_dict.
+ self.assertRaises(ValueError, verify_tempest_config._get_api_versions,
+ os=mock.MagicMock(), service='keystone')
+ self.assertTrue(mock_log_error.called)
+
def test_verify_api_versions(self):
api_services = ['cinder', 'glance', 'keystone']
fake_os = mock.MagicMock()
diff --git a/tempest/tests/common/test_admin_available.py b/tempest/tests/common/test_admin_available.py
index 75401db..98e76b9 100644
--- a/tempest/tests/common/test_admin_available.py
+++ b/tempest/tests/common/test_admin_available.py
@@ -36,19 +36,19 @@
dynamic_creds, group='auth')
if use_accounts_file:
accounts = [{'username': 'u1',
- 'tenant_name': 't1',
+ 'project_name': 't1',
'password': 'p'},
{'username': 'u2',
- 'tenant_name': 't2',
+ 'project_name': 't2',
'password': 'p'}]
if admin_creds == 'role':
accounts.append({'username': 'admin',
- 'tenant_name': 'admin',
+ 'project_name': 'admin',
'password': 'p',
'roles': ['admin']})
elif admin_creds == 'type':
accounts.append({'username': 'admin',
- 'tenant_name': 'admin',
+ 'project_name': 'admin',
'password': 'p',
'types': ['admin']})
self.useFixture(mockpatch.Patch(
@@ -63,17 +63,17 @@
return_value=False))
if admin_creds:
username = 'u'
- tenant = 't'
+ project = 't'
password = 'p'
domain = 'd'
else:
username = None
- tenant = None
+ project = None
password = None
domain = None
cfg.CONF.set_default('admin_username', username, group='auth')
- cfg.CONF.set_default('admin_tenant_name', tenant, group='auth')
+ cfg.CONF.set_default('admin_project_name', project, group='auth')
cfg.CONF.set_default('admin_password', password, group='auth')
cfg.CONF.set_default('admin_domain_name', domain, group='auth')
diff --git a/tempest/tests/common/test_alt_available.py b/tempest/tests/common/test_alt_available.py
index db3f5ec..48e4a3e 100644
--- a/tempest/tests/common/test_alt_available.py
+++ b/tempest/tests/common/test_alt_available.py
@@ -36,7 +36,7 @@
dynamic_creds, group='auth')
if use_accounts_file:
accounts = [dict(username="u%s" % ii,
- tenant_name="t%s" % ii,
+ project_name="t%s" % ii,
password="p") for ii in creds]
self.useFixture(mockpatch.Patch(
'tempest.common.preprov_creds.read_accounts_yaml',
@@ -52,19 +52,19 @@
for ii in range(0, 2):
if len(creds) > ii:
username = 'u%s' % creds[ii]
- tenant = 't%s' % creds[ii]
+ project = 't%s' % creds[ii]
password = 'p'
domain = 'd'
else:
username = None
- tenant = None
+ project = None
password = None
domain = None
cfg.CONF.set_default('%susername' % cred_prefix[ii], username,
group='identity')
- cfg.CONF.set_default('%stenant_name' % cred_prefix[ii], tenant,
- group='identity')
+ cfg.CONF.set_default('%sproject_name' % cred_prefix[ii],
+ project, group='identity')
cfg.CONF.set_default('%spassword' % cred_prefix[ii], password,
group='identity')
cfg.CONF.set_default('%sdomain_name' % cred_prefix[ii], domain,
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index be4a6ee..5ef1eb3 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -30,7 +30,6 @@
json_tenants_client
from tempest.services.identity.v2.json import users_client as \
json_users_client
-from tempest.services.network.json import network_client as json_network_client
from tempest.services.network.json import routers_client
from tempest.tests import base
from tempest.tests import fake_config
@@ -61,8 +60,6 @@
creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
self.assertTrue(isinstance(creds.identity_admin_client,
json_iden_client.IdentityClient))
- self.assertTrue(isinstance(creds.network_admin_client,
- json_network_client.NetworkClient))
def _get_fake_admin_creds(self):
return credentials.get_credentials(
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index c45f6da..edd7186 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -46,7 +46,7 @@
lock_path=str(os.environ.get('OS_TEST_LOCK_PATH')),
)
self.conf.set_default('auth_version', 'v2', group='identity')
- for config_option in ['username', 'password', 'tenant_name']:
+ for config_option in ['username', 'password', 'project_name']:
# Identity group items
for prefix in ['', 'alt_', 'admin_']:
if prefix == 'admin_':
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index 6a01490..55f0c4e 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -251,7 +251,7 @@
"""Test empty alternate auth data with no effect
Assert that when alt_part is defined, no auth_data is provided,
- and the the corresponding original request element was not going to
+ and the corresponding original request element was not going to
be changed anyways, and exception is raised
"""
filters = {
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
new file mode 100644
index 0000000..dc355b4
--- /dev/null
+++ b/tempest/tests/test_base_test.py
@@ -0,0 +1,110 @@
+# Copyright 2016 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+
+from tempest import clients
+from tempest.common import credentials_factory as credentials
+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
+
+
+class TestBaseTestCase(base.TestCase):
+ def setUp(self):
+ super(TestBaseTestCase, self).setUp()
+ self.useFixture(fake_config.ConfigFixture())
+ self.fixed_network_name = 'fixed-net'
+ config.CONF.compute.fixed_network_name = self.fixed_network_name
+ config.CONF.service_available.neutron = True
+
+ @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+ @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+ @mock.patch.object(fixed_network, 'get_tenant_network')
+ def test_get_tenant_network(self, mock_gtn, mock_gprov, mock_gcm):
+ net_client = mock.Mock()
+ mock_prov = mock.Mock()
+ mock_gcm.return_value.compute_networks_client = net_client
+ mock_gprov.return_value = mock_prov
+
+ test.BaseTestCase.get_tenant_network()
+
+ mock_gcm.assert_called_once_with(credential_type='primary')
+ mock_gprov.assert_called_once_with()
+ mock_gtn.assert_called_once_with(mock_prov, net_client,
+ self.fixed_network_name)
+
+ @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+ @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+ @mock.patch.object(fixed_network, 'get_tenant_network')
+ @mock.patch.object(test.BaseTestCase, 'get_identity_version')
+ @mock.patch.object(credentials, 'is_admin_available')
+ @mock.patch.object(clients, 'Manager')
+ def test_get_tenant_network_with_nova_net(self, mock_man, mock_iaa,
+ mock_giv, mock_gtn, mock_gcp,
+ mock_gcm):
+ config.CONF.service_available.neutron = False
+ mock_prov = mock.Mock()
+ mock_admin_man = mock.Mock()
+ mock_iaa.return_value = True
+ mock_gcp.return_value = mock_prov
+ mock_man.return_value = mock_admin_man
+
+ test.BaseTestCase.get_tenant_network()
+
+ mock_man.assert_called_once_with(
+ mock_prov.get_admin_creds.return_value)
+ mock_iaa.assert_called_once_with(
+ identity_version=mock_giv.return_value)
+ mock_gcp.assert_called_once_with()
+ mock_gtn.assert_called_once_with(
+ mock_prov, mock_admin_man.compute_networks_client,
+ self.fixed_network_name)
+
+ @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+ @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+ @mock.patch.object(fixed_network, 'get_tenant_network')
+ def test_get_tenant_network_with_alt_creds(self, mock_gtn, mock_gprov,
+ mock_gcm):
+ net_client = mock.Mock()
+ mock_prov = mock.Mock()
+ mock_gcm.return_value.compute_networks_client = net_client
+ mock_gprov.return_value = mock_prov
+
+ test.BaseTestCase.get_tenant_network(credentials_type='alt')
+
+ mock_gcm.assert_called_once_with(credential_type='alt')
+ mock_gprov.assert_called_once_with()
+ mock_gtn.assert_called_once_with(mock_prov, net_client,
+ self.fixed_network_name)
+
+ @mock.patch.object(test.BaseTestCase, 'get_client_manager')
+ @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
+ @mock.patch.object(fixed_network, 'get_tenant_network')
+ def test_get_tenant_network_with_role_creds(self, mock_gtn, mock_gprov,
+ mock_gcm):
+ net_client = mock.Mock()
+ mock_prov = mock.Mock()
+ mock_gcm.return_value.compute_networks_client = net_client
+ mock_gprov.return_value = mock_prov
+ creds = ['foo_type', 'role1']
+
+ test.BaseTestCase.get_tenant_network(credentials_type=creds)
+
+ mock_gcm.assert_called_once_with(roles=['role1'])
+ mock_gprov.assert_called_once_with()
+ mock_gtn.assert_called_once_with(mock_prov, net_client,
+ self.fixed_network_name)
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index 98b045a..da5e2d7 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -201,7 +201,13 @@
if expected_to_skip:
self.assertRaises(testtools.TestCase.skipException, t.test_bar)
else:
- self.assertEqual(t.test_bar(), 0)
+ 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_requires_ext_decorator(self):
self._test_requires_ext_helper(expected_to_skip=False,
@@ -213,7 +219,7 @@
service='compute')
def test_requires_ext_decorator_with_all_ext_enabled(self):
- cfg.CONF.set_default('api_extensions', 'all',
+ cfg.CONF.set_default('api_extensions', ['all'],
group='compute-feature-enabled')
self._test_requires_ext_helper(expected_to_skip=False,
extension='random_ext',
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index db9db34..1811141 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import socket
+
import mock
from oslotest import mockpatch
import six
@@ -101,6 +103,20 @@
self.assertTrue(isinstance(self.client._get_connection(),
glance_http.VerifiedHTTPSConnection))
+ def test_get_connection_ipv4_https(self):
+ endpoint = 'https://127.0.0.1'
+ self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
+ self.client = glance_http.HTTPClient(self.fake_auth, {})
+ self.assertTrue(isinstance(self.client._get_connection(),
+ glance_http.VerifiedHTTPSConnection))
+
+ def test_get_connection_ipv6_https(self):
+ endpoint = 'https://[::1]'
+ self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
+ self.client = glance_http.HTTPClient(self.fake_auth, {})
+ self.assertTrue(isinstance(self.client._get_connection(),
+ glance_http.VerifiedHTTPSConnection))
+
def test_get_connection_url_not_fount(self):
self.useFixture(mockpatch.PatchObject(self.client, 'connection_class',
side_effect=httplib.InvalidURL()
@@ -146,6 +162,64 @@
self.assertEqual(6, len(kwargs.keys()))
+class TestVerifiedHTTPSConnection(base.TestCase):
+
+ @mock.patch('socket.socket')
+ @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
+ def test_connect_ipv4(self, mock_delegator, mock_socket):
+ connection = glance_http.VerifiedHTTPSConnection('127.0.0.1')
+ connection.connect()
+
+ mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM)
+ mock_delegator.assert_called_once_with(connection.context,
+ mock_socket.return_value)
+ mock_delegator.return_value.connect.assert_called_once_with(
+ (connection.host, 443))
+
+ @mock.patch('socket.socket')
+ @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
+ def test_connect_ipv6(self, mock_delegator, mock_socket):
+ connection = glance_http.VerifiedHTTPSConnection('[::1]')
+ connection.connect()
+
+ mock_socket.assert_called_once_with(socket.AF_INET6,
+ socket.SOCK_STREAM)
+ mock_delegator.assert_called_once_with(connection.context,
+ mock_socket.return_value)
+ mock_delegator.return_value.connect.assert_called_once_with(
+ (connection.host, 443, 0, 0))
+
+ @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
+ @mock.patch('socket.getaddrinfo',
+ side_effect=OSError('Gettaddrinfo failed'))
+ def test_connect_with_address_lookup_failure(self, mock_getaddrinfo,
+ mock_delegator):
+ connection = glance_http.VerifiedHTTPSConnection('127.0.0.1')
+ self.assertRaises(exceptions.RestClientException, connection.connect)
+
+ mock_getaddrinfo.assert_called_once_with(
+ connection.host, connection.port, 0, socket.SOCK_STREAM)
+
+ @mock.patch('socket.socket')
+ @mock.patch('socket.getaddrinfo',
+ return_value=[(2, 1, 6, '', ('127.0.0.1', 443))])
+ @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
+ def test_connect_with_socket_failure(self, mock_delegator,
+ mock_getaddrinfo,
+ mock_socket):
+ mock_delegator.return_value.connect.side_effect = \
+ OSError('Connect failed')
+
+ connection = glance_http.VerifiedHTTPSConnection('127.0.0.1')
+ self.assertRaises(exceptions.RestClientException, connection.connect)
+
+ mock_getaddrinfo.assert_called_once_with(
+ connection.host, connection.port, 0, socket.SOCK_STREAM)
+ mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM)
+ mock_delegator.return_value.connect.\
+ assert_called_once_with((connection.host, 443))
+
+
class TestResponseBodyIterator(base.TestCase):
def test_iter_default_chunk_size_64k(self):
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index 55f00ef..aba2aab 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -147,3 +147,23 @@
" @testtools.skipUnless(CONF.something, 'msg')"))))
self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
" @testtools.skipIf(CONF.something, 'msg')"))))
+
+ def test_dont_import_local_tempest_code_into_lib(self):
+ self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
+ "from tempest.common import waiters",
+ './tempest/common/compute.py'))))
+ self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
+ "from tempest import config",
+ './tempest/common/compute.py'))))
+ self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
+ "import tempest.exception",
+ './tempest/common/compute.py'))))
+ self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
+ "from tempest.common import waiters",
+ './tempest/lib/common/compute.py'))))
+ self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
+ "from tempest import config",
+ './tempest/lib/common/compute.py'))))
+ self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
+ "import tempest.exception",
+ './tempest/lib/common/compute.py'))))
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
new file mode 100644
index 0000000..03dbd9b
--- /dev/null
+++ b/tools/generate-tempest-plugins-list.py
@@ -0,0 +1,70 @@
+#! /usr/bin/env python
+
+# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# This script is intended to be run as part of a periodic proposal bot
+# job in OpenStack infrastructure.
+#
+# In order to function correctly, the environment in which the
+# script runs must have
+# * network access to the review.openstack.org Gerrit API
+# working directory
+# * network access to https://git.openstack.org/cgit
+
+import json
+import re
+import requests
+
+url = 'https://review.openstack.org/projects/'
+
+# This is what a project looks like
+'''
+ "openstack-attic/akanda": {
+ "id": "openstack-attic%2Fakanda",
+ "state": "READ_ONLY"
+ },
+'''
+
+
+def is_in_openstack_namespace(proj):
+ return proj.startswith('openstack/')
+
+# Rather than returning a 404 for a nonexistent file, cgit delivers a
+# 0-byte response to a GET request. It also does not provide a
+# Content-Length in a HEAD response, so the way we tell if a file exists
+# is to check the length of the entire GET response body.
+
+
+def has_tempest_plugin(proj):
+ r = requests.get(
+ "https://git.openstack.org/cgit/%s/plain/setup.cfg" % proj)
+ p = re.compile('^tempest\.test_plugins', re.M)
+ if p.findall(r.text):
+ return True
+ else:
+ False
+
+r = requests.get(url)
+# Gerrit prepends 4 garbage octets to the JSON, in order to counter
+# cross-site scripting attacks. Therefore we must discard it so the
+# json library won't choke.
+projects = sorted(filter(is_in_openstack_namespace, json.loads(r.text[4:])))
+
+found_plugins = filter(has_tempest_plugin, projects)
+
+# Every element of the found_plugins list begins with "openstack/".
+# We drop those initial 10 octets when printing the list.
+for project in found_plugins:
+ print(project[10:])
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
new file mode 100755
index 0000000..ecff508
--- /dev/null
+++ b/tools/generate-tempest-plugins-list.sh
@@ -0,0 +1,64 @@
+#!/bin/bash -ex
+
+# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# This script is intended to be run as a periodic proposal bot job
+# in OpenStack infrastructure, though you can run it as a one-off.
+#
+# In order to function correctly, the environment in which the
+# script runs must have
+# * a writable doc/source directory relative to the current
+# working directory
+# AND ( (
+# * git
+# * all git repos meant to be searched for plugins cloned and
+# at the desired level of up-to-datedness
+# * the environment variable git_dir pointing to the location
+# * of said git repositories
+# ) OR (
+# * network access to the review.openstack.org Gerrit API
+# working directory
+# * network access to https://git.openstack.org/cgit
+# ))
+#
+# If a file named data/tempest-plugins-registry.header or
+# data/tempest-plugins-registry.footer is found relative to the
+# current working directory, it will be prepended or appended to
+# the generated reStructuredText plugins table respectively.
+
+(
+declare -A plugins
+
+if [[ -r data/tempest-plugins-registry.header ]]; then
+ cat data/tempest-plugins-registry.header
+fi
+
+sorted_plugins=$(python tools/generate-tempest-plugins-list.py)
+
+for k in ${sorted_plugins}; do
+ project=${k:0:28}
+ giturl="git://git.openstack.org/openstack/${k:0:26}"
+ printf "|%-28s|%-73s|\n" "${project}" "${giturl}"
+ printf "+----------------------------+-------------------------------------------------------------------------+\n"
+done
+
+if [[ -r data/tempest-plugins-registry.footer ]]; then
+ cat data/tempest-plugins-registry.footer
+fi
+) > doc/source/plugin-registry.rst
+
+if [[ -n ${1} ]]; then
+ cp doc/source/plugin-registry.rst ${1}/doc/source/plugin-registry.rst
+fi