Merge "Snapshot list using 'limit' parameter"
diff --git a/HACKING.rst b/HACKING.rst
index 44519d4..b66fa24 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -16,10 +16,11 @@
- [T107] Check that a service tag isn't in the module path
- [T108] Check no hyphen at the end of rand_name() argument
- [T109] Cannot use testtools.skip decorator; instead use
- decorators.skip_because from tempest-lib
+ decorators.skip_because from tempest.lib
- [T110] Check that service client names of GET should be consistent
- [T111] Check that service client names of DELETE should be consistent
- [T112] Check that tempest.lib should not import local tempest code
+- [T113] Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
- [N322] Method's default argument shouldn't be mutable
Test Data/Configuration
@@ -156,33 +157,7 @@
Negative Tests
--------------
-Newly added negative tests should use the negative test framework. First step
-is to create an interface description in a python file under
-`tempest/api_schema/request/`. These descriptions consists of two important
-sections for the test (one of those is mandatory):
-
- - A resource (part of the URL of the request): Resources needed for a test
- must be created in `setUpClass` and registered with `set_resource` e.g.:
- `cls.set_resource("server", server['id'])`
-
- - A json schema: defines properties for a request.
-
-After that a test class must be added to automatically generate test scenarios
-out of the given interface description::
-
- load_tests = test.NegativeAutoTest.load_tests
-
- @test.SimpleNegativeAutoTest
- class SampleTestNegativeTestJSON(<your base class>, test.NegativeAutoTest):
- _service = 'compute'
- _schema = <your schema file>
-
-The class decorator `SimpleNegativeAutoTest` will automatically generate test
-cases out of the given schema in the attribute `_schema`.
-
-All negative tests should be added into a separate negative test file.
-If such a file doesn't exist for the particular resource being tested a new
-test file should be added.
+TODO: Write the guideline related to negative tests.
Test skips because of Known Bugs
--------------------------------
@@ -214,9 +189,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
@@ -332,9 +307,9 @@
# The created server should be in the detailed list of all servers
...
-Tempest-lib includes a ``check-uuid`` tool that will test for the existence
+Tempest.lib includes a ``check-uuid`` tool that will test for the existence
and uniqueness of idempotent_id metadata for every test. If you have
-tempest-lib installed you run the tool against Tempest by calling from the
+tempest installed you run the tool against Tempest by calling from the
tempest repo::
check-uuid
diff --git a/README.rst b/README.rst
index 7da83cd..650a1ed 100644
--- a/README.rst
+++ b/README.rst
@@ -58,7 +58,7 @@
#. You first need to install Tempest. This is done with pip after you check out
the Tempest repo::
- $ git clone https://github.com/openstack/tempest/
+ $ git clone http://git.openstack.org/openstack/tempest
$ pip install tempest/
This can be done within a venv, but the assumption for this guide is that
@@ -71,14 +71,14 @@
it's recommended that you copy or rename tempest.conf.sample to tempest.conf
and make those changes to that file in /etc/tempest
-#. Setup a local working Tempest dir. This is done using the tempest init
+#. Setup a local working Tempest dir. This is done by using the tempest init
command::
- tempest init cloud-01
+ $ tempest init cloud-01
works the same as::
- mkdir cloud-01 && cd cloud-01 && tempest init
+ $ mkdir cloud-01 && cd cloud-01 && tempest init
This will create a new directory for running a single Tempest configuration.
If you'd like to run Tempest against multiple OpenStack deployments the idea
diff --git a/REVIEWING.rst b/REVIEWING.rst
index f7334ad..bd6018d 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -2,7 +2,7 @@
======================
To start read the `OpenStack Common Review Checklist
-<https://wiki.openstack.org/wiki/ReviewChecklist#Common_Review_Checklist>`_
+<http://docs.openstack.org/infra/manual/developers.html#peer-review>`_
Ensuring code is executed
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 367be41..bcb1e3e 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -9,23 +9,38 @@
config file which explains the purpose of each individual option. You can see
the sample config file here: :ref:`tempest-sampleconf`
-Auth/Credentials
+Test Credentials
----------------
-Tempest currently has two different ways in configuration to provide credentials
-to use when running Tempest. One is a traditional set of configuration options
-in the tempest.conf file. These options are clearly labelled in the ``identity``
-section and let you specify a set of credentials for a regular user, a global
-admin user, and an alternate user, consisting of a username, password, and
-project/tenant name.
+Tempest allows for configuring a set of admin credentials in the ``auth``
+section, via the following parameters:
-The other method to provide credentials is using the accounts.yaml file. This
-file is used to specify an arbitrary number of users available to run tests
-with. You can specify the location of the file in the ``auth`` section in the
+ #. ``admin_username``
+ #. ``admin_password``
+ #. ``admin_project_name``
+ #. ``admin_domain_name``
+
+Admin credentials are not mandatory to run Tempest, but when provided they
+can be used to:
+
+- Run tests for admin APIs
+- Generate test credentials on the fly (see `Dynamic Credentials`_)
+
+Tempest allows for configuring pre-provisioned test credentials as well.
+This can be done in two different ways.
+
+One is to provide credentials is using the accounts.yaml file (see
+`Pre-Provisioned Credentials`_). This file is used to specify an arbitrary
+number of users available to run tests with.
+You can specify the location of the file in the ``auth`` section in the
tempest.conf file. To see the specific format used in the file please refer to
-the accounts.yaml.sample file included in Tempest. Eventually the config
-options for providing credentials to Tempest will be deprecated and removed in
-favor of the accounts.yaml file.
+the accounts.yaml.sample file included in Tempest.
+
+A second way - now deprecated - is a set of configuration options in the
+tempest.conf file (see `Legacy Credentials`_). These options are clearly
+labelled in the ``identity`` section and let you specify a set of credentials
+for a regular user and an alternate user, consisting of a username, password,
+project and domain name.
Keystone Connection Info
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -59,13 +74,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.
@@ -81,8 +96,9 @@
configured to use dynamic credentials.
-Pre-Provisioned Credentials (aka accounts.yaml or accounts file)
-""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+Pre-Provisioned Credentials
+"""""""""""""""""""""""""""
+
For a long time using dynamic credentials was the only method available if you
wanted to enable parallel execution of Tempest tests. However, this was
insufficient for certain use cases because of the admin credentials requirement
@@ -112,18 +128,19 @@
#. 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.
+Pre-Provisioned Credentials are also know as accounts.yaml or accounts file.
-Legacy Credentials (aka credentials config options)
-"""""""""""""""""""""""""""""""""""""""""""""""""""
+Legacy Credentials
+""""""""""""""""""
**Starting in the Liberty release this mechanism was deprecated; it will be
removed in a future release.**
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 +148,10 @@
#. ``username``
#. ``password``
- #. ``tenant_name``
- #. ``admin_username``
- #. ``admin_password``
- #. ``admin_tenant_name``
+ #. ``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
@@ -265,7 +279,7 @@
""""""""""""""""""
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.
@@ -287,10 +301,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
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 5f357b2..10364db 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -11,6 +11,7 @@
HACKING
REVIEWING
plugin
+ plugin-registry
library
microversion_testing
diff --git a/doc/source/library.rst b/doc/source/library.rst
index 64bd2c2..a89512c 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -7,15 +7,14 @@
test suites an interface for reusing pieces of tempest code. Any public
interface that lives in tempest/lib in the tempest repo is treated as a stable
public interface and it should be safe to external consume that. Every effort
-goes into maintaining backwards compatibility with any change. Just as with
-tempest-lib the library is self contained and doesn't have any dependency on
-other tempest internals outside of lib. (including no usage of tempest
-configuration)
+goes into maintaining backwards compatibility with any change.
+The library is self contained and doesn't have any dependency on
+other tempest internals outside of lib (including no usage of tempest
+configuration).
Stability
---------
-Just as tempest-lib before it any code that lives in tempest/lib will be treated
-as a stable interface, nothing has changed in regards to interface stability.
+Any code that lives in tempest/lib will be treated as a stable interface.
This means that any public interface under the tempest/lib directory is
expected to be a stable interface suitable for public consumption. However, for
any interfaces outside of tempest/lib in the tempest tree (unless otherwise
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 63ec04b..fc05b12 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -205,3 +205,7 @@
* `2.2`_
.. _2.2: http://docs.openstack.org/developer/nova/api_microversion_history.html#id2
+
+ * `2.10`_
+
+ .. _2.10: http://docs.openstack.org/developer/nova/api_microversion_history.html#id9
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/doc/source/plugin.rst b/doc/source/plugin.rst
index 2622c22..ad26741 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -11,13 +11,13 @@
=================
Creating a plugin is fairly straightforward and doesn't require much additional
-effort on top of creating a test suite using tempest-lib. One thing to note with
+effort on top of creating a test suite using tempest.lib. One thing to note with
doing this is that the interfaces exposed by tempest are not considered stable
(with the exception of configuration variables which ever effort goes into
ensuring backwards compatibility). You should not need to import anything from
tempest itself except where explicitly noted. If there is an interface from
tempest that you need to rely on in your plugin it likely needs to be migrated
-to tempest-lib. In that situation, file a bug, push a migration patch, etc. to
+to tempest.lib. In that situation, file a bug, push a migration patch, etc. to
expedite providing the interface in a reliable manner.
Plugin Cookiecutter
@@ -102,11 +102,6 @@
Then you need to ensure you locally define all of the methods in the abstract
class, you can refer to the api doc below for a reference of what that entails.
-Also, note eventually this abstract class will likely live in tempest-lib, when
-that migration occurs a deprecation shim will be added to tempest so as to not
-break any existing plugins. But, when that occurs migrating to using tempest-lib
-as the source for the abstract class will be prudent.
-
Abstract Plugin Class
---------------------
diff --git a/openstack-common.conf b/openstack-common.conf
deleted file mode 100644
index acb1437..0000000
--- a/openstack-common.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-[DEFAULT]
-
-# The list of modules to copy from openstack-common
-module=install_venv_common
-module=with_venv
-module=install_venv
-
-# The base module to hold the copy of openstack.common
-base=tempest
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/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml b/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
new file mode 100644
index 0000000..9baf035
--- /dev/null
+++ b/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+ This release is marking the end of Kilo release support in Tempest
+other:
+ - OpenStack Releases Supported after this release are **Liberty**
+ and **Mitaka**
+
+ The release under current development as of this tag is Newton,
+ meaning that every Tempest commit is also tested against master during
+ the Newton cycle. However, this does not necessarily mean that using
+ Tempest as of this tag will work against a Newton (or future releases)
+ cloud.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 5bd9176..b617b22 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -5,6 +5,7 @@
.. toctree::
:maxdepth: 1
+ v11.0.0
v10.0.0
unreleased
diff --git a/releasenotes/source/v11.0.0.rst b/releasenotes/source/v11.0.0.rst
new file mode 100644
index 0000000..84b145d
--- /dev/null
+++ b/releasenotes/source/v11.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v11.0.0 Release Notes
+=====================
+
+.. release-notes:: 11.0.0 Release Notes
+ :version: 11.0.0
diff --git a/requirements.txt b/requirements.txt
index 9dd57a9..dd73257 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,8 +3,6 @@
# process, which may cause wedges in the gate later.
pbr>=1.6 # Apache-2.0
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
-anyjson>=0.3.3 # BSD
-httplib2>=0.7.5 # MIT
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
testtools>=1.4.0 # MIT
paramiko>=1.16.0 # LGPL
@@ -12,16 +10,16 @@
testrepository>=0.0.18 # Apache-2.0/BSD
pyOpenSSL>=0.14 # Apache-2.0
oslo.concurrency>=3.5.0 # Apache-2.0
-oslo.config>=3.7.0 # Apache-2.0
+oslo.config>=3.9.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.log>=1.14.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.5.0 # Apache-2.0
six>=1.9.0 # MIT
-iso8601>=0.1.9 # MIT
-fixtures>=1.3.1 # Apache-2.0/BSD
+fixtures<2.0,>=1.3.1 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
PyYAML>=3.1.0 # MIT
-stevedore>=1.5.0 # Apache-2.0
+stevedore>=1.9.0 # Apache-2.0
PrettyTable<0.8,>=0.7 # BSD
os-testr>=0.4.1 # Apache-2.0
+urllib3>=1.8.3 # MIT
diff --git a/run_tempest.sh b/run_tempest.sh
index 8c8f25f..af01734 100755
--- a/run_tempest.sh
+++ b/run_tempest.sh
@@ -103,22 +103,25 @@
fi
if [ $update -eq 1 ]; then
echo "Updating virtualenv..."
- python tools/install_venv.py $installvenvopts
+ virtualenv $installvenvopts $venv
+ $venv/bin/pip install -U -r requirements.txt
fi
if [ -e ${venv} ]; then
wrapper="${with_venv}"
else
if [ $always_venv -eq 1 ]; then
# Automatically install the virtualenv
- python tools/install_venv.py $installvenvopts
+ virtualenv $installvenvopts $venv
wrapper="${with_venv}"
+ ${wrapper} pip install -U -r requirements.txt
else
echo -e "No virtual environment found...create one? (Y/n) \c"
read use_ve
if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
# Install the virtualenv and run the test suite in it
- python tools/install_venv.py $installvenvopts
+ virtualenv $installvenvopts $venv
wrapper=${with_venv}
+ ${wrapper} pip install -U -r requirements.txt
fi
fi
fi
diff --git a/run_tests.sh b/run_tests.sh
index 908056f..22314b6 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -114,22 +114,25 @@
fi
if [ $update -eq 1 ]; then
echo "Updating virtualenv..."
- python tools/install_venv.py $installvenvopts
+ virtualenv $installvenvopts $venv
+ $venv/bin/pip install -U -r requirements.txt -r test-requirements.txt
fi
if [ -e ${venv} ]; then
wrapper="${with_venv}"
else
if [ $always_venv -eq 1 ]; then
# Automatically install the virtualenv
- python tools/install_venv.py $installvenvopts
+ virtualenv $installvenvopts $venv
wrapper="${with_venv}"
+ ${wrapper} pip install -U -r requirements.txt -r test-requirements.txt
else
echo -e "No virtual environment found...create one? (Y/n) \c"
read use_ve
if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
# Install the virtualenv and run the test suite in it
- python tools/install_venv.py $installvenvopts
+ virtualenv $installvenvopts $venv
wrapper=${with_venv}
+ ${wrapper} pip install -U -r requirements.txt -r test-requirements.txt
fi
fi
fi
diff --git a/tempest/api/baremetal/admin/test_nodes.py b/tempest/api/baremetal/admin/test_nodes.py
index 8cc4d05..2c44665 100644
--- a/tempest/api/baremetal/admin/test_nodes.py
+++ b/tempest/api/baremetal/admin/test_nodes.py
@@ -138,14 +138,14 @@
body = self.client.get_node_boot_device(self.node['uuid'])
self.assertIn('boot_device', body)
self.assertIn('persistent', body)
- self.assertTrue(isinstance(body['boot_device'], six.string_types))
- self.assertTrue(isinstance(body['persistent'], bool))
+ self.assertIsInstance(body['boot_device'], six.string_types)
+ self.assertIsInstance(body['persistent'], bool)
@test.idempotent_id('3622bc6f-3589-4bc2-89f3-50419c66b133')
def test_get_node_supported_boot_devices(self):
body = self.client.get_node_supported_boot_devices(self.node['uuid'])
self.assertIn('supported_boot_devices', body)
- self.assertTrue(isinstance(body['supported_boot_devices'], list))
+ self.assertIsInstance(body['supported_boot_devices'], list)
@test.idempotent_id('f63b6288-1137-4426-8cfe-0d5b7eb87c06')
def test_get_console(self):
diff --git a/tempest/api/baremetal/admin/test_ports_negative.py b/tempest/api/baremetal/admin/test_ports_negative.py
index 8f04db9..5e3a33f 100644
--- a/tempest/api/baremetal/admin/test_ports_negative.py
+++ b/tempest/api/baremetal/admin/test_ports_negative.py
@@ -37,7 +37,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('30277ee8-0c60-4f1d-b125-0e51c2f43369')
def test_create_port_nonexsistent_node_id(self):
- node_id = str(data_utils.rand_uuid())
+ node_id = data_utils.rand_uuid()
address = data_utils.rand_mac_address()
self.assertRaises(lib_exc.BadRequest, self.create_port,
node_id=node_id, address=address)
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 96dedcf..95e7ef1 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -91,7 +91,7 @@
@test.idempotent_id('94c9bb4e-2c2a-4f3c-bb1f-5f0daf918e6d')
def test_create_flavor_with_uuid_id(self):
- flavor_id = str(uuid.uuid4())
+ flavor_id = data_utils.rand_uuid()
new_flavor_id = self._create_flavor(flavor_id)
self.assertEqual(new_flavor_id, flavor_id)
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 3854973..1b7eb12 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
@@ -145,4 +143,4 @@
self.assertRaises(lib_exc.NotFound,
self.client.remove_flavor_access,
new_flavor['id'],
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index f6ea3a4..a9e9644 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -49,7 +49,7 @@
@test.idempotent_id('c6ddbadb-c94e-4500-b12f-8ffc43843ff8')
def test_list_hosts_with_nonexistent_zone(self):
# If send the request with a nonexistent zone, the request will be
- # successful and no hosts will be retured
+ # successful and no hosts will be returned
hosts = self.client.list_hosts(zone='xxx')['hosts']
self.assertEqual(0, len(hosts))
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index f313f76..9c6df7f 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
@@ -38,7 +36,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('c136086a-0f67-4b2b-bc61-8482bd68989f')
def test_show_nonexistent_hypervisor(self):
- nonexistent_hypervisor_id = str(uuid.uuid4())
+ nonexistent_hypervisor_id = data_utils.rand_uuid()
self.assertRaises(
lib_exc.NotFound,
@@ -70,7 +68,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('02463d69-0ace-4d33-a4a8-93d7883a2bba')
def test_show_servers_with_nonexistent_hypervisor(self):
- nonexistent_hypervisor_id = str(uuid.uuid4())
+ nonexistent_hypervisor_id = data_utils.rand_uuid()
self.assertRaises(
lib_exc.NotFound,
@@ -87,7 +85,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('f60aa680-9a3a-4c7d-90e1-fae3a4891303')
def test_get_nonexistent_hypervisor_uptime(self):
- nonexistent_hypervisor_id = str(uuid.uuid4())
+ nonexistent_hypervisor_id = data_utils.rand_uuid()
self.assertRaises(
lib_exc.NotFound,
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_migrations.py b/tempest/api/compute/admin/test_migrations.py
index f81d665..6113c04 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -15,8 +15,10 @@
import testtools
from tempest.api.compute import base
+from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib import exceptions
from tempest import test
CONF = config.CONF
@@ -28,6 +30,7 @@
def setup_clients(cls):
super(MigrationsAdminTest, cls).setup_clients()
cls.client = cls.os_adm.migrations_client
+ cls.flavors_admin_client = cls.os_adm.flavors_client
@test.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
def test_list_migrations(self):
@@ -53,3 +56,50 @@
instance_uuids = [x['instance_uuid'] for x in body]
self.assertIn(server_id, instance_uuids)
+
+ def _flavor_clean_up(self, flavor_id):
+ try:
+ self.flavors_admin_client.delete_flavor(flavor_id)
+ self.flavors_admin_client.wait_for_resource_deletion(flavor_id)
+ except exceptions.NotFound:
+ pass
+
+ @test.idempotent_id('33f1fec3-ba18-4470-8e4e-1d888e7c3593')
+ @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+ 'Resize not available.')
+ def test_resize_server_revert_deleted_flavor(self):
+ # Tests that we can revert the resize on an instance whose original
+ # flavor has been deleted.
+
+ # First we have to create a flavor that we can delete so make a copy
+ # of the normal flavor from which we'd create a server.
+ flavor = self.flavors_admin_client.show_flavor(
+ self.flavor_ref)['flavor']
+ flavor = self.flavors_admin_client.create_flavor(
+ name=data_utils.rand_name('test_resize_flavor_'),
+ ram=flavor['ram'],
+ disk=flavor['disk'],
+ vcpus=flavor['vcpus']
+ )['flavor']
+ self.addCleanup(self._flavor_clean_up, flavor['id'])
+
+ # Now boot a server with the copied flavor.
+ server = self.create_test_server(
+ wait_until='ACTIVE', flavor=flavor['id'])
+
+ # Delete the flavor we used to boot the instance.
+ self._flavor_clean_up(flavor['id'])
+
+ # Now resize the server and wait for it to go into verify state.
+ self.servers_client.resize_server(server['id'], self.flavor_ref_alt)
+ waiters.wait_for_server_status(self.servers_client, server['id'],
+ 'VERIFY_RESIZE')
+
+ # Now revert the resize, it should be OK even though the original
+ # flavor used to boot the server was deleted.
+ self.servers_client.revert_resize_server(server['id'])
+ waiters.wait_for_server_status(self.servers_client, server['id'],
+ 'ACTIVE')
+
+ server = self.servers_client.show_server(server['id'])['server']
+ self.assertEqual(flavor['id'], server['flavor']['id'])
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 2907e26..b1f0755 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -185,7 +185,7 @@
# increment all of the values for updating the default quota class
for quota, default in six.iteritems(body):
# NOTE(sdague): we need to increment a lot, otherwise
- # there is a real chance that we go from -1 (unlimitted)
+ # there is a real chance that we go from -1 (unlimited)
# to a very small number which causes issues.
body[quota] = default + 100
LOG.debug("update limits for the default quota class set")
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 07a7a30..7437c14 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -12,8 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
import testtools
from tempest.api.compute import base
@@ -147,7 +145,7 @@
# migrate a non existent server
self.assertRaises(lib_exc.NotFound,
self.client.migrate_server,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
@test.idempotent_id('b0b17f83-d14e-4fc4-8f31-bcc9f3cfa629')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
diff --git a/tempest/api/compute/admin/test_servers_on_multinodes.py b/tempest/api/compute/admin/test_servers_on_multinodes.py
index 814a876..1bbde98 100644
--- a/tempest/api/compute/admin/test_servers_on_multinodes.py
+++ b/tempest/api/compute/admin/test_servers_on_multinodes.py
@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.compute import base
from tempest import config
from tempest import test
@@ -34,6 +36,9 @@
server_id)['server']['OS-EXT-SRV-ATTR:host']
@test.idempotent_id('26a9d5df-6890-45f2-abc4-a659290cb130')
+ @testtools.skipUnless(
+ test.is_scheduler_filter_enabled("SameHostFilter"),
+ 'SameHostFilter is not available.')
def test_create_servers_on_same_host(self):
server01 = self.create_test_server(wait_until='ACTIVE')['id']
@@ -45,6 +50,9 @@
self.assertEqual(host01, host02)
@test.idempotent_id('cc7ca884-6e3e-42a3-a92f-c522fcf25e8e')
+ @testtools.skipUnless(
+ test.is_scheduler_filter_enabled("DifferentHostFilter"),
+ 'DifferentHostFilter is not available.')
def test_create_servers_on_different_hosts(self):
server01 = self.create_test_server(wait_until='ACTIVE')['id']
@@ -56,6 +64,9 @@
self.assertNotEqual(host01, host02)
@test.idempotent_id('7869cc84-d661-4e14-9f00-c18cdc89cf57')
+ @testtools.skipUnless(
+ test.is_scheduler_filter_enabled("DifferentHostFilter"),
+ 'DifferentHostFilter is not available.')
def test_create_servers_on_different_hosts_with_list_of_servers(self):
server01 = self.create_test_server(wait_until='ACTIVE')['id']
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index 105c4e3..f71f046 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.compute.floating_ips import base
from tempest.common.utils import data_utils
from tempest import config
@@ -47,7 +45,7 @@
while True:
cls.non_exist_id = data_utils.rand_int_id(start=999)
if CONF.service_available.neutron:
- cls.non_exist_id = str(uuid.uuid4())
+ cls.non_exist_id = data_utils.rand_uuid()
if cls.non_exist_id not in cls.floating_ip_ids:
break
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
index c6c7347..ea56ae9 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest import config
@@ -39,7 +37,7 @@
# of non-existent floating IP
# Creating a non-existent floatingIP id
if CONF.service_available.neutron:
- non_exist_id = str(uuid.uuid4())
+ non_exist_id = data_utils.rand_uuid()
else:
non_exist_id = data_utils.rand_int_id(start=999)
self.assertRaises(lib_exc.NotFound,
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_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 4fb4e9a..8201363 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import netaddr
import time
from tempest.api.compute import base
@@ -44,6 +45,8 @@
def setup_clients(cls):
super(AttachInterfacesTestJSON, cls).setup_clients()
cls.client = cls.os.interfaces_client
+ cls.networks_client = cls.os.networks_client
+ cls.subnets_client = cls.os.subnets_client
cls.ports_client = cls.os.ports_client
def wait_for_interface_status(self, server, port_id, status):
@@ -120,6 +123,22 @@
self._check_interface(iface, port_id=port_id)
return iface
+ def _test_create_interface_by_fixed_ips(self, server, ifs):
+ network_id = ifs[0]['net_id']
+ ip_list = [
+ ifs[n]['fixed_ips'][0]['ip_address'] for n in range(0, len(ifs))]
+ ip = str(netaddr.IPAddress(sorted(ip_list)[-1]) + 1)
+
+ fixed_ips = [{'ip_address': ip}]
+ iface = self.client.create_interface(
+ server['id'], net_id=network_id,
+ fixed_ips=fixed_ips)['interfaceAttachment']
+ self.addCleanup(self.ports_client.delete_port, iface['port_id'])
+ iface = self.wait_for_interface_status(
+ server['id'], iface['port_id'], 'ACTIVE')
+ self._check_interface(iface, fixed_ip=ip)
+ return iface
+
def _test_show_interface(self, server, ifs):
iface = ifs[0]
_iface = self.client.show_interface(
@@ -182,6 +201,9 @@
iface = self._test_create_interface_by_port_id(server, ifs)
ifs.append(iface)
+ iface = self._test_create_interface_by_fixed_ips(server, ifs)
+ ifs.append(iface)
+
_ifs = (self.client.list_interfaces(server['id'])
['interfaceAttachments'])
self._compare_iface_list(ifs, _ifs)
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index f719bfc..c05045e 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
@@ -120,7 +119,9 @@
self.get_server_ip(self.server),
self.ssh_user,
self.password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.client)
self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
@test.idempotent_id('ac1ad47f-984b-4441-9274-c9079b7a0666')
@@ -132,7 +133,9 @@
self.get_server_ip(self.server),
self.ssh_user,
self.password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.client)
self.assertTrue(linux_client.hostname_equals_servername(self.name))
@test.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
@@ -323,7 +326,9 @@
self.get_server_ip(server_no_eph_disk),
self.ssh_user,
admin_pass,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server_no_eph_disk,
+ servers_client=self.client)
partition_num = len(linux_client.get_partitions().split('\n'))
# Explicit server deletion necessary for Juno compatibility
@@ -341,7 +346,9 @@
self.get_server_ip(server_with_eph_disk),
self.ssh_user,
admin_pass,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server_with_eph_disk,
+ servers_client=self.client)
partition_num_emph = len(linux_client.get_partitions().split('\n'))
self.assertEqual(partition_num + 1, partition_num_emph)
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 6796bb5..079465d 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -16,6 +16,7 @@
import testtools
from tempest.api.compute import base
+from tempest.common import compute
from tempest.common import waiters
from tempest import config
from tempest import test
@@ -84,16 +85,8 @@
def test_delete_server_while_in_shelved_state(self):
# Delete a server while it's VM state is Shelved
server = self.create_test_server(wait_until='ACTIVE')
- self.client.shelve_server(server['id'])
+ compute.shelve_server(self.client, server['id'])
- offload_time = CONF.compute.shelved_offload_time
- if offload_time >= 0:
- waiters.wait_for_server_status(self.client, server['id'],
- 'SHELVED_OFFLOADED',
- extra_timeout=offload_time)
- else:
- waiters.wait_for_server_status(self.client, server['id'],
- 'SHELVED')
self.client.delete_server(server['id'])
waiters.wait_for_server_termination(self.client, server['id'])
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 5b4417a..f01657b 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -19,6 +19,7 @@
import testtools
from tempest.api.compute import base
+from tempest.common import compute
from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
from tempest.common import waiters
@@ -90,7 +91,9 @@
linux_client = remote_client.RemoteClient(
self.get_server_ip(server),
self.ssh_user,
- new_password)
+ new_password,
+ server=server,
+ servers_client=self.client)
linux_client.validate_authentication()
def _test_reboot_server(self, reboot_type):
@@ -101,7 +104,9 @@
self.get_server_ip(server),
self.ssh_user,
self.password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.client)
boot_time = linux_client.get_boot_time()
self.client.reboot_server(self.server_id, type=reboot_type)
@@ -113,7 +118,9 @@
self.get_server_ip(server),
self.ssh_user,
self.password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.client)
new_boot_time = linux_client.get_boot_time()
self.assertTrue(new_boot_time > boot_time,
'%s > %s' % (new_boot_time, boot_time))
@@ -182,7 +189,9 @@
self.get_server_ip(rebuilt_server),
self.ssh_user,
password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=rebuilt_server,
+ servers_client=self.client)
linux_client.validate_authentication()
@test.idempotent_id('30449a88-5aff-4f9b-9866-6ee9b17f906d')
@@ -440,20 +449,8 @@
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
def test_shelve_unshelve_server(self):
- self.client.shelve_server(self.server_id)
-
- offload_time = CONF.compute.shelved_offload_time
- if offload_time >= 0:
- waiters.wait_for_server_status(self.client, self.server_id,
- 'SHELVED_OFFLOADED',
- extra_timeout=offload_time)
- else:
- waiters.wait_for_server_status(self.client, self.server_id,
- 'SHELVED')
-
- self.client.shelve_offload_server(self.server_id)
- waiters.wait_for_server_status(self.client, self.server_id,
- 'SHELVED_OFFLOADED')
+ compute.shelve_server(self.client, self.server_id,
+ force_shelve_offload=True)
server = self.client.show_server(self.server_id)['server']
image_name = server['name'] + '-shelved'
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index 74d34a2..baa4f9a 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -66,7 +66,9 @@
linux_client = remote_client.RemoteClient(
self.get_server_ip(server),
self.ssh_user, password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.client)
self.assertEqual(file_contents,
linux_client.exec_command(
'sudo cat %s' % file_path))
@@ -130,7 +132,9 @@
linux_client = remote_client.RemoteClient(
self.get_server_ip(server),
self.ssh_user, password,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.client)
for i in person:
self.assertEqual(base64.b64decode(i['contents']),
linux_client.exec_command(
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 0df6ead..10ea31d 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -18,6 +18,7 @@
import testtools
from tempest.api.compute import base
+from tempest.common import compute
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
@@ -29,8 +30,6 @@
class ServersNegativeTestJSON(base.BaseV2ComputeTest):
- credentials = ['primary', 'alt']
-
def setUp(self):
super(ServersNegativeTestJSON, self).setUp()
try:
@@ -47,7 +46,6 @@
def setup_clients(cls):
super(ServersNegativeTestJSON, cls).setup_clients()
cls.client = cls.servers_client
- cls.alt_client = cls.os_alt.servers_client
@classmethod
def resource_setup(cls):
@@ -271,16 +269,6 @@
self.server_id, name=new_name)
@test.attr(type=['negative'])
- @test.idempotent_id('543d84c1-dd2e-4c6d-8cb2-b9da0efaa384')
- def test_update_server_of_another_tenant(self):
- # Update name of a server that belongs to another tenant
-
- new_name = self.server_id + '_new'
- self.assertRaises(lib_exc.NotFound,
- self.alt_client.update_server, self.server_id,
- name=new_name)
-
- @test.attr(type=['negative'])
@test.idempotent_id('5c8e244c-dada-4590-9944-749c455b431f')
def test_update_server_name_length_exceeds_256(self):
# Update name of server exceed the name length limit
@@ -301,14 +289,6 @@
nonexistent_server)
@test.attr(type=['negative'])
- @test.idempotent_id('5c75009d-3eea-423e-bea3-61b09fd25f9c')
- def test_delete_a_server_of_another_tenant(self):
- # Delete a server that belongs to another tenant
- self.assertRaises(lib_exc.NotFound,
- self.alt_client.delete_server,
- self.server_id)
-
- @test.attr(type=['negative'])
@test.idempotent_id('75f79124-277c-45e6-a373-a1d6803f4cc4')
def test_delete_server_pass_negative_id(self):
# Pass an invalid string parameter to delete server
@@ -474,18 +454,7 @@
@test.attr(type=['negative'])
def test_shelve_shelved_server(self):
# shelve a shelved server.
- self.client.shelve_server(self.server_id)
-
- offload_time = CONF.compute.shelved_offload_time
- if offload_time >= 0:
- waiters.wait_for_server_status(self.client,
- self.server_id,
- 'SHELVED_OFFLOADED',
- extra_timeout=offload_time)
- else:
- waiters.wait_for_server_status(self.client,
- self.server_id,
- 'SHELVED')
+ compute.shelve_server(self.client, self.server_id)
server = self.client.show_server(self.server_id)['server']
image_name = server['name'] + '-shelved'
@@ -519,3 +488,45 @@
self.assertRaises(lib_exc.Conflict,
self.client.unshelve_server,
self.server_id)
+
+
+class ServersNegativeTestMultiTenantJSON(base.BaseV2ComputeTest):
+
+ credentials = ['primary', 'alt']
+
+ def setUp(self):
+ super(ServersNegativeTestMultiTenantJSON, self).setUp()
+ try:
+ waiters.wait_for_server_status(self.client, self.server_id,
+ 'ACTIVE')
+ except Exception:
+ self.__class__.server_id = self.rebuild_server(self.server_id)
+
+ @classmethod
+ def setup_clients(cls):
+ super(ServersNegativeTestMultiTenantJSON, cls).setup_clients()
+ cls.alt_client = cls.os_alt.servers_client
+
+ @classmethod
+ def resource_setup(cls):
+ super(ServersNegativeTestMultiTenantJSON, cls).resource_setup()
+ server = cls.create_test_server(wait_until='ACTIVE')
+ cls.server_id = server['id']
+
+ @test.attr(type=['negative'])
+ @test.idempotent_id('543d84c1-dd2e-4c6d-8cb2-b9da0efaa384')
+ def test_update_server_of_another_tenant(self):
+ # Update name of a server that belongs to another tenant
+
+ new_name = self.server_id + '_new'
+ self.assertRaises(lib_exc.NotFound,
+ self.alt_client.update_server, self.server_id,
+ name=new_name)
+
+ @test.attr(type=['negative'])
+ @test.idempotent_id('5c75009d-3eea-423e-bea3-61b09fd25f9c')
+ def test_delete_a_server_of_another_tenant(self):
+ # Delete a server that belongs to another tenant
+ self.assertRaises(lib_exc.NotFound,
+ self.alt_client.delete_server,
+ self.server_id)
diff --git a/tempest/api/compute/servers/test_virtual_interfaces_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index e038b82..912b0a1 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -13,9 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.compute import base
+from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -39,7 +38,7 @@
def test_list_virtual_interfaces_invalid_server_id(self):
# Negative test: Should not be able to GET virtual interfaces
# for an invalid server_id
- invalid_server_id = str(uuid.uuid4())
+ invalid_server_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
self.client.list_virtual_interfaces,
invalid_server_id)
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/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index e9c8e30..fa3fdfe 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -16,6 +16,7 @@
import testtools
from tempest.api.compute import base
+from tempest.common import compute
from tempest.common.utils.linux import remote_client
from tempest.common import waiters
from tempest import config
@@ -25,6 +26,7 @@
class AttachVolumeTestJSON(base.BaseV2ComputeTest):
+ max_microversion = '2.19'
def __init__(self, *args, **kwargs):
super(AttachVolumeTestJSON, self).__init__(*args, **kwargs)
@@ -62,7 +64,7 @@
self.volumes_client.wait_for_resource_deletion(self.volume['id'])
self.volume = None
- def _create_and_attach(self):
+ def _create_and_attach(self, shelve_server=False):
# Start a server and wait for it to become ready
self.admin_pass = self.image_ssh_password
self.server = self.create_test_server(
@@ -81,6 +83,22 @@
waiters.wait_for_volume_status(self.volumes_client,
self.volume['id'], 'available')
+ if shelve_server:
+ # NOTE(andreaf) If we are going to shelve a server, we should
+ # check first whether the server is ssh-able. Otherwise we won't
+ # be able to distinguish failures introduced by shelve from
+ # pre-existing ones. Also it's good to wait for cloud-init to be
+ # done and sshd server to be running before shelving to avoid
+ # breaking the VM
+ linux_client = remote_client.RemoteClient(
+ self.get_server_ip(self.server),
+ self.image_ssh_user,
+ self.admin_pass,
+ self.validation_resources['keypair']['private_key'])
+ linux_client.validate_authentication()
+ # If validation went ok, shelve the server
+ compute.shelve_server(self.servers_client, self.server['id'])
+
# Attach the volume to the server
self.attachment = self.servers_client.attach_volume(
self.server['id'],
@@ -111,7 +129,9 @@
self.get_server_ip(self.server),
self.image_ssh_user,
self.admin_pass,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.servers_client)
partitions = linux_client.get_partitions()
self.assertIn(self.device, partitions)
@@ -130,7 +150,9 @@
self.get_server_ip(self.server),
self.image_ssh_user,
self.admin_pass,
- self.validation_resources['keypair']['private_key'])
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.servers_client)
partitions = linux_client.get_partitions()
self.assertNotIn(self.device, partitions)
@@ -152,3 +174,68 @@
self.assertEqual(self.server['id'], body['serverId'])
self.assertEqual(self.volume['id'], body['volumeId'])
self.assertEqual(self.attachment['id'], body['id'])
+
+
+class AttachVolumeShelveTestJSON(AttachVolumeTestJSON):
+ """Testing volume with shelved instance.
+
+ This test checks the attaching and detaching volumes from
+ a shelved or shelved ofload instance.
+ """
+
+ min_microversion = '2.20'
+ max_microversion = 'latest'
+
+ def _unshelve_server_and_check_volumes(self, number_of_partition):
+ # Unshelve the instance and check that there are expected volumes
+ self.servers_client.unshelve_server(self.server['id'])
+ waiters.wait_for_server_status(self.servers_client,
+ self.server['id'],
+ 'ACTIVE')
+ linux_client = remote_client.RemoteClient(
+ self.get_server_ip(self.server['id']),
+ self.image_ssh_user,
+ self.admin_pass,
+ self.validation_resources['keypair']['private_key'],
+ server=self.server,
+ servers_client=self.servers_client)
+
+ command = 'grep vd /proc/partitions | wc -l'
+ nb_partitions = linux_client.exec_command(command).strip()
+ self.assertEqual(number_of_partition, nb_partitions)
+
+ @test.idempotent_id('13a940b6-3474-4c3c-b03f-29b89112bfee')
+ @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
+ 'Shelve is not available.')
+ @testtools.skipUnless(CONF.validation.run_validation,
+ 'SSH required for this test')
+ def test_attach_volume_shelved_or_offload_server(self):
+ self._create_and_attach(shelve_server=True)
+
+ # Unshelve the instance and check that there are two volumes
+ self._unshelve_server_and_check_volumes('2')
+
+ # Get Volume attachment of the server
+ volume_attachment = self.servers_client.show_volume_attachment(
+ self.server['id'],
+ self.attachment['id'])['volumeAttachment']
+ self.assertEqual(self.server['id'], volume_attachment['serverId'])
+ self.assertEqual(self.attachment['id'], volume_attachment['id'])
+ # Check the mountpoint is not None after unshelve server even in
+ # case of shelved_offloaded.
+ self.assertIsNotNone(volume_attachment['device'])
+
+ @test.idempotent_id('b54e86dd-a070-49c4-9c07-59ae6dae15aa')
+ @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
+ 'Shelve is not available.')
+ @testtools.skipUnless(CONF.validation.run_validation,
+ 'SSH required for this test')
+ def test_detach_volume_shelved_or_offload_server(self):
+ self._create_and_attach(shelve_server=True)
+
+ # Detach the volume
+ self._detach(self.server['id'], self.volume['id'])
+ self.attachment = None
+
+ # Unshelve the instance and check that there is only one volume
+ self._unshelve_server_and_check_volumes('1')
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index 990e429..f709c91 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -27,7 +27,7 @@
# ensure that the backing file for the volume group that Nova uses
# has space for at least 3 1G volumes!
# If you are running a Devstack environment, ensure that the
- # VOLUME_BACKING_FILE_SIZE is atleast 4G in your localrc
+ # VOLUME_BACKING_FILE_SIZE is at least 4G in your localrc
@classmethod
def skip_checks(cls):
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index d1c48c4..92f5ea8 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest import config
@@ -45,7 +43,7 @@
# Creating a nonexistent volume id
# Trying to GET a non existent volume
self.assertRaises(lib_exc.NotFound, self.client.show_volume,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('54a34226-d910-4b00-9ef8-8683e6c55846')
@@ -54,7 +52,7 @@
# Creating nonexistent volume id
# Trying to DELETE a non existent volume
self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('5125ae14-152b-40a7-b3c5-eae15e9022ef')
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
index f75b867..bb7a0a4 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
@@ -26,6 +27,7 @@
@test.attr(type='smoke')
@test.idempotent_id('c94b825e-0132-4686-8049-8a4a2bc09525')
+ @decorators.skip_because(bug='1567134')
def test_get_db_flavor(self):
# The expected flavor details should be returned
flavor = (self.client.show_db_flavor(self.db_flavor_ref)
@@ -37,6 +39,7 @@
@test.attr(type='smoke')
@test.idempotent_id('685025d6-0cec-4673-8a8d-995cb8e0d3bb')
+ @decorators.skip_because(bug='1567134')
def test_list_db_flavors(self):
flavor = (self.client.show_db_flavor(self.db_flavor_ref)
['flavor'])
@@ -58,6 +61,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/identity/admin/v2/test_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
index 14f4306..fd56285 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.identity import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
@@ -115,7 +113,7 @@
@test.idempotent_id('38373691-8551-453a-b074-4260ad8298ef')
def test_delete_role_non_existent(self):
# Attempt to delete a non existent role should fail
- non_existent_role = str(uuid.uuid4().hex)
+ non_existent_role = data_utils.rand_uuid_hex()
self.assertRaises(lib_exc.NotFound, self.roles_client.delete_role,
non_existent_role)
@@ -146,7 +144,7 @@
def test_assign_user_role_for_non_existent_role(self):
# Attempt to assign a non existent role to user should fail
(user, tenant, role) = self._get_role_params()
- non_existent_role = str(uuid.uuid4().hex)
+ non_existent_role = data_utils.rand_uuid_hex()
self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
tenant['id'], user['id'], non_existent_role)
@@ -155,7 +153,7 @@
def test_assign_user_role_for_non_existent_tenant(self):
# Attempt to assign a role on a non existent tenant should fail
(user, tenant, role) = self._get_role_params()
- non_existent_tenant = str(uuid.uuid4().hex)
+ non_existent_tenant = data_utils.rand_uuid_hex()
self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
non_existent_tenant, user['id'], role['id'])
@@ -205,7 +203,7 @@
self.roles_client.assign_user_role(tenant['id'],
user['id'],
role['id'])
- non_existent_role = str(uuid.uuid4().hex)
+ non_existent_role = data_utils.rand_uuid_hex()
self.assertRaises(lib_exc.NotFound, self.roles_client.delete_user_role,
tenant['id'], user['id'], non_existent_role)
@@ -217,7 +215,7 @@
self.roles_client.assign_user_role(tenant['id'],
user['id'],
role['id'])
- non_existent_tenant = str(uuid.uuid4().hex)
+ non_existent_tenant = data_utils.rand_uuid_hex()
self.assertRaises(lib_exc.NotFound, self.roles_client.delete_user_role,
non_existent_tenant, user['id'], role['id'])
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index a4c1afc..5169dae 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.identity import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
@@ -70,7 +68,7 @@
def test_delete_non_existent_tenant(self):
# Attempt to delete a non existent tenant should fail
self.assertRaises(lib_exc.NotFound, self.tenants_client.delete_tenant,
- str(uuid.uuid4().hex))
+ data_utils.rand_uuid_hex())
@test.attr(type=['negative'])
@test.idempotent_id('af16f44b-a849-46cb-9f13-a751c388f739')
@@ -130,7 +128,7 @@
def test_update_non_existent_tenant(self):
# Attempt to update a non existent tenant should fail
self.assertRaises(lib_exc.NotFound, self.tenants_client.update_tenant,
- str(uuid.uuid4().hex))
+ data_utils.rand_uuid_hex())
@test.attr(type=['negative'])
@test.idempotent_id('41704dc5-c5f7-4f79-abfa-76e6fedc570b')
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
index dee42b7..46ecba1 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.identity import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
@@ -108,7 +106,7 @@
def test_update_user_for_non_existent_user(self):
# Attempt to update a user non-existent user should fail
user_name = data_utils.rand_name('user')
- non_existent_id = str(uuid.uuid4())
+ non_existent_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.users_client.update_user,
non_existent_id, name=user_name)
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index 010e4a0..a3ada21 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -20,12 +20,18 @@
class GroupsV3TestJSON(base.BaseIdentityV3AdminTest):
+ @classmethod
+ def resource_setup(cls):
+ super(GroupsV3TestJSON, cls).resource_setup()
+ cls.data.setup_test_domain()
+
@test.idempotent_id('2e80343b-6c81-4ac3-88c7-452f3e9d5129')
def test_group_create_update_get(self):
name = data_utils.rand_name('Group')
description = data_utils.rand_name('Description')
group = self.groups_client.create_group(
- name=name, description=description)['group']
+ name=name, domain_id=self.data.domain['id'],
+ description=description)['group']
self.addCleanup(self.groups_client.delete_group, group['id'])
self.assertEqual(group['name'], name)
self.assertEqual(group['description'], description)
@@ -47,7 +53,8 @@
name = data_utils.rand_name('Group')
old_description = data_utils.rand_name('Description')
group = self.groups_client.create_group(
- name=name, description=old_description)['group']
+ name=name, domain_id=self.data.domain['id'],
+ description=old_description)['group']
self.addCleanup(self.groups_client.delete_group, group['id'])
new_name = data_utils.rand_name('UpdateGroup')
@@ -61,7 +68,8 @@
@test.idempotent_id('1598521a-2f36-4606-8df9-30772bd51339')
def test_group_users_add_list_delete(self):
name = data_utils.rand_name('Group')
- group = self.groups_client.create_group(name=name)['group']
+ group = self.groups_client.create_group(
+ name=name, domain_id=self.data.domain['id'])['group']
self.addCleanup(self.groups_client.delete_group, group['id'])
# add user into group
users = []
@@ -94,7 +102,8 @@
groups = []
for i in range(2):
name = data_utils.rand_name('Group')
- group = self.groups_client.create_group(name=name)['group']
+ group = self.groups_client.create_group(
+ name=name, domain_id=self.data.domain['id'])['group']
groups.append(group)
self.addCleanup(self.groups_client.delete_group, group['id'])
self.groups_client.add_group_user(group['id'], user['id'])
@@ -112,7 +121,8 @@
name = data_utils.rand_name('Group')
description = data_utils.rand_name('Description')
group = self.groups_client.create_group(
- name=name, description=description)['group']
+ name=name, domain_id=self.data.domain['id'],
+ description=description)['group']
self.addCleanup(self.groups_client.delete_group, group['id'])
group_ids.append(group['id'])
# List and Verify Groups
diff --git a/tempest/api/identity/v3/test_projects.py b/tempest/api/identity/v3/test_projects.py
index 995b77e..1574ab7 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -33,7 +33,7 @@
# user can successfully authenticate using his credentials and
# project name from received projects list
for project in resp['projects']:
- # 'user_domain_id' needs to be specified otherwise tempest_lib
+ # 'user_domain_id' needs to be specified otherwise tempest.lib
# assumes it to be 'default'
token_id, body = self.non_admin_token.get_token(
username=self.os.credentials.username,
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index 593bf2a..d5bed96 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -30,7 +30,7 @@
password = creds.password
user_domain_id = creds.user_domain_id
- # 'user_domain_id' needs to be specified otherwise tempest_lib assumes
+ # 'user_domain_id' needs to be specified otherwise tempest.lib assumes
# it to be 'default'
token_id, resp = self.non_admin_token.get_token(
user_id=user_id,
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index fc74326..14de8fd 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -14,9 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.image import base
+from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -38,7 +37,7 @@
@test.idempotent_id('668743d5-08ad-4480-b2b8-15da34f81d9f')
def test_get_non_existent_image(self):
# get the non-existent image
- non_existent_id = str(uuid.uuid4())
+ non_existent_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.client.show_image,
non_existent_id)
@@ -72,7 +71,7 @@
@test.idempotent_id('6fe40f1c-57bd-4918-89cc-8500f850f3de')
def test_delete_non_existing_image(self):
# delete non-existent image
- non_existent_image_id = str(uuid.uuid4())
+ non_existent_image_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
non_existent_image_id)
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
index 1aa2d11..dd5650f 100644
--- a/tempest/api/image/v2/test_images_tags_negative.py
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -12,8 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.image import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
@@ -27,7 +25,7 @@
def test_update_tags_for_non_existing_image(self):
# Update tag with non existing image.
tag = data_utils.rand_name('tag')
- non_exist_image = str(uuid.uuid4())
+ non_exist_image = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.client.add_image_tag,
non_exist_image, tag)
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 71edf74..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:
@@ -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..a5fb25c 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
@@ -397,22 +397,22 @@
self.assertEmpty(body['subnets'], "Public subnets visible")
-class BulkNetworkOpsTestJSON(base.BaseNetworkTest):
+class BulkNetworkOpsTest(base.BaseNetworkTest):
"""Tests the following operations in the Neutron API:
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)]
@@ -520,16 +520,16 @@
self.assertIn(n['id'], ports_list)
-class BulkNetworkOpsIpV6TestJSON(BulkNetworkOpsTestJSON):
+class BulkNetworkOpsIpV6Test(BulkNetworkOpsTest):
_ip_version = 6
-class NetworksIpV6TestJSON(NetworksTest):
+class NetworksIpV6Test(NetworksTest):
_ip_version = 6
@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)
@@ -576,7 +576,7 @@
'Subnet are not in the same network')
-class NetworksIpV6TestAttrs(NetworksIpV6TestJSON):
+class NetworksIpV6TestAttrs(NetworksIpV6Test):
@classmethod
def skip_checks(cls):
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..46b068b 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 = []
@@ -364,6 +364,23 @@
self._verify_router_interface(router['id'], subnet02['id'],
interface02['port_id'])
+ @test.idempotent_id('96522edf-b4b5-45d9-8443-fa11c26e6eff')
+ def test_router_interface_port_update_with_fixed_ip(self):
+ network = self.create_network()
+ subnet = self.create_subnet(network)
+ router = self._create_router(data_utils.rand_name('router-'))
+ fixed_ip = [{'subnet_id': subnet['id']}]
+ interface = self._add_router_interface_with_subnet_id(router['id'],
+ subnet['id'])
+ self.assertIn('port_id', interface)
+ self.assertIn('subnet_id', interface)
+ port = self.ports_client.show_port(interface['port_id'])
+ self.assertEqual(port['port']['id'], interface['port_id'])
+ router_port = self.ports_client.update_port(port['port']['id'],
+ fixed_ips=fixed_ip)
+ self.assertEqual(subnet['id'],
+ router_port['port']['fixed_ips'][0]['subnet_id'])
+
def _verify_router_interface(self, router_id, subnet_id, port_id):
show_port_body = self.ports_client.show_port(port_id)
interface_port = show_port_body['port']
diff --git a/tempest/api/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..b9765c8 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -13,10 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.network import base_security_groups as base
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -24,7 +23,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):
@@ -36,7 +35,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('424fd5c3-9ddc-486a-b45f-39bf0c820fc6')
def test_show_non_existent_security_group(self):
- non_exist_id = str(uuid.uuid4())
+ non_exist_id = data_utils.rand_uuid()
self.assertRaises(
lib_exc.NotFound, self.security_groups_client.show_security_group,
non_exist_id)
@@ -44,7 +43,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('4c094c09-000b-4e41-8100-9617600c02a6')
def test_show_non_existent_security_group_rule(self):
- non_exist_id = str(uuid.uuid4())
+ non_exist_id = data_utils.rand_uuid()
self.assertRaises(
lib_exc.NotFound,
self.security_group_rules_client.show_security_group_rule,
@@ -53,7 +52,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('1f1bb89d-5664-4956-9fcd-83ee0fa603df')
def test_delete_non_existent_security_group(self):
- non_exist_id = str(uuid.uuid4())
+ non_exist_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
self.security_groups_client.delete_security_group,
non_exist_id
@@ -91,7 +90,7 @@
@test.idempotent_id('4bf786fd-2f02-443c-9716-5b98e159a49a')
def test_create_security_group_rule_with_non_existent_remote_groupid(self):
group_create_body, _ = self._create_security_group()
- non_exist_id = str(uuid.uuid4())
+ non_exist_id = data_utils.rand_uuid()
# Create rule with non existent remote_group_id
group_ids = ['bad_group_id', non_exist_id]
@@ -110,7 +109,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,
@@ -204,7 +203,7 @@
@test.idempotent_id('be308db6-a7cf-4d5c-9baf-71bafd73f35e')
def test_create_security_group_rule_with_non_existent_security_group(self):
# Create security group rules with not existing security group.
- non_existent_sg = str(uuid.uuid4())
+ non_existent_sg = data_utils.rand_uuid()
self.assertRaises(
lib_exc.NotFound,
self.security_group_rules_client.create_security_group_rule,
@@ -214,7 +213,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 +222,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/telemetry/test_alarming_api_negative.py b/tempest/api/telemetry/test_alarming_api_negative.py
index 0701b54..3e34f8b 100644
--- a/tempest/api/telemetry/test_alarming_api_negative.py
+++ b/tempest/api/telemetry/test_alarming_api_negative.py
@@ -17,8 +17,6 @@
from tempest.lib import exceptions as lib_exc
from tempest import test
-import uuid
-
class TelemetryAlarmingNegativeTest(base.BaseAlarmingTest):
"""Negative tests for show_alarm, update_alarm, show_alarm_history tests
@@ -33,7 +31,7 @@
@test.idempotent_id('668743d5-08ad-4480-b2b8-15da34f81e7d')
def test_get_non_existent_alarm(self):
# get the non-existent alarm
- non_existent_id = str(uuid.uuid4())
+ non_existent_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.alarming_client.show_alarm,
non_existent_id)
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index f19717e..00acc7d 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -32,14 +32,9 @@
@classmethod
def resource_setup(cls):
super(VolumeMultiBackendV2Test, cls).resource_setup()
- # support 2 backends names, deprecated_for_removal.
- # keep support 2 backend names, in case they are not empty
- if CONF.volume.backend1_name and CONF.volume.backend2_name:
- cls.backend_names = {CONF.volume.backend1_name,
- CONF.volume.backend2_name}
- else:
- # read backend name from a list .
- cls.backend_names = set(CONF.volume.backend_names)
+
+ # read backend name from a list .
+ cls.backend_names = set(CONF.volume.backend_names)
cls.name_field = cls.special_fields['name_field']
cls.volume_type_id_list = []
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 7202881..6fc6f1c 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -28,9 +28,6 @@
self.volumes_client.delete_volume(volume_id)
self.volumes_client.wait_for_resource_deletion(volume_id)
- def _delete_volume_type(self, volume_type_id):
- self.volume_types_client.delete_volume_type(volume_type_id)
-
@test.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
def test_volume_type_list(self):
# List volume types.
@@ -54,7 +51,8 @@
name=vol_type_name,
extra_specs=extra_specs)['volume_type']
volume_types.append(vol_type)
- self.addCleanup(self._delete_volume_type, vol_type['id'])
+ self.addCleanup(self.volume_types_client.delete_volume_type,
+ vol_type['id'])
params = {self.name_field: vol_name,
'volume_type': volume_types[0]['id']}
@@ -103,7 +101,8 @@
name=name,
extra_specs=extra_specs)['volume_type']
self.assertIn('id', body)
- self.addCleanup(self._delete_volume_type, body['id'])
+ self.addCleanup(self.volume_types_client.delete_volume_type,
+ body['id'])
self.assertIn('name', body)
self.assertEqual(body['name'], name,
"The created volume_type name is not equal "
@@ -130,7 +129,8 @@
name = data_utils.rand_name("volume-type")
body = self.volume_types_client.create_volume_type(
name=name)['volume_type']
- self.addCleanup(self._delete_volume_type, body['id'])
+ self.addCleanup(self.volume_types_client.delete_volume_type,
+ body['id'])
# Create encryption type
encryption_type = self.volume_types_client.create_encryption_type(
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
index 29ce2e7..f3e52e9 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
@@ -53,7 +51,7 @@
self.assertRaises(
lib_exc.BadRequest,
self.volume_types_client.update_volume_type_extra_specs,
- self.volume_type['id'], str(uuid.uuid4()),
+ self.volume_type['id'], data_utils.rand_uuid(),
extra_spec)
@test.idempotent_id('9bf7a657-b011-4aec-866d-81c496fbe5c8')
@@ -84,7 +82,7 @@
self.assertRaises(
lib_exc.NotFound,
self.volume_types_client.create_volume_type_extra_specs,
- str(uuid.uuid4()), extra_specs)
+ data_utils.rand_uuid(), extra_specs)
@test.idempotent_id('c821bdc8-43a4-4bf4-86c8-82f3858d5f7d')
def test_create_none_body(self):
@@ -110,7 +108,7 @@
self.assertRaises(
lib_exc.NotFound,
self.volume_types_client.delete_volume_type_extra_specs,
- str(uuid.uuid4()), extra_specs.keys()[0])
+ data_utils.rand_uuid(), extra_specs.keys()[0])
@test.idempotent_id('dee5cf0c-cdd6-4353-b70c-e847050d71fb')
def test_list_nonexistent_volume_type_id(self):
@@ -118,7 +116,7 @@
self.assertRaises(
lib_exc.NotFound,
self.volume_types_client.list_volume_types_extra_specs,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
@test.idempotent_id('9f402cbd-1838-4eb4-9554-126a6b1908c9')
def test_get_nonexistent_volume_type_id(self):
@@ -127,7 +125,7 @@
self.assertRaises(
lib_exc.NotFound,
self.volume_types_client.show_volume_type_extra_specs,
- str(uuid.uuid4()), extra_specs.keys()[0])
+ data_utils.rand_uuid(), extra_specs.keys()[0])
@test.idempotent_id('c881797d-12ff-4f1a-b09d-9f6212159753')
def test_get_nonexistent_extra_spec_id(self):
@@ -136,7 +134,7 @@
self.assertRaises(
lib_exc.NotFound,
self.volume_types_client.show_volume_type_extra_specs,
- self.volume_type['id'], str(uuid.uuid4()))
+ self.volume_type['id'], data_utils.rand_uuid())
class ExtraSpecsNegativeV1Test(ExtraSpecsNegativeV2Test):
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index bccf20e..aff5466 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -13,9 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.volume import base
+from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -26,8 +25,8 @@
def test_create_with_nonexistent_volume_type(self):
# Should not be able to create volume with nonexistent volume_type.
self.name_field = self.special_fields['name_field']
- params = {self.name_field: str(uuid.uuid4()),
- 'volume_type': str(uuid.uuid4())}
+ params = {self.name_field: data_utils.rand_uuid(),
+ 'volume_type': data_utils.rand_uuid()}
self.assertRaises(lib_exc.NotFound,
self.volumes_client.create_volume, **params)
@@ -42,14 +41,14 @@
# Should not be able to get volume type with nonexistent type id.
self.assertRaises(lib_exc.NotFound,
self.volume_types_client.show_volume_type,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
@test.idempotent_id('6b3926d2-7d73-4896-bc3d-e42dfd11a9f6')
def test_delete_nonexistent_type_id(self):
# Should not be able to delete volume type with nonexistent type id.
self.assertRaises(lib_exc.NotFound,
self.volume_types_client.delete_volume_type,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
class VolumeTypesNegativeV1Test(VolumeTypesNegativeV2Test):
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 9c67579..e52216f 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -17,6 +17,7 @@
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib import exceptions
from tempest import test
import testtools
@@ -34,7 +35,6 @@
@classmethod
def resource_setup(cls):
super(VolumesV2ActionsTest, cls).resource_setup()
-
# Create a test shared instance
srv_name = data_utils.rand_name(cls.__name__ + '-Instance')
cls.server = cls.create_server(
@@ -61,10 +61,10 @@
@test.services('compute')
def test_attach_detach_volume_to_instance(self):
# Volume is attached and detached successfully from an instance
- mountpoint = '/dev/vdc'
self.client.attach_volume(self.volume['id'],
instance_uuid=self.server['id'],
- mountpoint=mountpoint)
+ mountpoint='/dev/%s' %
+ CONF.compute.volume_device_name)
waiters.wait_for_volume_status(self.client,
self.volume['id'], 'in-use')
self.client.detach_volume(self.volume['id'])
@@ -90,10 +90,10 @@
@test.services('compute')
def test_get_volume_attachment(self):
# Verify that a volume's attachment information is retrieved
- mountpoint = '/dev/vdc'
self.client.attach_volume(self.volume['id'],
instance_uuid=self.server['id'],
- mountpoint=mountpoint)
+ mountpoint='/dev/%s' %
+ CONF.compute.volume_device_name)
waiters.wait_for_volume_status(self.client,
self.volume['id'], 'in-use')
# NOTE(gfidente): added in reverse order because functions will be
@@ -105,7 +105,9 @@
volume = self.client.show_volume(self.volume['id'])['volume']
self.assertIn('attachments', volume)
attachment = self.client.get_attachment_from_volume(volume)
- self.assertEqual(mountpoint, attachment['device'])
+ self.assertEqual('/dev/%s' %
+ CONF.compute.volume_device_name,
+ attachment['device'])
self.assertEqual(self.server['id'], attachment['server_id'])
self.assertEqual(self.volume['id'], attachment['id'])
self.assertEqual(self.volume['id'], attachment['volume_id'])
@@ -122,11 +124,19 @@
self.volume['id'], image_name=image_name,
disk_format=CONF.volume.disk_format)['os-volume_upload_image']
image_id = body["image_id"]
- self.addCleanup(self.image_client.delete_image, image_id)
+ self.addCleanup(self._cleanup_image, image_id)
self.image_client.wait_for_image_status(image_id, 'active')
waiters.wait_for_volume_status(self.client,
self.volume['id'], 'available')
+ def _cleanup_image(self, image_id):
+ # Ignores the image deletion
+ # in the case that image wasn't created in the first place
+ try:
+ self.image_client.delete_image(image_id)
+ except exceptions.NotFound:
+ pass
+
@test.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
def test_reserve_unreserve_volume(self):
# Mark volume as reserved.
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 1b5e72a..77bfaf1 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest.common import waiters
@@ -44,14 +42,14 @@
def test_volume_get_nonexistent_volume_id(self):
# Should not be able to get a non-existent volume
self.assertRaises(lib_exc.NotFound, self.client.show_volume,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('555efa6e-efcd-44ef-8a3b-4a7ca4837a29')
def test_volume_delete_nonexistent_volume_id(self):
# Should not be able to delete a non-existent Volume
self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('1ed83a8a-682d-4dfb-a30e-ee63ffd6c049')
@@ -98,7 +96,7 @@
v_name = data_utils.rand_name('Volume')
metadata = {'Type': 'work'}
self.assertRaises(lib_exc.NotFound, self.client.create_volume,
- size='1', volume_type=str(uuid.uuid4()),
+ size='1', volume_type=data_utils.rand_uuid(),
display_name=v_name, metadata=metadata)
@test.attr(type=['negative'])
@@ -108,7 +106,7 @@
v_name = data_utils.rand_name('Volume')
metadata = {'Type': 'work'}
self.assertRaises(lib_exc.NotFound, self.client.create_volume,
- size='1', snapshot_id=str(uuid.uuid4()),
+ size='1', snapshot_id=data_utils.rand_uuid(),
display_name=v_name, metadata=metadata)
@test.attr(type=['negative'])
@@ -118,7 +116,7 @@
v_name = data_utils.rand_name('Volume')
metadata = {'Type': 'work'}
self.assertRaises(lib_exc.NotFound, self.client.create_volume,
- size='1', source_volid=str(uuid.uuid4()),
+ size='1', source_volid=data_utils.rand_uuid(),
display_name=v_name, metadata=metadata)
@test.attr(type=['negative'])
@@ -127,7 +125,8 @@
v_name = data_utils.rand_name('Volume')
metadata = {'Type': 'work'}
self.assertRaises(lib_exc.NotFound, self.client.update_volume,
- volume_id=str(uuid.uuid4()), display_name=v_name,
+ volume_id=data_utils.rand_uuid(),
+ display_name=v_name,
metadata=metadata)
@test.attr(type=['negative'])
@@ -188,7 +187,7 @@
self.assertRaises(lib_exc.NotFound,
self.client.attach_volume,
- str(uuid.uuid4()),
+ data_utils.rand_uuid(),
instance_uuid=server['id'],
mountpoint=self.mountpoint)
@@ -229,7 +228,7 @@
# Extend volume size when volume is nonexistent.
extend_size = int(self.volume['size']) + 1
self.assertRaises(lib_exc.NotFound, self.client.extend_volume,
- str(uuid.uuid4()), new_size=extend_size)
+ data_utils.rand_uuid(), new_size=extend_size)
@test.attr(type=['negative'])
@test.idempotent_id('aff8ba64-6d6f-4f2e-bc33-41a08ee9f115')
@@ -244,14 +243,14 @@
def test_reserve_volume_with_nonexistent_volume_id(self):
self.assertRaises(lib_exc.NotFound,
self.client.reserve_volume,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('eb467654-3dc1-4a72-9b46-47c29d22654c')
def test_unreserve_volume_with_nonexistent_volume_id(self):
self.assertRaises(lib_exc.NotFound,
self.client.unreserve_volume,
- str(uuid.uuid4()))
+ data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('449c4ed2-ecdd-47bb-98dc-072aeccf158c')
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 561b5bf..6707121 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -80,10 +80,9 @@
name=server_name,
wait_until='ACTIVE')
self.addCleanup(self.servers_client.delete_server, server['id'])
- mountpoint = '/dev/%s' % CONF.compute.volume_device_name
self.servers_client.attach_volume(
server['id'], volumeId=self.volume_origin['id'],
- device=mountpoint)
+ device='/dev/%s' % CONF.compute.volume_device_name)
waiters.wait_for_volume_status(self.volumes_client,
self.volume_origin['id'], 'in-use')
self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 54459ac..374979c 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -10,8 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest import config
@@ -36,7 +34,8 @@
s_name = data_utils.rand_name('snap')
self.assertRaises(lib_exc.NotFound,
self.snapshots_client.create_snapshot,
- volume_id=str(uuid.uuid4()), display_name=s_name)
+ volume_id=data_utils.rand_uuid(),
+ display_name=s_name)
@test.attr(type=['negative'])
@test.idempotent_id('bb9da53e-d335-4309-9c15-7e76fd5e4d6d')
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 6568627..1fa54c2 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -14,9 +14,11 @@
# License for the specific language governing permissions and limitations
# under the License.
+import random
from six.moves.urllib import parse
from tempest.api.volume import base
+from tempest.lib import decorators
from tempest import test
@@ -185,3 +187,29 @@
@test.idempotent_id('af55e775-8e4b-4feb-8719-215c43b0238c')
def test_volume_list_pagination(self):
self._test_pagination('volumes', ids=self.volume_id_list, detail=False)
+
+ @test.idempotent_id('46eff077-100b-427f-914e-3db2abcdb7e2')
+ @decorators.skip_because(bug='1572765')
+ def test_volume_list_with_detail_param_marker(self):
+ # Choosing a random volume from a list of volumes for 'marker'
+ # parameter
+ random_volume = random.choice(self.volume_id_list)
+
+ params = {'marker': random_volume}
+
+ # Running volume list using marker parameter
+ vol_with_marker = self.client.list_volumes(detail=True,
+ params=params)['volumes']
+
+ # Fetching the index of the random volume from volume_id_list
+ index_marker = self.volume_id_list.index(random_volume)
+
+ # The expected list with marker parameter
+ verify_volume_list = self.volume_id_list[:index_marker]
+
+ failed_msg = "Failed to list volume details by marker"
+
+ # Validating the expected list is the same like the observed list
+ self.assertEqual(verify_volume_list,
+ map(lambda x: x['id'],
+ vol_with_marker[::-1]), failed_msg)
diff --git a/tempest/api_schema/response/compute/__init__.py b/tempest/api_schema/response/compute/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api_schema/response/compute/__init__.py
+++ /dev/null
diff --git a/tempest/api_schema/response/compute/v2_1/__init__.py b/tempest/api_schema/response/compute/v2_1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api_schema/response/compute/v2_1/__init__.py
+++ /dev/null
diff --git a/tempest/api_schema/response/compute/v2_1/keypairs.py b/tempest/api_schema/response/compute/v2_1/keypairs.py
deleted file mode 100644
index 9c04c79..0000000
--- a/tempest/api_schema/response/compute/v2_1/keypairs.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright 2014 NEC Corporation. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-get_keypair = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'keypair': {
- 'type': 'object',
- 'properties': {
- 'public_key': {'type': 'string'},
- 'name': {'type': 'string'},
- 'fingerprint': {'type': 'string'},
- 'user_id': {'type': 'string'},
- 'deleted': {'type': 'boolean'},
- 'created_at': {'type': 'string'},
- 'updated_at': {'type': ['string', 'null']},
- 'deleted_at': {'type': ['string', 'null']},
- 'id': {'type': 'integer'}
-
- },
- 'additionalProperties': False,
- # When we run the get keypair API, response body includes
- # all the above mentioned attributes.
- # But in Nova API sample file, response body includes only
- # 'public_key', 'name' & 'fingerprint'. So only 'public_key',
- # 'name' & 'fingerprint' are defined as 'required'.
- 'required': ['public_key', 'name', 'fingerprint']
- }
- },
- 'additionalProperties': False,
- 'required': ['keypair']
- }
-}
-
-create_keypair = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'keypair': {
- 'type': 'object',
- 'properties': {
- 'fingerprint': {'type': 'string'},
- 'name': {'type': 'string'},
- 'public_key': {'type': 'string'},
- 'user_id': {'type': 'string'},
- 'private_key': {'type': 'string'}
- },
- 'additionalProperties': False,
- # When create keypair API is being called with 'Public key'
- # (Importing keypair) then, response body does not contain
- # 'private_key' So it is not defined as 'required'
- 'required': ['fingerprint', 'name', 'public_key', 'user_id']
- }
- },
- 'additionalProperties': False,
- 'required': ['keypair']
- }
-}
-
-delete_keypair = {
- 'status_code': [202],
-}
-
-list_keypairs = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'keypairs': {
- 'type': 'array',
- 'items': {
- 'type': 'object',
- 'properties': {
- 'keypair': {
- 'type': 'object',
- 'properties': {
- 'public_key': {'type': 'string'},
- 'name': {'type': 'string'},
- 'fingerprint': {'type': 'string'}
- },
- 'additionalProperties': False,
- 'required': ['public_key', 'name', 'fingerprint']
- }
- },
- 'additionalProperties': False,
- 'required': ['keypair']
- }
- }
- },
- 'additionalProperties': False,
- 'required': ['keypairs']
- }
-}
diff --git a/tempest/clients.py b/tempest/clients.py
index 8931706..0eded8b 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -46,6 +46,7 @@
from tempest.lib.services.compute.instance_usage_audit_log_client import \
InstanceUsagesAuditLogClient
from tempest.lib.services.compute.interfaces_client import InterfacesClient
+from tempest.lib.services.compute.keypairs_client import KeyPairsClient
from tempest.lib.services.compute.limits_client import LimitsClient
from tempest.lib.services.compute.migrations_client import MigrationsClient
from tempest.lib.services.compute.networks_client import NetworksClient \
@@ -87,6 +88,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
@@ -94,7 +97,6 @@
from tempest import manager
from tempest.services.baremetal.v1.json.baremetal_client import \
BaremetalClient
-from tempest.services.compute.json.keypairs_client import KeyPairsClient
from tempest.services.data_processing.v1_1.data_processing_client import \
DataProcessingClient
from tempest.services.database.json.flavors_client import \
@@ -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..5fab961 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
@@ -270,7 +262,7 @@
user = identity.get_user_by_username(tenants_admin,
tenant['id'], u['name'])
except tempest.lib.exceptions.NotFound:
- LOG.error("User: %s - not found" % u['user'])
+ LOG.error("User: %s - not found" % u['name'])
continue
for r in u['role_ids']:
try:
@@ -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.py b/tempest/cmd/cleanup.py
index 3706b54..caba4b5 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -190,7 +190,7 @@
rl_cl = self.admin_mgr.roles_client
tenant = identity.get_tenant_by_name(tn_cl,
- CONF.auth.admin_tenant_name)
+ CONF.auth.admin_project_name)
self.admin_tenant_id = tenant['id']
user = identity.get_user_by_username(tn_cl, self.admin_tenant_id,
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 99933dd..f51fc53 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -33,7 +33,7 @@
CONF_TENANTS = None
CONF_USERS = None
-IS_CEILOMETER = None
+IS_AODH = None
IS_CINDER = None
IS_GLANCE = None
IS_HEAT = None
@@ -51,14 +51,14 @@
global CONF_PUB_ROUTER
global CONF_TENANTS
global CONF_USERS
- global IS_CEILOMETER
+ global IS_AODH
global IS_CINDER
global IS_GLANCE
global IS_HEAT
global IS_NEUTRON
global IS_NOVA
- IS_CEILOMETER = CONF.service_available.ceilometer
+ IS_AODH = CONF.service_available.aodh
IS_CINDER = CONF.service_available.cinder
IS_GLANCE = CONF.service_available.glance
IS_HEAT = CONF.service_available.heat
@@ -70,25 +70,25 @@
CONF_PRIV_NETWORK_NAME = CONF.compute.fixed_network_name
CONF_PUB_NETWORK = CONF.network.public_network_id
CONF_PUB_ROUTER = CONF.network.public_router_id
- CONF_TENANTS = [CONF.auth.admin_tenant_name,
- CONF.identity.tenant_name,
- CONF.identity.alt_tenant_name]
+ CONF_TENANTS = [CONF.auth.admin_project_name,
+ CONF.identity.project_name,
+ CONF.identity.alt_project_name]
CONF_USERS = [CONF.auth.admin_username, CONF.identity.username,
CONF.identity.alt_username]
if IS_NEUTRON:
CONF_PRIV_NETWORK = _get_network_id(CONF.compute.fixed_network_name,
- CONF.auth.admin_tenant_name)
+ CONF.auth.admin_project_name)
CONF_NETWORKS = [CONF_PUB_NETWORK, CONF_PRIV_NETWORK]
-def _get_network_id(net_name, tenant_name):
+def _get_network_id(net_name, project_name):
am = credentials.AdminManager()
net_cl = am.networks_client
tn_cl = am.tenants_client
networks = net_cl.list_networks()
- tenant = identity.get_tenant_by_name(tn_cl, tenant_name)
+ tenant = identity.get_tenant_by_name(tn_cl, project_name)
t_id = tenant['id']
n_id = None
for net in networks['networks']:
@@ -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
@@ -711,7 +710,7 @@
class TelemetryAlarmService(BaseService):
def __init__(self, manager, **kwargs):
super(TelemetryAlarmService, self).__init__(kwargs)
- self.client = manager.telemetry_client
+ self.client = manager.alarming_client
def list(self):
client = self.client
@@ -910,7 +909,7 @@
if not self.is_save_state:
tenants = [tenant for tenant in tenants if (tenant['id']
not in self.saved_state_json['tenants'].keys()
- and tenant['name'] != CONF.auth.admin_tenant_name)]
+ and tenant['name'] != CONF.auth.admin_project_name)]
if self.is_preserve:
tenants = [tenant for tenant in tenants if tenant['name']
@@ -977,7 +976,7 @@
def get_tenant_cleanup_services():
tenant_services = []
- if IS_CEILOMETER:
+ if IS_AODH:
tenant_services.append(TelemetryAlarmService)
if IS_NOVA:
tenant_services.append(ServerService)
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index ac67ce4..633b9e9 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -54,21 +54,28 @@
real_prefix = getattr(sys, 'real_prefix', None)
base_prefix = getattr(sys, 'base_prefix', None)
prefix = sys.prefix
+ global_conf_dir = '/etc/tempest'
if (real_prefix is None and
- (base_prefix is None or base_prefix == prefix)):
+ (base_prefix is None or base_prefix == prefix) and
+ os.path.isdir(global_conf_dir)):
# Probably not running in a virtual environment.
# NOTE(andreaf) we cannot distinguish this case from the case of
# a virtual environment created with virtualenv, and running python3.
# Also if it appears we are not in virtual env and fail to find
# global config: '/etc/tempest', fall back to
# '[sys.prefix]/etc/tempest'
- global_conf_dir = '/etc/tempest'
- if os.path.isdir(global_conf_dir):
- return global_conf_dir
- else:
- return os.path.join(prefix, 'etc/tempest')
+ return global_conf_dir
else:
- return os.path.join(prefix, 'etc/tempest')
+ conf_dir = os.path.join(prefix, 'etc/tempest')
+ if os.path.isdir(conf_dir):
+ return conf_dir
+ else:
+ # NOTE: The prefix is gotten from the path which pyconfig.h is
+ # installed under. Some envs contain it under /usr/include, not
+ # /user/local/include. Then prefix becomes /usr on such envs.
+ # However, etc/tempest is installed under /usr/local and the bove
+ # path logic mismatches. This is a workaround for such envs.
+ return os.path.join(prefix, 'local/etc/tempest')
class TempestInit(command.Command):
@@ -78,6 +85,10 @@
parser = super(TempestInit, self).get_parser(prog_name)
parser.add_argument('dir', nargs='?', default=os.getcwd())
parser.add_argument('--config-dir', '-c', default=None)
+ parser.add_argument('--show-global-config-dir', '-s',
+ action='store_true', dest='show_global_dir',
+ help="Print the global config dir location, "
+ "then exit")
return parser
def generate_testr_conf(self, local_path):
@@ -91,8 +102,7 @@
def update_local_conf(self, conf_path, lock_dir, log_dir):
config_parse = moves.configparser.SafeConfigParser()
config_parse.optionxform = str
- with open(conf_path, 'w+') as conf_file:
- config_parse.readfp(conf_file)
+ with open(conf_path, 'a+') as conf_file:
# Set local lock_dir in tempest conf
if not config_parse.has_section('oslo_concurrency'):
config_parse.add_section('oslo_concurrency')
@@ -101,6 +111,7 @@
config_parse.set('DEFAULT', 'log_dir', log_dir)
# Set default log filename to tempest.log
config_parse.set('DEFAULT', 'log_file', 'tempest.log')
+ config_parse.write(conf_file)
def copy_config(self, etc_dir, config_dir):
shutil.copytree(config_dir, etc_dir)
@@ -149,4 +160,7 @@
def take_action(self, parsed_args):
config_dir = parsed_args.config_dir or get_tempest_default_config_dir()
+ if parsed_args.show_global_dir:
+ print("Global config dir is located at: %s" % config_dir)
+ sys.exit(0)
self.create_working_dir(parsed_args.dir, config_dir)
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index e3c1c64..2a4e314 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -127,6 +127,7 @@
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
@@ -134,7 +135,6 @@
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
@@ -264,7 +264,7 @@
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,
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 0ba322d..39fed63 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -15,8 +15,8 @@
# under the License.
import argparse
-import httplib2
import os
+import re
import sys
import traceback
@@ -29,6 +29,7 @@
from tempest import clients
from tempest.common import credentials_factory as credentials
from tempest import config
+import tempest.lib.common.http
CONF = config.CONF
@@ -77,9 +78,16 @@
not CONF.image_feature_enabled.api_v2, update)
+def _remove_version_project(url_path):
+ # The regex matches strings like /v2.0, /v3/, /v2.1/project-id/
+ return re.sub(r'/v\d+(\.\d+)?(/[^/]+)?', '', url_path)
+
+
def _get_unversioned_endpoint(base_url):
endpoint_parts = urlparse.urlparse(base_url)
- endpoint = endpoint_parts.scheme + '://' + endpoint_parts.netloc
+ new_path = _remove_version_project(endpoint_parts.path)
+ endpoint_parts = endpoint_parts._replace(path=new_path)
+ endpoint = urlparse.urlunparse(endpoint_parts)
return endpoint
@@ -89,13 +97,16 @@
'keystone': os.identity_client,
'cinder': os.volumes_client,
}
- client_dict[service].skip_path()
+ if service != 'keystone':
+ # Since keystone may be listening on a path, do not remove the path.
+ client_dict[service].skip_path()
endpoint = _get_unversioned_endpoint(client_dict[service].base_url)
- dscv = CONF.identity.disable_ssl_certificate_validation
- ca_certs = CONF.identity.ca_certificates_file
- raw_http = httplib2.Http(disable_ssl_certificate_validation=dscv,
- ca_certs=ca_certs)
- __, body = raw_http.request(endpoint, 'GET')
+
+ http = tempest.lib.common.http.ClosingHttp(
+ CONF.identity.disable_ssl_certificate_validation,
+ CONF.identity.ca_certificates_file)
+
+ __, body = http.request(endpoint, 'GET')
client_dict[service].reset_path()
try:
body = json.loads(body)
@@ -364,7 +375,16 @@
CONF_PARSER = moves.configparser.SafeConfigParser()
CONF_PARSER.optionxform = str
CONF_PARSER.readfp(conf_file)
- icreds = credentials.get_credentials_provider('verify_tempest_config')
+
+ # Indicate not to create network resources as part of getting credentials
+ net_resources = {
+ 'network': False,
+ 'router': False,
+ 'subnet': False,
+ 'dhcp': False
+ }
+ icreds = credentials.get_credentials_provider(
+ 'verify_tempest_config', network_resources=net_resources)
try:
os = clients.Manager(icreds.get_primary_creds())
services = check_service_availability(os, update)
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index b5c4547..7ebc283 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -157,3 +157,29 @@
% server['id'])
return body, servers
+
+
+def shelve_server(client, server_id, force_shelve_offload=False):
+ """Common wrapper utility to shelve server.
+
+ This method is a common wrapper to make server in 'SHELVED'
+ or 'SHELVED_OFFLOADED' state.
+
+ :param server_id: Server to make in shelve state
+ :param force_shelve_offload: Forcefully offload shelve server if it
+ is configured not to offload server
+ automatically after offload time.
+ """
+ client.shelve_server(server_id)
+
+ offload_time = CONF.compute.shelved_offload_time
+ if offload_time >= 0:
+ waiters.wait_for_server_status(client, server_id,
+ 'SHELVED_OFFLOADED',
+ extra_timeout=offload_time)
+ else:
+ waiters.wait_for_server_status(client, server_id, 'SHELVED')
+ if force_shelve_offload:
+ client.shelve_offload_server(server_id)
+ waiters.wait_for_server_status(client, server_id,
+ 'SHELVED_OFFLOADED')
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 0ca14a9..6cb43f3 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -26,7 +26,7 @@
"""This module provides factories of credential and credential providers
-Credentials providers and clients are (going to be) part of tempest-lib,
+Credentials providers and clients are (going to be) part of tempest.lib,
and so they may not hold any dependency to tempest configuration.
Methods in this module collect the relevant configuration details and pass
@@ -133,7 +133,7 @@
def get_creds_by_roles(self, roles, force_new=False):
msg = "Credentials being specified through the config file can not be"\
" used with tests that specify using credentials by roles. "\
- "Either exclude/skip the tests doing this or use either an "\
+ "Either exclude/skip the tests doing this or use either a "\
"test_accounts_file or dynamic credentials."
raise exceptions.InvalidConfiguration(msg)
@@ -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/generator/base_generator.py b/tempest/common/generator/base_generator.py
index 3a51f2e..0647edb 100644
--- a/tempest/common/generator/base_generator.py
+++ b/tempest/common/generator/base_generator.py
@@ -169,7 +169,7 @@
expected_result = generator_result[2]
element = path.pop()
if len(path) > 0:
- schema_snip = reduce(dict.get, path, schema)
+ schema_snip = six.moves.reduce(dict.get, path, schema)
schema_snip[element] = invalid_snippet
else:
schema[element] = invalid_snippet
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 97cc2ec..51f723b 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -219,9 +219,9 @@
else:
hashes = self.hash_dict['creds'].keys()
# NOTE(mtreinish): admin is a special case because of the increased
- # privlege set which could potentially cause issues on tests where that
- # is not expected. So unless the admin role isn't specified do not
- # allocate admin.
+ # privilege set which could potentially cause issues on tests where
+ # that is not expected. So unless the admin role isn't specified do
+ # not allocate admin.
admin_hashes = self.hash_dict['roles'].get(self.admin_role,
None)
if ((not roles or self.admin_role not in roles) and
@@ -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..3f573b7 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -12,6 +12,8 @@
import netaddr
import re
+import six
+import sys
import time
from oslo_log import log as logging
@@ -19,6 +21,7 @@
from tempest import config
from tempest import exceptions
from tempest.lib.common import ssh
+from tempest.lib.common.utils import misc as misc_utils
import tempest.lib.exceptions
CONF = config.CONF
@@ -26,16 +29,61 @@
LOG = logging.getLogger(__name__)
+def debug_ssh(function):
+ """Decorator to generate extra debug info in case off SSH failure"""
+ def wrapper(self, *args, **kwargs):
+ try:
+ return function(self, *args, **kwargs)
+ except tempest.lib.exceptions.SSHTimeout:
+ try:
+ original_exception = sys.exc_info()
+ caller = misc_utils.find_test_caller() or "not found"
+ if self.server:
+ msg = 'Caller: %s. Timeout trying to ssh to server %s'
+ LOG.debug(msg, caller, self.server)
+ if self.log_console and self.servers_client:
+ try:
+ msg = 'Console log for server %s: %s'
+ console_log = (
+ self.servers_client.get_console_output(
+ self.server['id'])['output'])
+ LOG.debug(msg, self.server['id'], console_log)
+ except Exception:
+ msg = 'Could not get console_log for server %s'
+ LOG.debug(msg, self.server['id'])
+ # re-raise the original ssh timeout exception
+ six.reraise(*original_exception)
+ finally:
+ # Delete the traceback to avoid circular references
+ _, _, trace = original_exception
+ del trace
+ return wrapper
+
+
class RemoteClient(object):
- def __init__(self, ip_address, username, password=None, pkey=None):
+ def __init__(self, ip_address, username, password=None, pkey=None,
+ server=None, servers_client=None):
+ """Executes commands in a VM over ssh
+
+ :param ip_address: IP address to ssh to
+ :param username: ssh username
+ :param password: ssh password (optional)
+ :param pkey: ssh public key (optional)
+ :param server: server dict, used for debugging purposes
+ :param servers_client: servers client, used for debugging purposes
+ """
+ self.server = server
+ self.servers_client = servers_client
ssh_timeout = CONF.validation.ssh_timeout
connect_timeout = CONF.validation.connect_timeout
+ self.log_console = CONF.compute_feature_enabled.console_output
self.ssh_client = ssh.Client(ip_address, username, password,
ssh_timeout, pkey=pkey,
channel_timeout=connect_timeout)
+ @debug_ssh
def exec_command(self, cmd):
# Shell options below add more clearness on failures,
# path is extended for some non-cirros guest oses (centos7)
@@ -43,6 +91,7 @@
LOG.debug("Remote command: %s" % cmd)
return self.ssh_client.exec_command(cmd)
+ @debug_ssh
def validate_authentication(self):
"""Validate ssh connection and authentication
@@ -118,7 +167,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/config.py b/tempest/config.py
index 4c3b04b..3e0f28f 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -15,6 +15,7 @@
from __future__ import print_function
+import functools
import logging as std_logging
import os
import tempfile
@@ -22,6 +23,7 @@
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log as logging
+import testtools
from tempest.test_discover import plugins
@@ -56,7 +58,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 +83,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 "
+ help="Password to use for an administrative user. This is "
+ "needed for authenticating requests made by project "
"isolation to create users and projects",
secret=True,
deprecated_group='identity'),
@@ -158,8 +163,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 +182,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 +253,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 +500,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',
@@ -690,22 +702,10 @@
choices=['public', 'admin', 'internal',
'publicURL', 'adminURL', 'internalURL'],
help="The endpoint type to use for the volume service."),
- cfg.StrOpt('backend1_name',
- default='',
- help='Name of the backend1 (must be declared in cinder.conf)',
- deprecated_for_removal=True),
- cfg.StrOpt('backend2_name',
- default='',
- help='Name of the backend2 (must be declared in cinder.conf)',
- deprecated_for_removal=True),
cfg.ListOpt('backend_names',
default=['BACKEND_1', 'BACKEND_2'],
help='A list of backend names separated by comma. '
- 'The backend name must be declared in cinder.conf',
- deprecated_opts=[cfg.DeprecatedOpt('BACKEND_1',
- group='volume'),
- cfg.DeprecatedOpt('BACKEND_2',
- group='volume')]),
+ 'The backend name must be declared in cinder.conf'),
cfg.StrOpt('storage_protocol',
default='iSCSI',
help='Backend protocol to target when creating volume types'),
@@ -1003,7 +1003,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.')
]
@@ -1149,7 +1149,7 @@
'shelve, snapshot, and suspend')
-# NOTE(deva): Ironic tests have been ported to tempest-lib. New config options
+# NOTE(deva): Ironic tests have been ported to tempest.lib. New config options
# should be added to ironic/ironic_tempest_plugin/config.py.
# However, these options need to remain here for testing stable
# branches until Liberty release reaches EOL.
@@ -1387,3 +1387,72 @@
CONF = TempestConfigProxy()
+
+
+def skip_unless_config(*args):
+ """Decorator to raise a skip if a config opt doesn't exist or is False
+
+ :param str group: The first arg, the option group to check
+ :param str name: The second arg, the option name to check
+ :param str msg: Optional third arg, the skip msg to use if a skip is raised
+ :raises testtools.TestCaseskipException: If the specified config option
+ doesn't exist or it exists and evaluates to False
+ """
+ def decorator(f):
+ group = args[0]
+ name = args[1]
+
+ @functools.wraps(f)
+ def wrapper(self, *func_args, **func_kwargs):
+ if not hasattr(CONF, group):
+ msg = "Config group %s doesn't exist" % group
+ raise testtools.TestCase.skipException(msg)
+
+ conf_group = getattr(CONF, group)
+ if not hasattr(conf_group, name):
+ msg = "Config option %s.%s doesn't exist" % (group,
+ name)
+ raise testtools.TestCase.skipException(msg)
+
+ value = getattr(conf_group, name)
+ if not value:
+ if len(args) == 3:
+ msg = args[2]
+ else:
+ msg = "Config option %s.%s is false" % (group,
+ name)
+ raise testtools.TestCase.skipException(msg)
+ return f(self, *func_args, **func_kwargs)
+ return wrapper
+ return decorator
+
+
+def skip_if_config(*args):
+ """Raise a skipException if a config exists and is True
+
+ :param str group: The first arg, the option group to check
+ :param str name: The second arg, the option name to check
+ :param str msg: Optional third arg, the skip msg to use if a skip is raised
+ :raises testtools.TestCase.skipException: If the specified config option
+ exists and evaluates to True
+ """
+ def decorator(f):
+ group = args[0]
+ name = args[1]
+
+ @functools.wraps(f)
+ def wrapper(self, *func_args, **func_kwargs):
+ if hasattr(CONF, group):
+ conf_group = getattr(CONF, group)
+ if hasattr(conf_group, name):
+ value = getattr(conf_group, name)
+ if value:
+ if len(args) == 3:
+ msg = args[2]
+ else:
+ msg = "Config option %s.%s is false" % (group,
+ name)
+ raise testtools.TestCase.skipException(msg)
+ return f(self, *func_args, **func_kwargs)
+ return wrapper
+ return decorator
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 031df7f..92f335f 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -82,11 +82,6 @@
message = "Image %(image_id)s failed to become ACTIVE in the allotted time"
-class EC2RegisterImageException(TempestException):
- message = ("Image %(image_id)s failed to become 'available' "
- "in the allotted time")
-
-
class VolumeBuildErrorException(TempestException):
message = "Volume %(volume_id)s failed to build and is in ERROR status"
@@ -108,25 +103,10 @@
"due to '%(stack_status_reason)s'")
-class StackResourceBuildErrorException(TempestException):
- message = ("Resource %(resource_name)s in stack %(stack_identifier)s is "
- "in %(resource_status)s status due to "
- "'%(resource_status_reason)s'")
-
-
-class AuthenticationFailure(TempestException):
- message = ("Authentication with user %(user)s and password "
- "%(password)s failed auth using tenant %(tenant)s.")
-
-
class EndpointNotFound(TempestException):
message = "Endpoint not found"
-class ImageFault(TempestException):
- message = "Got image fault"
-
-
class IdentityError(TempestException):
message = "Got identity error"
@@ -135,12 +115,8 @@
message = "The server is not reachable via the configured network"
-class TearDownException(TempestException):
- message = "%(num)d cleanUp operation failed"
-
-
# NOTE(andreaf) This exception is added here to facilitate the migration
-# of get_network_from_name and preprov_creds to tempest-lib, and it should
+# of get_network_from_name and preprov_creds to tempest.lib, and it should
# be migrated along with them
class InvalidTestResource(TempestException):
message = "%(name) is not a valid %(type), or the name is ambiguous"
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index f4b76e5..5943adf 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -119,11 +119,6 @@
T108
"""
- if './tempest/api/network/' in filename:
- # Network API tests are migrating from Tempest to Neutron repo now.
- # So here should avoid network API tests checks.
- return
-
msg = "T108: hyphen should not be specified at the end of rand_name()"
if RAND_NAME_HYPHEN_RE.match(logical_line):
return 0, msg
@@ -146,12 +141,12 @@
"""
if TESTTOOLS_SKIP_DECORATOR.match(logical_line):
yield (0, "T109: Cannot use testtools.skip decorator; instead use "
- "decorators.skip_because from tempest-lib")
+ "decorators.skip_because from tempest.lib")
def _common_service_clients_check(logical_line, physical_line, filename,
ignored_list_file=None):
- if 'tempest/services/' not in filename:
+ if not re.match('tempest/(lib/)?services/.*', filename):
return False
if ignored_list_file is not None:
@@ -246,6 +241,22 @@
yield (0, msg)
+def use_rand_uuid_instead_of_uuid4(logical_line, filename):
+ """Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
+
+ T113
+ """
+ if 'tempest/lib/' in filename:
+ return
+
+ if 'uuid.uuid4()' not in logical_line:
+ return
+
+ msg = ("T113: Tests should use data_utils.rand_uuid()/rand_uuid_hex() "
+ "instead of uuid.uuid4()/uuid.uuid4().hex")
+ yield (0, msg)
+
+
def factory(register):
register(import_no_clients_in_api_and_scenario_tests)
register(scenario_tests_need_service_tags)
@@ -258,3 +269,4 @@
register(get_resources_on_service_clients)
register(delete_resources_on_service_clients)
register(dont_import_local_tempest_into_lib)
+ register(use_rand_uuid_instead_of_uuid4)
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/api_schema/response/__init__.py b/tempest/lib/api_schema/response/compute/v2_19/__init__.py
similarity index 100%
rename from tempest/api_schema/response/__init__.py
rename to tempest/lib/api_schema/response/compute/v2_19/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_19/servers.py b/tempest/lib/api_schema/response/compute/v2_19/servers.py
new file mode 100644
index 0000000..883839e
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -0,0 +1,49 @@
+# Copyright 2016 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers as serversv21
+from tempest.lib.api_schema.response.compute.v2_9 import servers as serversv29
+
+get_server = copy.deepcopy(serversv29.get_server)
+get_server['response_body']['properties']['server'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+get_server['response_body']['properties']['server'][
+ 'required'].append('description')
+
+list_servers_detail = copy.deepcopy(serversv29.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'required'].append('description')
+
+update_server = copy.deepcopy(serversv21.update_server)
+update_server['response_body']['properties']['server'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+update_server['response_body']['properties']['server'][
+ 'required'].append('description')
+
+rebuild_server = copy.deepcopy(serversv21.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+rebuild_server['response_body']['properties']['server'][
+ 'required'].append('description')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+ serversv21.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'properties'].update({'description': {'type': ['string', 'null']}})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'required'].append('description')
diff --git a/tempest/api_schema/response/compute/v2_2/__init__.py b/tempest/lib/api_schema/response/compute/v2_2/__init__.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2_2/__init__.py
rename to tempest/lib/api_schema/response/compute/v2_2/__init__.py
diff --git a/tempest/api_schema/response/compute/v2_2/keypairs.py b/tempest/lib/api_schema/response/compute/v2_2/keypairs.py
similarity index 95%
rename from tempest/api_schema/response/compute/v2_2/keypairs.py
rename to tempest/lib/api_schema/response/compute/v2_2/keypairs.py
index 5d8d24d..0bb7771 100644
--- a/tempest/api_schema/response/compute/v2_2/keypairs.py
+++ b/tempest/lib/api_schema/response/compute/v2_2/keypairs.py
@@ -14,7 +14,7 @@
import copy
-from tempest.api_schema.response.compute.v2_1 import keypairs
+from tempest.lib.api_schema.response.compute.v2_1 import keypairs
get_keypair = copy.deepcopy(keypairs.get_keypair)
get_keypair['response_body']['properties']['keypair'][
diff --git a/tempest/api_schema/response/__init__.py b/tempest/lib/api_schema/response/compute/v2_9/__init__.py
similarity index 100%
copy from tempest/api_schema/response/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_9/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py
new file mode 100644
index 0000000..e9b7249
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -0,0 +1,29 @@
+# Copyright 2016 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers
+
+get_server = copy.deepcopy(servers.get_server)
+get_server['response_body']['properties']['server'][
+ 'properties'].update({'locked': {'type': 'boolean'}})
+get_server['response_body']['properties']['server'][
+ 'required'].append('locked')
+
+list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'properties'].update({'locked': {'type': 'boolean'}})
+list_servers_detail['response_body']['properties']['servers']['items'][
+ 'required'].append('locked')
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 71c4f4f..a6833be 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -31,6 +31,39 @@
LOG = logging.getLogger(__name__)
+def replace_version(url, new_version):
+ parts = urlparse.urlparse(url)
+ version_path = '/%s' % new_version
+ path, subs = re.subn(r'(^|/)+v\d+(?:\.\d+)?',
+ version_path,
+ parts.path,
+ count=1)
+ if not subs:
+ path = '%s%s' % (parts.path.rstrip('/'), version_path)
+ url = urlparse.urlunparse((parts.scheme,
+ parts.netloc,
+ path,
+ parts.params,
+ parts.query,
+ parts.fragment))
+ return url
+
+
+def apply_url_filters(url, filters):
+ if filters.get('api_version', None) is not None:
+ url = replace_version(url, filters['api_version'])
+ parts = urlparse.urlparse(url)
+ if filters.get('skip_path', None) is not None and parts.path != '':
+ url = urlparse.urlunparse((parts.scheme,
+ parts.netloc,
+ '/',
+ parts.params,
+ parts.query,
+ parts.fragment))
+
+ return url
+
+
@six.add_metaclass(abc.ABCMeta)
class AuthProvider(object):
"""Provide authentication"""
@@ -322,29 +355,7 @@
raise exceptions.EndpointNotFound(
"service: %s, region: %s, endpoint_type: %s" %
(service, region, endpoint_type))
-
- parts = urlparse.urlparse(_base_url)
- if filters.get('api_version', None) is not None:
- version_path = '/%s' % filters['api_version']
- path = re.sub(r'(^|/)+v\d+(?:\.\d+)?',
- version_path,
- parts.path,
- count=1)
- _base_url = urlparse.urlunparse((parts.scheme,
- parts.netloc,
- path or version_path,
- parts.params,
- parts.query,
- parts.fragment))
- if filters.get('skip_path', None) is not None and parts.path != '':
- _base_url = urlparse.urlunparse((parts.scheme,
- parts.netloc,
- '/',
- parts.params,
- parts.query,
- parts.fragment))
-
- return _base_url
+ return apply_url_filters(_base_url, filters)
def is_expired(self, auth_data):
_, access = auth_data
@@ -455,29 +466,7 @@
_base_url = filtered_catalog[0].get('url', None)
if _base_url is None:
raise exceptions.EndpointNotFound(service)
-
- parts = urlparse.urlparse(_base_url)
- if filters.get('api_version', None) is not None:
- version_path = '/%s' % filters['api_version']
- path = re.sub(r'(^|/)+v\d+(?:\.\d+)?',
- version_path,
- parts.path,
- count=1)
- _base_url = urlparse.urlunparse((parts.scheme,
- parts.netloc,
- path or version_path,
- parts.params,
- parts.query,
- parts.fragment))
- if filters.get('skip_path', None) is not None:
- _base_url = urlparse.urlunparse((parts.scheme,
- parts.netloc,
- '/',
- parts.params,
- parts.query,
- parts.fragment))
-
- return _base_url
+ return apply_url_filters(_base_url, filters)
def is_expired(self, auth_data):
_, access = auth_data
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
index b7d6a24..f35b14c 100755
--- a/tempest/lib/cmd/skip_tracker.py
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -48,7 +48,7 @@
def find_skips(start):
- """Find the entire list of skiped tests.
+ """Find the entire list of skipped tests.
Returns a list of tuples (method, bug) that represent
test methods that have been decorated to skip because of
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
index b3793bc..dffc5f9 100644
--- a/tempest/lib/common/http.py
+++ b/tempest/lib/common/http.py
@@ -1,5 +1,4 @@
-# Copyright 2013 OpenStack Foundation
-# Copyright 2013 Citrix Systems, Inc.
+# Copyright 2016 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -14,12 +13,43 @@
# License for the specific language governing permissions and limitations
# under the License.
-import httplib2
+import urllib3
-class ClosingHttp(httplib2.Http):
- def request(self, *args, **kwargs):
+class ClosingHttp(urllib3.poolmanager.PoolManager):
+ def __init__(self, disable_ssl_certificate_validation=False,
+ ca_certs=None):
+ kwargs = {}
+
+ if disable_ssl_certificate_validation:
+ urllib3.disable_warnings()
+ kwargs['cert_reqs'] = 'CERT_NONE'
+
+ if ca_certs:
+ kwargs['cert_reqs'] = 'CERT_REQUIRED'
+ kwargs['ca_certs'] = ca_certs
+
+ super(ClosingHttp, self).__init__(**kwargs)
+
+ def request(self, url, method, *args, **kwargs):
+
+ class Response(dict):
+ def __init__(self, info):
+ for key, value in info.getheaders().items():
+ self[key.lower()] = value
+ self.status = info.status
+ self['status'] = str(self.status)
+ self.reason = info.reason
+ self.version = info.version
+ self['content-location'] = url
+
original_headers = kwargs.get('headers', {})
new_headers = dict(original_headers, connection='close')
new_kwargs = dict(kwargs, headers=new_headers)
- return super(ClosingHttp, self).request(*args, **new_kwargs)
+
+ # Follow up to 5 redirections. Don't raise an exception if
+ # it's exceeded but return the HTTP 3XX response instead.
+ retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5)
+ r = super(ClosingHttp, self).request(method, url, retries=retry,
+ *args, **new_kwargs)
+ return Response(r), r.data
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 7d2eda0..d001d27 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -248,10 +248,10 @@
:param str url: the relative url to send the post request to
:param dict body: the request body
:param dict headers: The headers to use for the request
- :param dict extra_headers: If the headers returned by the get_headers()
- method are to be used but additional headers
- are needed in the request pass them in as a
- dict
+ :param bool extra_headers: Boolean value than indicates if the headers
+ returned by the get_headers() method are to
+ be used but additional headers are needed in
+ the request pass them in as a dict.
:return: a tuple with the first entry containing the response headers
and the second the response body
:rtype: tuple
@@ -263,10 +263,10 @@
:param str url: the relative url to send the post request to
:param dict headers: The headers to use for the request
- :param dict extra_headers: If the headers returned by the get_headers()
- method are to be used but additional headers
- are needed in the request pass them in as a
- dict
+ :param bool extra_headers: Boolean value than indicates if the headers
+ returned by the get_headers() method are to
+ be used but additional headers are needed in
+ the request pass them in as a dict.
:return: a tuple with the first entry containing the response headers
and the second the response body
:rtype: tuple
@@ -279,10 +279,10 @@
:param str url: the relative url to send the post request to
:param dict headers: The headers to use for the request
:param dict body: the request body
- :param dict extra_headers: If the headers returned by the get_headers()
- method are to be used but additional headers
- are needed in the request pass them in as a
- dict
+ :param bool extra_headers: Boolean value than indicates if the headers
+ returned by the get_headers() method are to
+ be used but additional headers are needed in
+ the request pass them in as a dict.
:return: a tuple with the first entry containing the response headers
and the second the response body
:rtype: tuple
@@ -295,10 +295,10 @@
:param str url: the relative url to send the post request to
:param dict body: the request body
:param dict headers: The headers to use for the request
- :param dict extra_headers: If the headers returned by the get_headers()
- method are to be used but additional headers
- are needed in the request pass them in as a
- dict
+ :param bool extra_headers: Boolean value than indicates if the headers
+ returned by the get_headers() method are to
+ be used but additional headers are needed in
+ the request pass them in as a dict.
:return: a tuple with the first entry containing the response headers
and the second the response body
:rtype: tuple
@@ -311,10 +311,10 @@
:param str url: the relative url to send the post request to
:param dict body: the request body
:param dict headers: The headers to use for the request
- :param dict extra_headers: If the headers returned by the get_headers()
- method are to be used but additional headers
- are needed in the request pass them in as a
- dict
+ :param bool extra_headers: Boolean value than indicates if the headers
+ returned by the get_headers() method are to
+ be used but additional headers are needed in
+ the request pass them in as a dict.
:return: a tuple with the first entry containing the response headers
and the second the response body
:rtype: tuple
@@ -326,10 +326,10 @@
:param str url: the relative url to send the post request to
:param dict headers: The headers to use for the request
- :param dict extra_headers: If the headers returned by the get_headers()
- method are to be used but additional headers
- are needed in the request pass them in as a
- dict
+ :param bool extra_headers: Boolean value than indicates if the headers
+ returned by the get_headers() method are to
+ be used but additional headers are needed in
+ the request pass them in as a dict.
:return: a tuple with the first entry containing the response headers
and the second the response body
:rtype: tuple
@@ -341,10 +341,10 @@
:param str url: the relative url to send the post request to
:param dict headers: The headers to use for the request
- :param dict extra_headers: If the headers returned by the get_headers()
- method are to be used but additional headers
- are needed in the request pass them in as a
- dict
+ :param bool extra_headers: Boolean value than indicates if the headers
+ returned by the get_headers() method are to
+ be used but additional headers are needed in
+ the request pass them in as a dict.
:return: a tuple with the first entry containing the response headers
and the second the response body
:rtype: tuple
@@ -576,10 +576,10 @@
:param str method: The HTTP verb to use for the request
:param str url: Relative url to send the request to
- :param dict extra_headers: If specified without the headers kwarg the
- headers sent with the request will be the
- combination from the get_headers() method
- and this kwarg
+ :param bool extra_headers: Boolean value than indicates if the headers
+ returned by the get_headers() method are to
+ be used but additional headers are needed in
+ the request pass them in as a dict.
:param dict headers: Headers to use for the request if none are
specifed the headers returned from the
get_headers() method are used. If the request
@@ -620,7 +620,6 @@
headers = self.get_headers()
elif extra_headers:
try:
- headers = headers.copy()
headers.update(self.get_headers())
except (ValueError, TypeError):
headers = self.get_headers()
diff --git a/tempest/lib/common/ssh.py b/tempest/lib/common/ssh.py
index 511dd08..a831dbd 100644
--- a/tempest/lib/common/ssh.py
+++ b/tempest/lib/common/ssh.py
@@ -117,56 +117,56 @@
"""
ssh = self._get_ssh_connection()
transport = ssh.get_transport()
- channel = transport.open_session()
- channel.fileno() # Register event pipe
- channel.exec_command(cmd)
- channel.shutdown_write()
- exit_status = channel.recv_exit_status()
+ with transport.open_session() as channel:
+ channel.fileno() # Register event pipe
+ channel.exec_command(cmd)
+ channel.shutdown_write()
+ exit_status = channel.recv_exit_status()
- # If the executing host is linux-based, poll the channel
- if self._can_system_poll():
- out_data_chunks = []
- err_data_chunks = []
- poll = select.poll()
- poll.register(channel, select.POLLIN)
- start_time = time.time()
+ # If the executing host is linux-based, poll the channel
+ if self._can_system_poll():
+ out_data_chunks = []
+ err_data_chunks = []
+ poll = select.poll()
+ poll.register(channel, select.POLLIN)
+ start_time = time.time()
- while True:
- ready = poll.poll(self.channel_timeout)
- if not any(ready):
- if not self._is_timed_out(start_time):
+ while True:
+ ready = poll.poll(self.channel_timeout)
+ if not any(ready):
+ if not self._is_timed_out(start_time):
+ continue
+ raise exceptions.TimeoutException(
+ "Command: '{0}' executed on host '{1}'.".format(
+ cmd, self.host))
+ if not ready[0]: # If there is nothing to read.
continue
- raise exceptions.TimeoutException(
- "Command: '{0}' executed on host '{1}'.".format(
- cmd, self.host))
- if not ready[0]: # If there is nothing to read.
- continue
- out_chunk = err_chunk = None
- if channel.recv_ready():
- out_chunk = channel.recv(self.buf_size)
- out_data_chunks += out_chunk,
- if channel.recv_stderr_ready():
- err_chunk = channel.recv_stderr(self.buf_size)
- err_data_chunks += err_chunk,
- if channel.closed and not err_chunk and not out_chunk:
- break
- out_data = b''.join(out_data_chunks)
- err_data = b''.join(err_data_chunks)
- # Just read from the channels
- else:
- out_file = channel.makefile('rb', self.buf_size)
- err_file = channel.makefile_stderr('rb', self.buf_size)
- out_data = out_file.read()
- err_data = err_file.read()
- if encoding:
- out_data = out_data.decode(encoding)
- err_data = err_data.decode(encoding)
+ out_chunk = err_chunk = None
+ if channel.recv_ready():
+ out_chunk = channel.recv(self.buf_size)
+ out_data_chunks += out_chunk,
+ if channel.recv_stderr_ready():
+ err_chunk = channel.recv_stderr(self.buf_size)
+ err_data_chunks += err_chunk,
+ if not err_chunk and not out_chunk:
+ break
+ out_data = b''.join(out_data_chunks)
+ err_data = b''.join(err_data_chunks)
+ # Just read from the channels
+ else:
+ out_file = channel.makefile('rb', self.buf_size)
+ err_file = channel.makefile_stderr('rb', self.buf_size)
+ out_data = out_file.read()
+ err_data = err_file.read()
+ if encoding:
+ out_data = out_data.decode(encoding)
+ err_data = err_data.decode(encoding)
- if 0 != exit_status:
- raise exceptions.SSHExecCommandFailed(
- command=cmd, exit_status=exit_status,
- stderr=err_data, stdout=out_data)
- return out_data
+ if 0 != exit_status:
+ raise exceptions.SSHExecCommandFailed(
+ command=cmd, exit_status=exit_status,
+ stderr=err_data, stdout=out_data)
+ return out_data
def test_connection_auth(self):
"""Raises an exception when we can not connect to server via ssh."""
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 01b6477..9605479 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -39,7 +39,7 @@
def rand_name(name='', prefix=None):
- """Generate a random name that inclues a random number
+ """Generate a random name that includes a random number
:param str name: The name that you want to include
:param str prefix: The prefix that you want to include
@@ -81,7 +81,7 @@
def rand_url():
- """Generate a random url that inclues a random number
+ """Generate a random url that includes a random number
:return: a random url. The format is 'https://url-<random number>.com'.
(e.g. 'https://url-154876201.com')
@@ -121,6 +121,18 @@
return ':'.join(["%02x" % x for x in mac])
+def rand_infiniband_guid_address():
+ """Generate an Infiniband GUID address
+
+ :return: an random Infiniband GUID address
+ :rtype: string
+ """
+ guid = []
+ for i in range(8):
+ guid.append("%02x" % random.randint(0x00, 0xff))
+ return ':'.join(guid)
+
+
def parse_image_id(image_ref):
"""Return the image id from a given image ref
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index 6869f02..e377c84 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -133,7 +133,9 @@
resp, body)
return rest_client.ResponseBody(resp, body)
- def unset_flavor_extra_spec(self, flavor_id, key):
+ def unset_flavor_extra_spec(self, flavor_id, key): # noqa
+ # NOTE: This noqa is for passing T111 check and we cannot rename
+ # to keep backwards compatibility.
"""Unset extra Specs from the mentioned flavor."""
resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
(flavor_id, key))
diff --git a/tempest/lib/services/compute/hosts_client.py b/tempest/lib/services/compute/hosts_client.py
index 0143765..16b5edd 100644
--- a/tempest/lib/services/compute/hosts_client.py
+++ b/tempest/lib/services/compute/hosts_client.py
@@ -61,7 +61,11 @@
self.validate_response(schema.update_host, resp, body)
return rest_client.ResponseBody(resp, body)
- def startup_host(self, hostname):
+ def startup_host(self, hostname): # noqa
+ # NOTE: This noqa is for passing T110 check and we cannot rename
+ # to keep backwards compatibility. Actually, the root problem
+ # of this is a wrong API design. GET operation should not change
+ # resource status, but current API does that.
"""Startup a host."""
resp, body = self.get("os-hosts/%s/startup" % hostname)
@@ -69,7 +73,11 @@
self.validate_response(schema.startup_host, resp, body)
return rest_client.ResponseBody(resp, body)
- def shutdown_host(self, hostname):
+ def shutdown_host(self, hostname): # noqa
+ # NOTE: This noqa is for passing T110 check and we cannot rename
+ # to keep backwards compatibility. Actually, the root problem
+ # of this is a wrong API design. GET operation should not change
+ # resource status, but current API does that.
"""Shutdown a host."""
resp, body = self.get("os-hosts/%s/shutdown" % hostname)
@@ -77,7 +85,11 @@
self.validate_response(schema.shutdown_host, resp, body)
return rest_client.ResponseBody(resp, body)
- def reboot_host(self, hostname):
+ def reboot_host(self, hostname): # noqa
+ # NOTE: This noqa is for passing T110 check and we cannot rename
+ # to keep backwards compatibility. Actually, the root problem
+ # of this is a wrong API design. GET operation should not change
+ # resource status, but current API does that.
"""Reboot a host."""
resp, body = self.get("os-hosts/%s/reboot" % hostname)
diff --git a/tempest/lib/services/compute/hypervisor_client.py b/tempest/lib/services/compute/hypervisor_client.py
index 5dcecc9..23c304e 100644
--- a/tempest/lib/services/compute/hypervisor_client.py
+++ b/tempest/lib/services/compute/hypervisor_client.py
@@ -63,7 +63,9 @@
self.validate_response(schema.get_hypervisor_uptime, resp, body)
return rest_client.ResponseBody(resp, body)
- def search_hypervisor(self, hypervisor_name):
+ def search_hypervisor(self, hypervisor_name): # noqa
+ # NOTE: This noqa is for passing T110 check and we cannot rename
+ # to keep backwards compatibility.
"""Search specified hypervisor."""
resp, body = self.get('os-hypervisors/%s/search' % hypervisor_name)
body = json.loads(body)
diff --git a/tempest/lib/services/compute/images_client.py b/tempest/lib/services/compute/images_client.py
index 4a55ce7..da8a61e 100644
--- a/tempest/lib/services/compute/images_client.py
+++ b/tempest/lib/services/compute/images_client.py
@@ -131,8 +131,10 @@
return rest_client.ResponseBody(resp, body)
def is_resource_deleted(self, id):
+ # Added status check for user with admin role
try:
- self.show_image(id)
+ if self.show_image(id)['image']['status'] == 'DELETED':
+ return True
except lib_exc.NotFound:
return True
return False
diff --git a/tempest/lib/services/compute/keypairs_client.py b/tempest/lib/services/compute/keypairs_client.py
index 0361b9d..7b8e6b2 100644
--- a/tempest/lib/services/compute/keypairs_client.py
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -14,23 +14,36 @@
# under the License.
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
-from tempest.lib.api_schema.response.compute.v2_1 import keypairs as schema
+from tempest.lib.api_schema.response.compute.v2_1 import keypairs as schemav21
+from tempest.lib.api_schema.response.compute.v2_2 import keypairs as schemav22
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
class KeyPairsClient(base_compute_client.BaseComputeClient):
- def list_keypairs(self):
- resp, body = self.get("os-keypairs")
+ schema_versions_info = [{'min': None, 'max': '2.1', 'schema': schemav21},
+ {'min': '2.2', 'max': None, 'schema': schemav22}]
+
+ 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)
return rest_client.ResponseBody(resp, body)
@@ -43,10 +56,15 @@
post_body = json.dumps({'keypair': kwargs})
resp, body = self.post("os-keypairs", body=post_body)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
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/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index a37f167..8e4eca1 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -20,11 +20,17 @@
from six.moves.urllib import parse as urllib
from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
+from tempest.lib.api_schema.response.compute.v2_19 import servers as schemav219
+from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
class ServersClient(base_compute_client.BaseComputeClient):
+ schema_versions_info = [
+ {'min': None, 'max': '2.8', 'schema': schema},
+ {'min': '2.9', 'max': '2.18', 'schema': schemav29},
+ {'min': '2.19', 'max': None, 'schema': schemav219}]
def __init__(self, auth_provider, service, region,
enable_instance_password=True, **kwargs):
@@ -55,7 +61,7 @@
post_body = {'server': body}
if hints:
- post_body = dict(post_body.items() + hints.items())
+ post_body.update(hints)
post_body = json.dumps(post_body)
resp, body = self.post('servers', post_body)
@@ -88,6 +94,7 @@
post_body = json.dumps({'server': kwargs})
resp, body = self.put("servers/%s" % server_id, post_body)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.update_server, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -95,6 +102,7 @@
"""Get server details."""
resp, body = self.get("servers/%s" % server_id)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_server, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -114,6 +122,7 @@
"""
url = 'servers'
+ schema = self.get_schema(self.schema_versions_info)
_schema = schema.list_servers
if detail:
@@ -209,6 +218,7 @@
kwargs['imageRef'] = image_ref
if 'disk_config' in kwargs:
kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config')
+ schema = self.get_schema(self.schema_versions_info)
if self.enable_instance_password:
rebuild_schema = schema.rebuild_server_with_admin_pass
else:
diff --git a/tempest/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/lib/services/network/quotas_client.py b/tempest/lib/services/network/quotas_client.py
index b5cf35b..752b253 100644
--- a/tempest/lib/services/network/quotas_client.py
+++ b/tempest/lib/services/network/quotas_client.py
@@ -22,7 +22,9 @@
uri = '/quotas/%s' % tenant_id
return self.update_resource(uri, put_body)
- def reset_quotas(self, tenant_id):
+ def reset_quotas(self, tenant_id): # noqa
+ # NOTE: This noqa is for passing T111 check and we cannot rename
+ # to keep backwards compatibility.
uri = '/quotas/%s' % tenant_id
return self.delete_resource(uri)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 1217dc9..956fe88 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -29,7 +29,7 @@
from tempest import exceptions
from tempest.lib.common.utils import misc as misc_utils
from tempest.lib import exceptions as lib_exc
-from tempest.services.network import resources as net_resources
+from tempest.scenario import network_resources
import tempest.test
CONF = config.CONF
@@ -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
@@ -649,7 +648,7 @@
"""
if CONF.validation.connect_method == 'floating':
# The tests calling this method don't have a floating IP
- # and can't make use of the validattion resources. So the
+ # and can't make use of the validation resources. So the
# method is creating the floating IP there.
return self.create_floating_ip(server)['ip']
elif CONF.validation.connect_method == 'fixed':
@@ -687,20 +686,18 @@
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(
+ network = network_resources.DeletableNetwork(
networks_client=networks_client, routers_client=routers_client,
**result['network'])
self.assertEqual(network.name, name)
@@ -737,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:
@@ -764,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
@@ -795,8 +790,8 @@
if not is_overlapping_cidr:
raise
self.assertIsNotNone(result, 'Unable to allocate tenant network')
- subnet = net_resources.DeletableSubnet(
- network_client=client, subnets_client=subnets_client,
+ subnet = network_resources.DeletableSubnet(
+ subnets_client=subnets_client,
routers_client=routers_client, **result['subnet'])
self.assertEqual(subnet.cidr, str_cidr)
self.addCleanup(self.delete_wrapper, subnet.delete)
@@ -812,8 +807,8 @@
network_id=network_id,
**kwargs)
self.assertIsNotNone(result, 'Unable to allocate port')
- port = net_resources.DeletablePort(ports_client=client,
- **result['port'])
+ port = network_resources.DeletablePort(ports_client=client,
+ **result['port'])
self.addCleanup(self.delete_wrapper, port.delete)
return port
@@ -843,7 +838,7 @@
net = self._list_networks(name=network_name)
self.assertNotEqual(len(net), 0,
"Unable to get network by name: %s" % network_name)
- return net_resources.AttributeDict(net[0])
+ return network_resources.AttributeDict(net[0])
def create_floating_ip(self, thing, external_network_id=None,
port_id=None, client=None):
@@ -862,7 +857,7 @@
tenant_id=thing['tenant_id'],
fixed_ip_address=ip4
)
- floating_ip = net_resources.DeletableFloatingIp(
+ floating_ip = network_resources.DeletableFloatingIp(
client=client,
**result['floatingip'])
self.addCleanup(self.delete_wrapper, floating_ip.delete)
@@ -883,8 +878,8 @@
def check_floating_ip_status(self, floating_ip, status):
"""Verifies floatingip reaches the given status
- :param floating_ip: net_resources.DeletableFloatingIp floating IP to
- to check status
+ :param floating_ip: network_resources.DeletableFloatingIp floating
+ IP to check status
:param status: target status
:raises: AssertionError if status doesn't match
"""
@@ -908,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
@@ -996,7 +991,7 @@
description=sg_desc)
sg_dict['tenant_id'] = tenant_id
result = client.create_security_group(**sg_dict)
- secgroup = net_resources.DeletableSecurityGroup(
+ secgroup = network_resources.DeletableSecurityGroup(
client=client, routers_client=self.routers_client,
**result['security_group']
)
@@ -1021,8 +1016,8 @@
]
msg = "No default security group for tenant %s." % (tenant_id)
self.assertTrue(len(sgs) > 0, msg)
- return net_resources.DeletableSecurityGroup(client=client,
- **sgs[0])
+ return network_resources.DeletableSecurityGroup(client=client,
+ **sgs[0])
def _create_security_group_rule(self, secgroup=None,
sec_group_rules_client=None,
@@ -1060,7 +1055,7 @@
ruleset.update(kwargs)
sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
- sg_rule = net_resources.DeletableSecurityGroupRule(
+ sg_rule = network_resources.DeletableSecurityGroupRule(
client=sec_group_rules_client,
**sg_rule['security_group_rule']
)
@@ -1140,7 +1135,7 @@
network_id = CONF.network.public_network_id
if router_id:
body = client.show_router(router_id)
- return net_resources.AttributeDict(**body['router'])
+ return network_resources.AttributeDict(**body['router'])
elif network_id:
router = self._create_router(client, tenant_id)
router.set_gateway(network_id)
@@ -1159,8 +1154,8 @@
result = client.create_router(name=name,
admin_state_up=True,
tenant_id=tenant_id)
- router = net_resources.DeletableRouter(routers_client=client,
- **result['router'])
+ router = network_resources.DeletableRouter(routers_client=client,
+ **result['router'])
self.assertEqual(router.name, name)
self.addCleanup(self.delete_wrapper, router.delete)
return router
@@ -1169,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.
@@ -1177,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
@@ -1197,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/services/network/resources.py b/tempest/scenario/network_resources.py
similarity index 97%
rename from tempest/services/network/resources.py
rename to tempest/scenario/network_resources.py
index e78fcfe..667476f 100644
--- a/tempest/services/network/resources.py
+++ b/tempest/scenario/network_resources.py
@@ -41,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)
@@ -77,7 +76,7 @@
"""Waits for a network resource to reach a status
@param fetch: the callable to be used to query the resource status
- @type fecth: callable that takes no parameters and returns the resource
+ @type fetch: callable that takes no parameters and returns the resource
@param status: the status that the resource has to reach
@type status: String
"""
diff --git a/tempest/scenario/test_network_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..b9fdd18 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -24,7 +24,7 @@
from tempest import config
from tempest import exceptions
from tempest.scenario import manager
-from tempest.services.network import resources as net_resources
+from tempest.scenario import network_resources
from tempest import test
CONF = config.CONF
@@ -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'])
@@ -270,8 +269,9 @@
"Old port: %s. Number of new ports: %d" % (
CONF.network.build_timeout, old_port,
len(self.new_port_list)))
- new_port = net_resources.DeletablePort(ports_client=self.ports_client,
- **self.new_port_list[0])
+ new_port = network_resources.DeletablePort(
+ ports_client=self.ports_client,
+ **self.new_port_list[0])
def check_new_nic():
new_nic_list = self._get_server_nics(ssh_client)
@@ -687,12 +687,13 @@
unschedule_router = (self.admin_manager.network_agents_client.
delete_router_from_l3_agent)
- agent_list = set(a["id"] for a in
- self._list_agents(agent_type="L3 agent"))
+ agent_list_alive = set(a["id"] for a in
+ self._list_agents(agent_type="L3 agent") if
+ a["alive"] is True)
self._setup_network_and_servers()
# NOTE(kevinbenton): we have to use the admin credentials to check
- # 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."
@@ -703,7 +704,7 @@
# remove resource from agents
hosting_agents = set(a["id"] for a in
list_hosts(self.router.id)['agents'])
- no_migration = agent_list == hosting_agents
+ no_migration = agent_list_alive == hosting_agents
LOG.info("Router will be assigned to {mig} hosting agent".
format(mig="the same" if no_migration else "a new"))
@@ -723,7 +724,7 @@
# schedule resource to new agent
target_agent = list(hosting_agents if no_migration else
- agent_list - hosting_agents)[0]
+ agent_list_alive - hosting_agents)[0]
schedule_router(target_agent,
router_id=self.router['id'])
self.assertEqual(
diff --git a/tempest/scenario/test_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..adc9008 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -45,6 +45,12 @@
success - ping returns
failure - ping_timeout reached
+ multi-node:
+ Multi-Node mode is enabled when CONF.compute.min_compute_nodes > 1.
+ Tests connectivity between servers on different compute nodes.
+ When enabled, test will boot each new server to different
+ compute nodes.
+
setup:
for primary tenant:
1. create a network&subnet
@@ -130,9 +136,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 +200,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 +307,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 +327,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 +473,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_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 77de47e..6d3ecd4 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -15,6 +15,7 @@
import testtools
+from tempest.common import compute
from tempest.common import waiters
from tempest import config
from tempest.scenario import manager
@@ -35,18 +36,9 @@
"""
def _shelve_then_unshelve_server(self, server):
- self.servers_client.shelve_server(server['id'])
- offload_time = CONF.compute.shelved_offload_time
- if offload_time >= 0:
- waiters.wait_for_server_status(self.servers_client, server['id'],
- 'SHELVED_OFFLOADED',
- extra_timeout=offload_time)
- else:
- waiters.wait_for_server_status(self.servers_client,
- server['id'], 'SHELVED')
- self.servers_client.shelve_offload_server(server['id'])
- waiters.wait_for_server_status(self.servers_client, server['id'],
- 'SHELVED_OFFLOADED')
+ compute.shelve_server(self.servers_client, server['id'],
+ force_shelve_offload=True)
+
self.servers_client.unshelve_server(server['id'])
waiters.wait_for_server_status(self.servers_client, server['id'],
'ACTIVE')
diff --git a/tempest/services/compute/__init__.py b/tempest/services/compute/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/compute/__init__.py
+++ /dev/null
diff --git a/tempest/services/compute/json/__init__.py b/tempest/services/compute/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/compute/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/compute/json/keypairs_client.py b/tempest/services/compute/json/keypairs_client.py
deleted file mode 100644
index 2af55b2..0000000
--- a/tempest/services/compute/json/keypairs_client.py
+++ /dev/null
@@ -1,55 +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.
-
-from oslo_serialization import jsonutils as json
-
-from tempest.api_schema.response.compute.v2_1 import keypairs as schemav21
-from tempest.api_schema.response.compute.v2_2 import keypairs as schemav22
-from tempest.lib.common import rest_client
-from tempest.lib.services.compute import base_compute_client
-
-
-class KeyPairsClient(base_compute_client.BaseComputeClient):
-
- 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")
- 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)
- body = json.loads(body)
- schema = self.get_schema(self.schema_versions_info)
- self.validate_response(schema.get_keypair, resp, body)
- return rest_client.ResponseBody(resp, body)
-
- def create_keypair(self, **kwargs):
- post_body = json.dumps({'keypair': kwargs})
- resp, body = self.post("os-keypairs", body=post_body)
- body = json.loads(body)
- schema = self.get_schema(self.schema_versions_info)
- 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)
- 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/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
index 3f256ec..e29ff89 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/services/image/v1/json/images_client.py
@@ -213,7 +213,8 @@
def is_resource_deleted(self, id):
try:
- self.get_image_meta(id)
+ if self.get_image_meta(id)['status'] == 'deleted':
+ return True
except lib_exc.NotFound:
return True
return False
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
deleted file mode 100644
index 5080657..0000000
--- a/tempest/services/network/json/network_client.py
+++ /dev/null
@@ -1,56 +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 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
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/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 78bda5d..0acd4ad 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -150,9 +150,6 @@
def put_object_with_chunk(self, container, name, contents, chunk_size):
"""Put an object with Transfer-Encoding header"""
- if self.base_url is None:
- self._set_auth()
-
headers = {'Transfer-Encoding': 'chunked'}
if self.token:
headers['X-Auth-Token'] = self.token
@@ -182,8 +179,6 @@
if not data:
headers['content-length'] = '0'
- if self.base_url is None:
- self._set_auth()
headers['X-Auth-Token'] = self.token
conn = put_object_connection(self.base_url, str(container),
diff --git a/tempest/test.py b/tempest/test.py
index 6ba4962..b32beaa 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -19,7 +19,6 @@
import re
import sys
import time
-import uuid
import fixtures
from oslo_log import log as logging
@@ -38,6 +37,7 @@
import tempest.common.validation_resources as vresources
from tempest import config
from tempest import exceptions
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
LOG = logging.getLogger(__name__)
@@ -714,10 +714,10 @@
resource = resource['name']
LOG.debug("Add resource to test %s" % resource)
scn_name = "inv_res_%s" % (resource)
- scenario_list.append((scn_name, {"resource": (resource,
- str(uuid.uuid4())),
- "expected_result": expected_result
- }))
+ scenario_list.append((scn_name, {
+ "resource": (resource, data_utils.rand_uuid()),
+ "expected_result": expected_result
+ }))
if schema is not None:
for scenario in generator.generate_scenarios(schema):
scenario_list.append((scenario['_negtest_name'],
diff --git a/tempest/tests/base.py b/tempest/tests/base.py
index fe9268e..ca81d4d 100644
--- a/tempest/tests/base.py
+++ b/tempest/tests/base.py
@@ -14,17 +14,10 @@
import mock
from oslotest import base
-from oslotest import moxstubout
class TestCase(base.BaseTestCase):
- def setUp(self):
- super(TestCase, self).setUp()
- mox_fixture = self.useFixture(moxstubout.MoxStubout())
- self.mox = mox_fixture.mox
- self.stubs = mox_fixture.stubs
-
def patch(self, target, **kwargs):
"""Returns a started `mock.patch` object for the supplied target.
@@ -42,3 +35,15 @@
m = p.start()
self.addCleanup(p.stop)
return m
+
+ def patchobject(self, target, attribute, new=mock.DEFAULT):
+ """Convenient wrapper around `mock.patch.object`
+
+ Returns a started mock that will be automatically stopped after the
+ test ran.
+ """
+
+ p = mock.patch.object(target, attribute, new)
+ m = p.start()
+ self.addCleanup(p.stop)
+ return m
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index dc0ba6f..70cbf87 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -12,28 +12,56 @@
# License for the specific language governing permissions and limitations
# under the License.
-
import mock
from oslo_serialization import jsonutils as json
from oslotest import mockpatch
from tempest.cmd import verify_tempest_config
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.tests import base
from tempest.tests import fake_config
class TestGetAPIVersions(base.TestCase):
+ def test_remove_version_project(self):
+ f = verify_tempest_config._remove_version_project
+ self.assertEqual('/', f('/v2.1/%s/' % data_utils.rand_uuid_hex()))
+ self.assertEqual('', f('/v2.1/tenant_id'))
+ self.assertEqual('', f('/v3'))
+ self.assertEqual('/', f('/v3/'))
+ self.assertEqual('/something/', f('/something/v2.1/tenant_id/'))
+ self.assertEqual('/something', f('/something/v2.1/tenant_id'))
+ self.assertEqual('/something', f('/something/v3'))
+ self.assertEqual('/something/', f('/something/v3/'))
+ self.assertEqual('/', f('/')) # http://localhost/
+ self.assertEqual('', f('')) # http://localhost
+
def test_url_grab_versioned_nova_nossl(self):
base_url = 'http://127.0.0.1:8774/v2/'
endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
- self.assertEqual('http://127.0.0.1:8774', endpoint)
+ self.assertEqual('http://127.0.0.1:8774/', endpoint)
def test_url_grab_versioned_nova_ssl(self):
base_url = 'https://127.0.0.1:8774/v3/'
endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
- self.assertEqual('https://127.0.0.1:8774', endpoint)
+ self.assertEqual('https://127.0.0.1:8774/', endpoint)
+
+ def test_get_unversioned_endpoint_base(self):
+ base_url = 'https://127.0.0.1:5000/'
+ endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
+ self.assertEqual('https://127.0.0.1:5000/', endpoint)
+
+ def test_get_unversioned_endpoint_subpath(self):
+ base_url = 'https://127.0.0.1/identity/v3'
+ endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
+ self.assertEqual('https://127.0.0.1/identity', endpoint)
+
+ def test_get_unversioned_endpoint_subpath_trailing_solidus(self):
+ base_url = 'https://127.0.0.1/identity/v3/'
+ endpoint = verify_tempest_config._get_unversioned_endpoint(base_url)
+ self.assertEqual('https://127.0.0.1/identity/', endpoint)
class TestDiscovery(base.TestCase):
@@ -41,7 +69,8 @@
def setUp(self):
super(TestDiscovery, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
def test_get_keystone_api_versions(self):
self.useFixture(mockpatch.PatchObject(
@@ -49,8 +78,9 @@
return_value='http://fake_endpoint:5000'))
fake_resp = {'versions': {'values': [{'id': 'v2.0'}, {'id': 'v3.0'}]}}
fake_resp = json.dumps(fake_resp)
- self.useFixture(mockpatch.Patch('httplib2.Http.request',
- return_value=(None, fake_resp)))
+ self.useFixture(mockpatch.Patch(
+ 'tempest.lib.common.http.ClosingHttp.request',
+ return_value=(None, fake_resp)))
fake_os = mock.MagicMock()
versions = verify_tempest_config._get_api_versions(fake_os, 'keystone')
self.assertIn('v2.0', versions)
@@ -62,8 +92,9 @@
return_value='http://fake_endpoint:5000'))
fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v2.0'}]}
fake_resp = json.dumps(fake_resp)
- self.useFixture(mockpatch.Patch('httplib2.Http.request',
- return_value=(None, fake_resp)))
+ self.useFixture(mockpatch.Patch(
+ 'tempest.lib.common.http.ClosingHttp.request',
+ return_value=(None, fake_resp)))
fake_os = mock.MagicMock()
versions = verify_tempest_config._get_api_versions(fake_os, 'cinder')
self.assertIn('v1.0', versions)
@@ -75,8 +106,9 @@
return_value='http://fake_endpoint:5000'))
fake_resp = {'versions': [{'id': 'v2.0'}, {'id': 'v3.0'}]}
fake_resp = json.dumps(fake_resp)
- self.useFixture(mockpatch.Patch('httplib2.Http.request',
- return_value=(None, fake_resp)))
+ self.useFixture(mockpatch.Patch(
+ 'tempest.lib.common.http.ClosingHttp.request',
+ return_value=(None, fake_resp)))
fake_os = mock.MagicMock()
versions = verify_tempest_config._get_api_versions(fake_os, 'nova')
self.assertIn('v2.0', versions)
@@ -95,8 +127,9 @@
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)))
+ self.useFixture(mockpatch.Patch(
+ 'tempest.lib.common.http.ClosingHttp.request',
+ return_value=(None, sample_body)))
# service value doesn't matter, just needs to match what
# _get_api_versions puts in its client_dict.
@@ -122,14 +155,14 @@
verify_tempest_config.verify_api_versions(fake_os, 'foo', True)
self.assertFalse(verify_mock.called)
- def test_verify_keystone_api_versions_no_v3(self):
+ @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+ def test_verify_keystone_api_versions_no_v3(self, mock_request):
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, '_get_unversioned_endpoint',
return_value='http://fake_endpoint:5000'))
fake_resp = {'versions': {'values': [{'id': 'v2.0'}]}}
fake_resp = json.dumps(fake_resp)
- self.useFixture(mockpatch.Patch('httplib2.Http.request',
- return_value=(None, fake_resp)))
+ mock_request.return_value = (None, fake_resp)
fake_os = mock.MagicMock()
with mock.patch.object(verify_tempest_config,
'print_and_or_update') as print_mock:
@@ -138,14 +171,14 @@
'identity-feature-enabled',
False, True)
- def test_verify_keystone_api_versions_no_v2(self):
+ @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+ def test_verify_keystone_api_versions_no_v2(self, mock_request):
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, '_get_unversioned_endpoint',
return_value='http://fake_endpoint:5000'))
fake_resp = {'versions': {'values': [{'id': 'v3.0'}]}}
fake_resp = json.dumps(fake_resp)
- self.useFixture(mockpatch.Patch('httplib2.Http.request',
- return_value=(None, fake_resp)))
+ mock_request.return_value = (None, fake_resp)
fake_os = mock.MagicMock()
with mock.patch.object(verify_tempest_config,
'print_and_or_update') as print_mock:
@@ -154,14 +187,14 @@
'identity-feature-enabled',
False, True)
- def test_verify_cinder_api_versions_no_v2(self):
+ @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+ def test_verify_cinder_api_versions_no_v2(self, mock_request):
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, '_get_unversioned_endpoint',
return_value='http://fake_endpoint:5000'))
fake_resp = {'versions': [{'id': 'v1.0'}]}
fake_resp = json.dumps(fake_resp)
- self.useFixture(mockpatch.Patch('httplib2.Http.request',
- return_value=(None, fake_resp)))
+ mock_request.return_value = (None, fake_resp)
fake_os = mock.MagicMock()
with mock.patch.object(verify_tempest_config,
'print_and_or_update') as print_mock:
@@ -169,14 +202,14 @@
print_mock.assert_called_once_with('api_v2', 'volume-feature-enabled',
False, True)
- def test_verify_cinder_api_versions_no_v1(self):
+ @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+ def test_verify_cinder_api_versions_no_v1(self, mock_request):
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, '_get_unversioned_endpoint',
return_value='http://fake_endpoint:5000'))
fake_resp = {'versions': [{'id': 'v2.0'}]}
fake_resp = json.dumps(fake_resp)
- self.useFixture(mockpatch.Patch('httplib2.Http.request',
- return_value=(None, fake_resp)))
+ mock_request.return_value = (None, fake_resp)
fake_os = mock.MagicMock()
with mock.patch.object(verify_tempest_config,
'print_and_or_update') as print_mock:
diff --git a/tempest/tests/common/test_admin_available.py b/tempest/tests/common/test_admin_available.py
index 75401db..01a9cd0 100644
--- a/tempest/tests/common/test_admin_available.py
+++ b/tempest/tests/common/test_admin_available.py
@@ -28,7 +28,8 @@
def setUp(self):
super(TestAdminAvailable, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
def run_test(self, dynamic_creds, use_accounts_file, admin_creds):
@@ -36,19 +37,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 +64,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..d4cfab6 100644
--- a/tempest/tests/common/test_alt_available.py
+++ b/tempest/tests/common/test_alt_available.py
@@ -28,7 +28,8 @@
def setUp(self):
super(TestAltAvailable, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
def run_test(self, dynamic_creds, use_accounts_file, creds):
@@ -36,7 +37,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 +53,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_configured_creds.py b/tempest/tests/common/test_configured_creds.py
index be24595..3c242b3 100644
--- a/tempest/tests/common/test_configured_creds.py
+++ b/tempest/tests/common/test_configured_creds.py
@@ -23,7 +23,7 @@
from tempest.lib.services.identity.v3 import token_client as v3_client
from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests import fake_identity
+from tempest.tests.lib import fake_identity
class ConfiguredV2CredentialsTests(base.TestCase):
@@ -41,9 +41,10 @@
def setUp(self):
super(ConfiguredV2CredentialsTests, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
- self.stubs.Set(self.tokenclient_class, 'raw_request',
- self.identity_response)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
+ self.patchobject(self.tokenclient_class, 'raw_request',
+ self.identity_response)
def _get_credentials(self, attributes=None):
if attributes is None:
diff --git a/tempest/tests/common/test_credentials.py b/tempest/tests/common/test_credentials.py
index 136ac02..00f2d39 100644
--- a/tempest/tests/common/test_credentials.py
+++ b/tempest/tests/common/test_credentials.py
@@ -26,7 +26,8 @@
def setUp(self):
super(TestLegacyCredentialsProvider, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
def test_get_creds_roles_legacy_invalid(self):
test_accounts_class = credentials.LegacyCredentialProvider(
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index be4a6ee..8d4f33b 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -30,12 +30,11 @@
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
-from tempest.tests import fake_http
-from tempest.tests import fake_identity
+from tempest.tests.lib import fake_http
+from tempest.tests.lib import fake_identity
class TestDynamicCredentialProvider(base.TestCase):
@@ -47,10 +46,10 @@
def setUp(self):
super(TestDynamicCredentialProvider, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
- self.fake_http = fake_http.fake_httplib2(return_type=200)
- self.stubs.Set(json_token_client.TokenClient, 'raw_request',
- fake_identity._fake_v2_response)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
+ self.patchobject(json_token_client.TokenClient, 'raw_request',
+ fake_identity._fake_v2_response)
cfg.CONF.set_default('operator_role', 'FakeRole',
group='object-storage')
self._mock_list_ec2_credentials('fake_user_id', 'fake_tenant_id')
@@ -59,10 +58,8 @@
def test_tempest_client(self):
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))
+ self.assertIsInstance(creds.identity_admin_client,
+ json_iden_client.IdentityClient)
def _get_fake_admin_creds(self):
return credentials.get_credentials(
@@ -405,7 +402,7 @@
side_effect=side_effect)
secgroup_list_mock.start()
- return_values = (fake_http.fake_httplib({}, status=204), {})
+ return_values = fake_http.fake_http_response({}, status=204), ''
remove_secgroup_mock = self.patch(
'tempest.lib.services.network.security_groups_client.'
'SecurityGroupsClient.delete', return_value=return_values)
diff --git a/tempest/tests/common/test_preprov_creds.py b/tempest/tests/common/test_preprov_creds.py
index 7188e5f..b595c88 100644
--- a/tempest/tests/common/test_preprov_creds.py
+++ b/tempest/tests/common/test_preprov_creds.py
@@ -30,8 +30,7 @@
from tempest.lib.services.identity.v2 import token_client
from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests import fake_http
-from tempest.tests import fake_identity
+from tempest.tests.lib import fake_identity
class TestPreProvisionedCredentials(base.TestCase):
@@ -47,10 +46,10 @@
def setUp(self):
super(TestPreProvisionedCredentials, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
- self.fake_http = fake_http.fake_httplib2(return_type=200)
- self.stubs.Set(token_client.TokenClient, 'raw_request',
- fake_identity._fake_v2_response)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
+ self.patchobject(token_client.TokenClient, 'raw_request',
+ fake_identity._fake_v2_response)
self.useFixture(lockutils_fixtures.ExternalLockFixture())
self.test_accounts = [
{'username': 'test_user1', 'tenant_name': 'test_tenant1',
@@ -98,8 +97,8 @@
return hash_list
def test_get_hash(self):
- self.stubs.Set(token_client.TokenClient, 'raw_request',
- fake_identity._fake_v2_response)
+ self.patchobject(token_client.TokenClient, 'raw_request',
+ fake_identity._fake_v2_response)
test_account_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
hash_list = self._get_hash_list(self.test_accounts)
@@ -190,7 +189,7 @@
return False
return True
- self.stubs.Set(os.path, 'isfile', _fake_is_file)
+ self.patchobject(os.path, 'isfile', _fake_is_file)
with mock.patch('six.moves.builtins.open', mock.mock_open(),
create=True) as open_mock:
test_account_class._get_free_hash(hash_list)
@@ -325,7 +324,7 @@
'id': 'fake-id',
'label': 'network-2'}]}):
creds = test_accounts_class.get_creds_by_roles(['role-7'])
- self.assertTrue(isinstance(creds, cred_provider.TestResources))
+ self.assertIsInstance(creds, cred_provider.TestResources)
network = creds.network
self.assertIsNotNone(network)
self.assertIn('name', network)
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 9c2b99e..7d625cf 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import fixtures
import time
from oslo_config import cfg
@@ -19,15 +20,45 @@
from tempest.common.utils.linux import remote_client
from tempest import config
+from tempest.lib import exceptions as lib_exc
from tempest.tests import base
from tempest.tests import fake_config
+SERVER = {
+ 'id': 'server_uuid',
+ 'name': 'fake_server',
+ 'status': 'ACTIVE'
+}
+
+BROKEN_SERVER = {
+ 'id': 'broken_server_uuid',
+ 'name': 'broken_server',
+ 'status': 'ERROR'
+}
+
+
+class FakeServersClient(object):
+
+ CONSOLE_OUTPUT = "Console output for %s"
+
+ def get_console_output(self, server_id):
+ status = 'ERROR'
+ for s in SERVER, BROKEN_SERVER:
+ if s['id'] == server_id:
+ status = s['status']
+ if status == 'ERROR':
+ raise lib_exc.BadRequest('Server in ERROR state')
+ else:
+ return dict(output=self.CONSOLE_OUTPUT % server_id)
+
+
class TestRemoteClient(base.TestCase):
def setUp(self):
super(TestRemoteClient, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
cfg.CONF.set_default('ip_version_for_ssh', 4, group='validation')
cfg.CONF.set_default('network_for_ssh', 'public', group='validation')
cfg.CONF.set_default('connect_timeout', 1, group='validation')
@@ -154,3 +185,78 @@
self.conn.set_nic_state(nic, "down")
self._assert_exec_called_with(
'sudo ip link set %s down' % nic)
+
+
+class TestRemoteClientWithServer(base.TestCase):
+
+ server = SERVER
+
+ def setUp(self):
+ super(TestRemoteClientWithServer, self).setUp()
+ self.useFixture(fake_config.ConfigFixture())
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
+ cfg.CONF.set_default('ip_version_for_ssh', 4, group='validation')
+ cfg.CONF.set_default('network_for_ssh', 'public',
+ group='validation')
+ cfg.CONF.set_default('connect_timeout', 1, group='validation')
+ cfg.CONF.set_default('console_output', True,
+ group='compute-feature-enabled')
+
+ self.conn = remote_client.RemoteClient(
+ '127.0.0.1', 'user', 'pass',
+ server=self.server, servers_client=FakeServersClient())
+ self.useFixture(fixtures.MockPatch(
+ 'tempest.lib.common.ssh.Client._get_ssh_connection',
+ side_effect=lib_exc.SSHTimeout(host='127.0.0.1',
+ user='user',
+ password='pass')))
+ self.log = self.useFixture(fixtures.FakeLogger(
+ name='tempest.common.utils.linux.remote_client',
+ level='DEBUG'))
+
+ def test_validate_debug_ssh_console(self):
+ self.assertRaises(lib_exc.SSHTimeout,
+ self.conn.validate_authentication)
+ msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+ 'TestRemoteClientWithServer:test_validate_debug_ssh_console',
+ self.server)
+ self.assertIn(msg, self.log.output)
+ self.assertIn('Console output for', self.log.output)
+
+ def test_exec_command_debug_ssh_console(self):
+ self.assertRaises(lib_exc.SSHTimeout,
+ self.conn.exec_command, 'fake command')
+ self.assertIn('fake command', self.log.output)
+ msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+ 'TestRemoteClientWithServer:test_exec_command_debug_ssh_console',
+ self.server)
+ self.assertIn(msg, self.log.output)
+ self.assertIn('Console output for', self.log.output)
+
+
+class TestRemoteClientWithBrokenServer(TestRemoteClientWithServer):
+
+ server = BROKEN_SERVER
+
+ def test_validate_debug_ssh_console(self):
+ self.assertRaises(lib_exc.SSHTimeout,
+ self.conn.validate_authentication)
+ msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+ 'TestRemoteClientWithBrokenServer:test_validate_debug_ssh_console',
+ self.server)
+ self.assertIn(msg, self.log.output)
+ msg = 'Could not get console_log for server %s' % self.server['id']
+ self.assertIn(msg, self.log.output)
+
+ def test_exec_command_debug_ssh_console(self):
+ self.assertRaises(lib_exc.SSHTimeout,
+ self.conn.exec_command, 'fake command')
+ self.assertIn('fake command', self.log.output)
+ caller = ":".join(['TestRemoteClientWithBrokenServer',
+ 'test_exec_command_debug_ssh_console'])
+ msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
+ caller, self.server)
+ self.assertIn(msg, self.log.output)
+ msg = 'Could not get console_log for server %s' % self.server['id']
+ self.assertIn(msg, self.log.output)
diff --git a/tempest/tests/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/fake_http.py b/tempest/tests/fake_http.py
deleted file mode 100644
index d714055..0000000
--- a/tempest/tests/fake_http.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright 2013 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 copy
-
-import httplib2
-
-
-class fake_httplib2(object):
-
- def __init__(self, return_type=None, *args, **kwargs):
- self.return_type = return_type
-
- def request(self, uri, method="GET", body=None, headers=None,
- redirections=5, connection_type=None):
- if not self.return_type:
- fake_headers = httplib2.Response(headers)
- return_obj = {
- 'uri': uri,
- 'method': method,
- 'body': body,
- 'headers': headers
- }
- return (fake_headers, return_obj)
- elif isinstance(self.return_type, int):
- body = "fake_body"
- header_info = {
- 'content-type': 'text/plain',
- 'status': str(self.return_type),
- 'content-length': len(body)
- }
- resp_header = httplib2.Response(header_info)
- return (resp_header, body)
- else:
- msg = "unsupported return type %s" % self.return_type
- raise TypeError(msg)
-
-
-class fake_httplib(object):
- def __init__(self, headers, body=None,
- version=1.0, status=200, reason="Ok"):
- """Initialization of fake httplib
-
- :param headers: dict representing HTTP response headers
- :param body: file-like object
- :param version: HTTP Version
- :param status: Response status code
- :param reason: Status code related message.
- """
- self.body = body
- self.status = status
- self.reason = reason
- self.version = version
- self.headers = headers
-
- def getheaders(self):
- return copy.deepcopy(self.headers).items()
-
- def getheader(self, key, default):
- return self.headers.get(key, default)
-
- def read(self, amt):
- return self.body.read(amt)
diff --git a/tempest/tests/fake_identity.py b/tempest/tests/fake_identity.py
deleted file mode 100644
index d0de927..0000000
--- a/tempest/tests/fake_identity.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# Copyright 2014 IBM Corp.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import httplib2
-from oslo_serialization import jsonutils as json
-
-FAKE_AUTH_URL = 'http://fake_uri.com/auth'
-
-TOKEN = "fake_token"
-ALT_TOKEN = "alt_fake_token"
-
-# Fake Identity v2 constants
-COMPUTE_ENDPOINTS_V2 = {
- "endpoints": [
- {
- "adminURL": "http://fake_url/v2/first_endpoint/admin",
- "region": "NoMatchRegion",
- "internalURL": "http://fake_url/v2/first_endpoint/internal",
- "publicURL": "http://fake_url/v2/first_endpoint/public"
- },
- {
- "adminURL": "http://fake_url/v2/second_endpoint/admin",
- "region": "FakeRegion",
- "internalURL": "http://fake_url/v2/second_endpoint/internal",
- "publicURL": "http://fake_url/v2/second_endpoint/public"
- },
- ],
- "type": "compute",
- "name": "nova"
-}
-
-CATALOG_V2 = [COMPUTE_ENDPOINTS_V2, ]
-
-ALT_IDENTITY_V2_RESPONSE = {
- "access": {
- "token": {
- "expires": "2020-01-01T00:00:10Z",
- "id": ALT_TOKEN,
- "tenant": {
- "id": "fake_tenant_id"
- },
- },
- "user": {
- "id": "fake_user_id",
- },
- "serviceCatalog": CATALOG_V2,
- },
-}
-
-IDENTITY_V2_RESPONSE = {
- "access": {
- "token": {
- "expires": "2020-01-01T00:00:10Z",
- "id": TOKEN,
- "tenant": {
- "id": "fake_tenant_id"
- },
- },
- "user": {
- "id": "fake_user_id",
- },
- "serviceCatalog": CATALOG_V2,
- },
-}
-
-# Fake Identity V3 constants
-COMPUTE_ENDPOINTS_V3 = {
- "endpoints": [
- {
- "id": "first_compute_fake_service",
- "interface": "public",
- "region": "NoMatchRegion",
- "url": "http://fake_url/v3/first_endpoint/api"
- },
- {
- "id": "second_fake_service",
- "interface": "public",
- "region": "FakeRegion",
- "url": "http://fake_url/v3/second_endpoint/api"
- },
- {
- "id": "third_fake_service",
- "interface": "admin",
- "region": "MiddleEarthRegion",
- "url": "http://fake_url/v3/third_endpoint/api"
- }
-
- ],
- "type": "compute",
- "id": "fake_compute_endpoint"
-}
-
-CATALOG_V3 = [COMPUTE_ENDPOINTS_V3, ]
-
-IDENTITY_V3_RESPONSE = {
- "token": {
- "methods": [
- "token",
- "password"
- ],
- "expires_at": "2020-01-01T00:00:10.000123Z",
- "project": {
- "domain": {
- "id": "fake_domain_id",
- "name": "fake"
- },
- "id": "project_id",
- "name": "project_name"
- },
- "user": {
- "domain": {
- "id": "fake_domain_id",
- "name": "domain_name"
- },
- "id": "fake_user_id",
- "name": "username"
- },
- "issued_at": "2013-05-29T16:55:21.468960Z",
- "catalog": CATALOG_V3
- }
-}
-
-ALT_IDENTITY_V3 = IDENTITY_V3_RESPONSE
-
-
-def _fake_v3_response(self, uri, method="GET", body=None, headers=None,
- redirections=5, connection_type=None):
- fake_headers = {
- "status": "201",
- "x-subject-token": TOKEN
- }
- return (httplib2.Response(fake_headers),
- json.dumps(IDENTITY_V3_RESPONSE))
-
-
-def _fake_v2_response(self, uri, method="GET", body=None, headers=None,
- redirections=5, connection_type=None):
- return (httplib2.Response({"status": "200"}),
- json.dumps(IDENTITY_V2_RESPONSE))
-
-
-def _fake_auth_failure_response():
- # the response body isn't really used in this case, but lets send it anyway
- # to have a safe check in some future change on the rest client.
- body = {
- "unauthorized": {
- "message": "Unauthorized",
- "code": "401"
- }
- }
- return httplib2.Response({"status": "401"}), json.dumps(body)
diff --git a/tempest/tests/lib/base.py b/tempest/tests/lib/base.py
deleted file mode 100644
index fe9268e..0000000
--- a/tempest/tests/lib/base.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2013 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 oslotest import base
-from oslotest import moxstubout
-
-
-class TestCase(base.BaseTestCase):
-
- def setUp(self):
- super(TestCase, self).setUp()
- mox_fixture = self.useFixture(moxstubout.MoxStubout())
- self.mox = mox_fixture.mox
- self.stubs = mox_fixture.stubs
-
- def patch(self, target, **kwargs):
- """Returns a started `mock.patch` object for the supplied target.
-
- The caller may then call the returned patcher to create a mock object.
-
- The caller does not need to call stop() on the returned
- patcher object, as this method automatically adds a cleanup
- to the test class to stop the patcher.
-
- :param target: String module.class or module.object expression to patch
- :param **kwargs: Passed as-is to `mock.patch`. See mock documentation
- for details.
- """
- p = mock.patch(target, **kwargs)
- m = p.start()
- self.addCleanup(p.stop)
- return m
diff --git a/tempest/tests/lib/cli/test_command_failed.py b/tempest/tests/lib/cli/test_command_failed.py
index 8ce34c2..388028a 100644
--- a/tempest/tests/lib/cli/test_command_failed.py
+++ b/tempest/tests/lib/cli/test_command_failed.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class TestOutputParser(base.TestCase):
diff --git a/tempest/tests/lib/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
index b5f7145..b846c46 100644
--- a/tempest/tests/lib/cli/test_execute.py
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -14,7 +14,7 @@
from tempest.lib.cli import base as cli_base
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class TestExecute(base.TestCase):
diff --git a/tempest/tests/lib/cli/test_output_parser.py b/tempest/tests/lib/cli/test_output_parser.py
index a2c1b2d..d88dfc3 100644
--- a/tempest/tests/lib/cli/test_output_parser.py
+++ b/tempest/tests/lib/cli/test_output_parser.py
@@ -16,7 +16,7 @@
from tempest.lib.cli import output_parser
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class TestOutputParser(base.TestCase):
diff --git a/tempest/tests/lib/common/test_api_version_request.py b/tempest/tests/lib/common/test_api_version_request.py
index bdaa936..58e7040 100644
--- a/tempest/tests/lib/common/test_api_version_request.py
+++ b/tempest/tests/lib/common/test_api_version_request.py
@@ -14,7 +14,7 @@
from tempest.lib.common import api_version_request
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class APIVersionRequestTests(base.TestCase):
diff --git a/tempest/tests/lib/common/test_api_version_utils.py b/tempest/tests/lib/common/test_api_version_utils.py
index 591b87e..6206379 100644
--- a/tempest/tests/lib/common/test_api_version_utils.py
+++ b/tempest/tests/lib/common/test_api_version_utils.py
@@ -16,7 +16,7 @@
from tempest.lib.common import api_version_utils
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
class TestVersionSkipLogic(base.TestCase):
diff --git a/tempest/tests/lib/common/utils/test_data_utils.py b/tempest/tests/lib/common/utils/test_data_utils.py
index 493df89..f9e1f44 100644
--- a/tempest/tests/lib/common/utils/test_data_utils.py
+++ b/tempest/tests/lib/common/utils/test_data_utils.py
@@ -16,7 +16,7 @@
import netaddr
from tempest.lib.common.utils import data_utils
-from tempest.tests.lib import base
+from tempest.tests import base
class TestDataUtils(base.TestCase):
@@ -93,6 +93,15 @@
actual2 = data_utils.rand_int_id()
self.assertNotEqual(actual, actual2)
+ def test_rand_infiniband_guid_address(self):
+ actual = data_utils.rand_infiniband_guid_address()
+ self.assertIsInstance(actual, str)
+ self.assertRegex(actual, "^([0-9a-f][0-9a-f]:){7}"
+ "[0-9a-f][0-9a-f]$")
+
+ actual2 = data_utils.rand_infiniband_guid_address()
+ self.assertNotEqual(actual, actual2)
+
def test_rand_mac_address(self):
actual = data_utils.rand_mac_address()
self.assertIsInstance(actual, str)
diff --git a/tempest/tests/lib/common/utils/test_misc.py b/tempest/tests/lib/common/utils/test_misc.py
index e23d7fb..9597f5b 100644
--- a/tempest/tests/lib/common/utils/test_misc.py
+++ b/tempest/tests/lib/common/utils/test_misc.py
@@ -15,7 +15,7 @@
from tempest.lib.common.utils import misc
-from tempest.tests.lib import base
+from tempest.tests import base
@misc.singleton
diff --git a/tempest/tests/lib/fake_http.py b/tempest/tests/lib/fake_http.py
index eda202d..397c856 100644
--- a/tempest/tests/lib/fake_http.py
+++ b/tempest/tests/lib/fake_http.py
@@ -14,8 +14,6 @@
import copy
-import httplib2
-
class fake_httplib2(object):
@@ -25,7 +23,7 @@
def request(self, uri, method="GET", body=None, headers=None,
redirections=5, connection_type=None):
if not self.return_type:
- fake_headers = httplib2.Response(headers)
+ fake_headers = fake_http_response(headers)
return_obj = {
'uri': uri,
'method': method,
@@ -37,20 +35,20 @@
body = body or "fake_body"
header_info = {
'content-type': 'text/plain',
- 'status': str(self.return_type),
'content-length': len(body)
}
- resp_header = httplib2.Response(header_info)
+ resp_header = fake_http_response(header_info,
+ status=self.return_type)
return (resp_header, body)
else:
msg = "unsupported return type %s" % self.return_type
raise TypeError(msg)
-class fake_httplib(object):
+class fake_http_response(dict):
def __init__(self, headers, body=None,
version=1.0, status=200, reason="Ok"):
- """Fake httplib implementation
+ """Initialization of fake HTTP Response
:param headers: dict representing HTTP response headers
:param body: file-like object
@@ -60,10 +58,15 @@
"""
self.body = body
self.status = status
+ self['status'] = str(self.status)
self.reason = reason
self.version = version
self.headers = headers
+ if headers:
+ for key, value in headers.items():
+ self[key.lower()] = value
+
def getheaders(self):
return copy.deepcopy(self.headers).items()
diff --git a/tempest/tests/lib/fake_identity.py b/tempest/tests/lib/fake_identity.py
index bac2676..5732065 100644
--- a/tempest/tests/lib/fake_identity.py
+++ b/tempest/tests/lib/fake_identity.py
@@ -13,9 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import json
+from oslo_serialization import jsonutils as json
-import httplib2
+from tempest.tests.lib import fake_http
FAKE_AUTH_URL = 'http://fake_uri.com/auth'
@@ -139,16 +139,15 @@
def _fake_v3_response(self, uri, method="GET", body=None, headers=None,
redirections=5, connection_type=None):
fake_headers = {
- "status": "201",
"x-subject-token": TOKEN
}
- return (httplib2.Response(fake_headers),
+ return (fake_http.fake_http_response(fake_headers, status=201),
json.dumps(IDENTITY_V3_RESPONSE))
def _fake_v2_response(self, uri, method="GET", body=None, headers=None,
redirections=5, connection_type=None):
- return (httplib2.Response({"status": "200"}),
+ return (fake_http.fake_http_response({}, status=200),
json.dumps(IDENTITY_V2_RESPONSE))
@@ -161,4 +160,4 @@
"code": "401"
}
}
- return httplib2.Response({"status": "401"}), json.dumps(body)
+ return fake_http.fake_http_response({}, status=401), json.dumps(body)
diff --git a/tempest/tests/lib/services/compute/base.py b/tempest/tests/lib/services/compute/base.py
index 5602044..e77b436 100644
--- a/tempest/tests/lib/services/compute/base.py
+++ b/tempest/tests/lib/services/compute/base.py
@@ -12,11 +12,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import httplib2
from oslo_serialization import jsonutils as json
from oslotest import mockpatch
-from tempest.tests.lib import base
+from tempest.tests import base
+from tempest.tests.lib import fake_http
class BaseComputeServiceTest(base.TestCase):
@@ -26,11 +26,8 @@
json_body = json.dumps(body)
if to_utf:
json_body = json_body.encode('utf-8')
- resp_dict = {'status': status}
- if headers:
- resp_dict.update(headers)
- response = (httplib2.Response(resp_dict), json_body)
- return response
+ resp = fake_http.fake_http_response(headers, status=status), json_body
+ return resp
def check_service_client_function(self, function, function2mock,
body, to_utf=False, status=200,
diff --git a/tempest/tests/lib/services/compute/test_base_compute_client.py b/tempest/tests/lib/services/compute/test_base_compute_client.py
index f552ef5..49d29b3 100644
--- a/tempest/tests/lib/services/compute/test_base_compute_client.py
+++ b/tempest/tests/lib/services/compute/test_base_compute_client.py
@@ -12,14 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-import httplib2
import mock
-from oslotest import mockpatch
from tempest.lib.common import rest_client
from tempest.lib import exceptions
from tempest.lib.services.compute import base_compute_client
from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib import fake_http
from tempest.tests.lib.services.compute import base
@@ -36,28 +35,29 @@
super(TestMicroversionHeaderCheck, self).tearDown()
base_compute_client.COMPUTE_MICROVERSION = None
- def _check_microverion_header_in_response(self, fake_response):
- def request(*args, **kwargs):
- return (httplib2.Response(fake_response), {})
-
- self.useFixture(mockpatch.PatchObject(
- rest_client.RestClient,
- 'request',
- side_effect=request))
-
- def test_correct_microverion_in_response(self):
- fake_response = {self.client.api_microversion_header_name: '2.2'}
- self._check_microverion_header_in_response(fake_response)
+ @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+ def test_correct_microverion_in_response(self, mock_request):
+ response = fake_http.fake_http_response(
+ headers={self.client.api_microversion_header_name: '2.2'},
+ )
+ mock_request.return_value = response, ''
self.client.get('fake_url')
- def test_incorrect_microverion_in_response(self):
- fake_response = {self.client.api_microversion_header_name: '2.3'}
- self._check_microverion_header_in_response(fake_response)
+ @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+ def test_incorrect_microverion_in_response(self, mock_request):
+ response = fake_http.fake_http_response(
+ headers={self.client.api_microversion_header_name: '2.3'},
+ )
+ mock_request.return_value = response, ''
self.assertRaises(exceptions.InvalidHTTPResponseHeader,
self.client.get, 'fake_url')
- def test_no_microverion_header_in_response(self):
- self._check_microverion_header_in_response({})
+ @mock.patch('tempest.lib.common.http.ClosingHttp.request')
+ def test_no_microverion_header_in_response(self, mock_request):
+ response = fake_http.fake_http_response(
+ headers={},
+ )
+ mock_request.return_value = response, ''
self.assertRaises(exceptions.InvalidHTTPResponseHeader,
self.client.get, 'fake_url')
@@ -164,7 +164,7 @@
def test_no_microverion_header_in_raw_request(self):
def raw_request(*args, **kwargs):
self.assertNotIn('X-OpenStack-Nova-API-Version', kwargs['headers'])
- return (httplib2.Response({'status': 200}), {})
+ return (fake_http.fake_http_response({}, status=200), '')
with mock.patch.object(rest_client.RestClient,
'raw_request') as mock_get:
@@ -196,9 +196,9 @@
self.assertIn('X-OpenStack-Nova-API-Version', kwargs['headers'])
self.assertEqual('2.2',
kwargs['headers']['X-OpenStack-Nova-API-Version'])
- return (httplib2.Response(
- {'status': 200,
- self.client.api_microversion_header_name: '2.2'}), {})
+ return (fake_http.fake_http_response(
+ headers={self.client.api_microversion_header_name: '2.2'},
+ status=200), '')
with mock.patch.object(rest_client.RestClient,
'raw_request') as mock_get:
diff --git a/tempest/tests/lib/services/compute/test_flavors_client.py b/tempest/tests/lib/services/compute/test_flavors_client.py
index 795aff7..e22b4fe 100644
--- a/tempest/tests/lib/services/compute/test_flavors_client.py
+++ b/tempest/tests/lib/services/compute/test_flavors_client.py
@@ -13,13 +13,13 @@
# under the License.
import copy
-import httplib2
from oslo_serialization import jsonutils as json
from oslotest import mockpatch
from tempest.lib.services.compute import flavors_client
from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib import fake_http
from tempest.tests.lib.services.compute import base
@@ -117,7 +117,7 @@
body = json.dumps({'flavors': [TestFlavorsClient.FAKE_FLAVOR]})
if bytes_body:
body = body.encode('utf-8')
- response = (httplib2.Response({'status': 200}), body)
+ response = fake_http.fake_http_response({}, status=200), body
self.useFixture(mockpatch.Patch(
'tempest.lib.common.rest_client.RestClient.get',
return_value=response))
diff --git a/tempest/tests/lib/services/compute/test_images_client.py b/tempest/tests/lib/services/compute/test_images_client.py
index 28757c3..3ebc27f 100644
--- a/tempest/tests/lib/services/compute/test_images_client.py
+++ b/tempest/tests/lib/services/compute/test_images_client.py
@@ -186,7 +186,7 @@
def _test_resource_deleted(self, bytes_body=False):
params = {"id": self.FAKE_IMAGE_ID}
- expected_op = self.FAKE_IMAGE_DATA['show']['image']
+ expected_op = self.FAKE_IMAGE_DATA['show']
self.useFixture(mockpatch.Patch('tempest.lib.services.compute'
'.images_client.ImagesClient.show_image',
side_effect=lib_exc.NotFound))
diff --git a/tempest/tests/lib/services/compute/test_server_groups_client.py b/tempest/tests/lib/services/compute/test_server_groups_client.py
index f1f2906..cb163a8 100644
--- a/tempest/tests/lib/services/compute/test_server_groups_client.py
+++ b/tempest/tests/lib/services/compute/test_server_groups_client.py
@@ -12,12 +12,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import httplib2
-
from oslotest import mockpatch
from tempest.tests.lib import fake_auth_provider
from tempest.lib.services.compute import server_groups_client
+from tempest.tests.lib import fake_http
from tempest.tests.lib.services.compute import base
@@ -50,7 +49,7 @@
self._test_create_server_group(bytes_body=True)
def test_delete_server_group(self):
- response = (httplib2.Response({'status': 204}), None)
+ response = fake_http.fake_http_response({}, status=204), ''
self.useFixture(mockpatch.Patch(
'tempest.lib.common.rest_client.RestClient.delete',
return_value=response))
diff --git a/tempest/tests/lib/services/identity/v2/test_token_client.py b/tempest/tests/lib/services/identity/v2/test_token_client.py
index dd3533a..7925152 100644
--- a/tempest/tests/lib/services/identity/v2/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -14,13 +14,12 @@
import json
-import httplib2
-from oslotest import mockpatch
+import mock
from tempest.lib.common import rest_client
from tempest.lib import exceptions
from tempest.lib.services.identity.v2 import token_client
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_http
@@ -28,7 +27,6 @@
def setUp(self):
super(TestTokenClientV2, self).setUp()
- self.fake_200_http = fake_http.fake_httplib2(return_type=200)
def test_init_without_authurl(self):
self.assertRaises(exceptions.IdentityError,
@@ -36,10 +34,15 @@
def test_auth(self):
token_client_v2 = token_client.TokenClient('fake_url')
- post_mock = self.useFixture(mockpatch.PatchObject(
- token_client_v2, 'post', return_value=self.fake_200_http.request(
- 'fake_url', body={'access': {'token': 'fake_token'}})))
- resp = token_client_v2.auth('fake_user', 'fake_pass')
+ response = fake_http.fake_http_response(
+ None, status=200,
+ )
+ body = {'access': {'token': 'fake_token'}}
+
+ with mock.patch.object(token_client_v2, 'post') as post_mock:
+ post_mock.return_value = response, body
+ resp = token_client_v2.auth('fake_user', 'fake_pass')
+
self.assertIsInstance(resp, rest_client.ResponseBody)
req_dict = json.dumps({
'auth': {
@@ -49,15 +52,21 @@
},
}
}, sort_keys=True)
- post_mock.mock.assert_called_once_with('fake_url/tokens',
- body=req_dict)
+ post_mock.assert_called_once_with('fake_url/tokens',
+ body=req_dict)
def test_auth_with_tenant(self):
token_client_v2 = token_client.TokenClient('fake_url')
- post_mock = self.useFixture(mockpatch.PatchObject(
- token_client_v2, 'post', return_value=self.fake_200_http.request(
- 'fake_url', body={'access': {'token': 'fake_token'}})))
- resp = token_client_v2.auth('fake_user', 'fake_pass', 'fake_tenant')
+ response = fake_http.fake_http_response(
+ None, status=200,
+ )
+ body = {'access': {'token': 'fake_token'}}
+
+ with mock.patch.object(token_client_v2, 'post') as post_mock:
+ post_mock.return_value = response, body
+ resp = token_client_v2.auth('fake_user', 'fake_pass',
+ 'fake_tenant')
+
self.assertIsInstance(resp, rest_client.ResponseBody)
req_dict = json.dumps({
'auth': {
@@ -68,25 +77,31 @@
},
}
}, sort_keys=True)
- post_mock.mock.assert_called_once_with('fake_url/tokens',
- body=req_dict)
+ post_mock.assert_called_once_with('fake_url/tokens',
+ body=req_dict)
def test_request_with_str_body(self):
token_client_v2 = token_client.TokenClient('fake_url')
- self.useFixture(mockpatch.PatchObject(
- token_client_v2, 'raw_request', return_value=(
- httplib2.Response({'status': '200'}),
- str('{"access": {"token": "fake_token"}}'))))
- resp, body = token_client_v2.request('GET', 'fake_uri')
- self.assertIsInstance(resp, httplib2.Response)
+ response = fake_http.fake_http_response(
+ None, status=200,
+ )
+ body = str('{"access": {"token": "fake_token"}}')
+
+ with mock.patch.object(token_client_v2, 'raw_request') as mock_raw_r:
+ mock_raw_r.return_value = response, body
+ resp, body = token_client_v2.request('GET', 'fake_uri')
self.assertIsInstance(body, dict)
def test_request_with_bytes_body(self):
token_client_v2 = token_client.TokenClient('fake_url')
- self.useFixture(mockpatch.PatchObject(
- token_client_v2, 'raw_request', return_value=(
- httplib2.Response({'status': '200'}),
- bytes(b'{"access": {"token": "fake_token"}}'))))
- resp, body = token_client_v2.request('GET', 'fake_uri')
- self.assertIsInstance(resp, httplib2.Response)
+
+ response = fake_http.fake_http_response(
+ None, status=200,
+ )
+ body = b'{"access": {"token": "fake_token"}}'
+
+ with mock.patch.object(token_client_v2, 'raw_request') as mock_raw_r:
+ mock_raw_r.return_value = response, body
+ resp, body = token_client_v2.request('GET', 'fake_uri')
+
self.assertIsInstance(body, dict)
diff --git a/tempest/tests/lib/services/identity/v3/test_token_client.py b/tempest/tests/lib/services/identity/v3/test_token_client.py
index bb4dae3..e9ef740 100644
--- a/tempest/tests/lib/services/identity/v3/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_token_client.py
@@ -14,21 +14,19 @@
import json
-import httplib2
-from oslotest import mockpatch
+import mock
from tempest.lib.common import rest_client
from tempest.lib import exceptions
from tempest.lib.services.identity.v3 import token_client
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_http
-class TestTokenClientV2(base.TestCase):
+class TestTokenClientV3(base.TestCase):
def setUp(self):
- super(TestTokenClientV2, self).setUp()
- self.fake_201_http = fake_http.fake_httplib2(return_type=201)
+ super(TestTokenClientV3, self).setUp()
def test_init_without_authurl(self):
self.assertRaises(exceptions.IdentityError,
@@ -36,10 +34,16 @@
def test_auth(self):
token_client_v3 = token_client.V3TokenClient('fake_url')
- post_mock = self.useFixture(mockpatch.PatchObject(
- token_client_v3, 'post', return_value=self.fake_201_http.request(
- 'fake_url', body={'access': {'token': 'fake_token'}})))
- resp = token_client_v3.auth(username='fake_user', password='fake_pass')
+ response = fake_http.fake_http_response(
+ None, status=201,
+ )
+ body = {'access': {'token': 'fake_token'}}
+
+ with mock.patch.object(token_client_v3, 'post') as post_mock:
+ post_mock.return_value = response, body
+ resp = token_client_v3.auth(username='fake_user',
+ password='fake_pass')
+
self.assertIsInstance(resp, rest_client.ResponseBody)
req_dict = json.dumps({
'auth': {
@@ -54,19 +58,24 @@
},
}
}, sort_keys=True)
- post_mock.mock.assert_called_once_with('fake_url/auth/tokens',
- body=req_dict)
+ post_mock.assert_called_once_with('fake_url/auth/tokens',
+ body=req_dict)
def test_auth_with_project_id_and_domain_id(self):
token_client_v3 = token_client.V3TokenClient('fake_url')
- post_mock = self.useFixture(mockpatch.PatchObject(
- token_client_v3, 'post', return_value=self.fake_201_http.request(
- 'fake_url', body={'access': {'token': 'fake_token'}})))
- resp = token_client_v3.auth(
- username='fake_user', password='fake_pass',
- project_id='fcac2a055a294e4c82d0a9c21c620eb4',
- user_domain_id='14f4a9a99973404d8c20ba1d2af163ff',
- project_domain_id='291f63ae9ac54ee292ca09e5f72d9676')
+ response = fake_http.fake_http_response(
+ None, status=201,
+ )
+ body = {'access': {'token': 'fake_token'}}
+
+ with mock.patch.object(token_client_v3, 'post') as post_mock:
+ post_mock.return_value = response, body
+ resp = token_client_v3.auth(
+ username='fake_user', password='fake_pass',
+ project_id='fcac2a055a294e4c82d0a9c21c620eb4',
+ user_domain_id='14f4a9a99973404d8c20ba1d2af163ff',
+ project_domain_id='291f63ae9ac54ee292ca09e5f72d9676')
+
self.assertIsInstance(resp, rest_client.ResponseBody)
req_dict = json.dumps({
'auth': {
@@ -92,16 +101,22 @@
}
}
}, sort_keys=True)
- post_mock.mock.assert_called_once_with('fake_url/auth/tokens',
- body=req_dict)
+ post_mock.assert_called_once_with('fake_url/auth/tokens',
+ body=req_dict)
def test_auth_with_tenant(self):
- token_client_v2 = token_client.V3TokenClient('fake_url')
- post_mock = self.useFixture(mockpatch.PatchObject(
- token_client_v2, 'post', return_value=self.fake_201_http.request(
- 'fake_url', body={'access': {'token': 'fake_token'}})))
- resp = token_client_v2.auth(username='fake_user', password='fake_pass',
- project_name='fake_tenant')
+ token_client_v3 = token_client.V3TokenClient('fake_url')
+ response = fake_http.fake_http_response(
+ None, status=201,
+ )
+ body = {'access': {'token': 'fake_token'}}
+
+ with mock.patch.object(token_client_v3, 'post') as post_mock:
+ post_mock.return_value = response, body
+ resp = token_client_v3.auth(username='fake_user',
+ password='fake_pass',
+ project_name='fake_tenant')
+
self.assertIsInstance(resp, rest_client.ResponseBody)
req_dict = json.dumps({
'auth': {
@@ -121,25 +136,32 @@
}
}, sort_keys=True)
- post_mock.mock.assert_called_once_with('fake_url/auth/tokens',
- body=req_dict)
+ post_mock.assert_called_once_with('fake_url/auth/tokens',
+ body=req_dict)
def test_request_with_str_body(self):
token_client_v3 = token_client.V3TokenClient('fake_url')
- self.useFixture(mockpatch.PatchObject(
- token_client_v3, 'raw_request', return_value=(
- httplib2.Response({"status": "200"}),
- str('{"access": {"token": "fake_token"}}'))))
- resp, body = token_client_v3.request('GET', 'fake_uri')
- self.assertIsInstance(resp, httplib2.Response)
+ response = fake_http.fake_http_response(
+ None, status=200,
+ )
+ body = str('{"access": {"token": "fake_token"}}')
+
+ with mock.patch.object(token_client_v3, 'raw_request') as mock_raw_r:
+ mock_raw_r.return_value = response, body
+ resp, body = token_client_v3.request('GET', 'fake_uri')
+
self.assertIsInstance(body, dict)
def test_request_with_bytes_body(self):
token_client_v3 = token_client.V3TokenClient('fake_url')
- self.useFixture(mockpatch.PatchObject(
- token_client_v3, 'raw_request', return_value=(
- httplib2.Response({"status": "200"}),
- bytes(b'{"access": {"token": "fake_token"}}'))))
- resp, body = token_client_v3.request('GET', 'fake_uri')
- self.assertIsInstance(resp, httplib2.Response)
+
+ response = fake_http.fake_http_response(
+ None, status=200,
+ )
+ body = b'{"access": {"token": "fake_token"}}'
+
+ with mock.patch.object(token_client_v3, 'raw_request') as mock_raw_r:
+ mock_raw_r.return_value = response, body
+ resp, body = token_client_v3.request('GET', 'fake_uri')
+
self.assertIsInstance(body, dict)
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index 55f0c4e..cc71c92 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -22,9 +22,8 @@
from tempest.lib import exceptions
from tempest.lib.services.identity.v2 import token_client as v2_client
from tempest.lib.services.identity.v3 import token_client as v3_client
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_credentials
-from tempest.tests.lib import fake_http
from tempest.tests.lib import fake_identity
@@ -42,8 +41,7 @@
def setUp(self):
super(BaseAuthTestsSetUp, self).setUp()
- self.fake_http = fake_http.fake_httplib2(return_type=200)
- self.stubs.Set(auth, 'get_credentials', fake_get_credentials)
+ self.patchobject(auth, 'get_credentials', fake_get_credentials)
self.auth_provider = self._auth(self.credentials,
fake_identity.FAKE_AUTH_URL)
@@ -120,8 +118,8 @@
def setUp(self):
super(TestKeystoneV2AuthProvider, self).setUp()
- self.stubs.Set(v2_client.TokenClient, 'raw_request',
- fake_identity._fake_v2_response)
+ self.patchobject(v2_client.TokenClient, 'raw_request',
+ fake_identity._fake_v2_response)
self.target_url = 'test_api'
def _get_fake_identity(self):
@@ -435,8 +433,8 @@
def setUp(self):
super(TestKeystoneV3AuthProvider, self).setUp()
- self.stubs.Set(v3_client.V3TokenClient, 'raw_request',
- fake_identity._fake_v3_response)
+ self.patchobject(v3_client.V3TokenClient, 'raw_request',
+ fake_identity._fake_v3_response)
def _get_fake_identity(self):
return fake_identity.IDENTITY_V3_RESPONSE['token']
@@ -570,3 +568,65 @@
attrs = {'tenant_name': 'tenant', 'project_name': 'project'}
self.assertRaises(
exceptions.InvalidCredentials, auth.KeystoneV3Credentials, **attrs)
+
+
+class TestReplaceVersion(base.TestCase):
+ def test_version_no_trailing_path(self):
+ self.assertEqual(
+ 'http://localhost:35357/v2.0',
+ auth.replace_version('http://localhost:35357/v3', 'v2.0'))
+
+ def test_version_no_trailing_path_solidus(self):
+ self.assertEqual(
+ 'http://localhost:35357/v2.0/',
+ auth.replace_version('http://localhost:35357/v3/', 'v2.0'))
+
+ def test_version_trailing_path(self):
+ self.assertEqual(
+ 'http://localhost:35357/v2.0/uuid',
+ auth.replace_version('http://localhost:35357/v3/uuid', 'v2.0'))
+
+ def test_version_trailing_path_solidus(self):
+ self.assertEqual(
+ 'http://localhost:35357/v2.0/uuid/',
+ auth.replace_version('http://localhost:35357/v3/uuid/', 'v2.0'))
+
+ def test_no_version_base(self):
+ self.assertEqual(
+ 'http://localhost:35357/v2.0',
+ auth.replace_version('http://localhost:35357', 'v2.0'))
+
+ def test_no_version_base_solidus(self):
+ self.assertEqual(
+ 'http://localhost:35357/v2.0',
+ auth.replace_version('http://localhost:35357/', 'v2.0'))
+
+ def test_no_version_path(self):
+ self.assertEqual(
+ 'http://localhost/identity/v2.0',
+ auth.replace_version('http://localhost/identity', 'v2.0'))
+
+ def test_no_version_path_solidus(self):
+ self.assertEqual(
+ 'http://localhost/identity/v2.0',
+ auth.replace_version('http://localhost/identity/', 'v2.0'))
+
+ def test_path_version(self):
+ self.assertEqual(
+ 'http://localhost/identity/v2.0',
+ auth.replace_version('http://localhost/identity/v3', 'v2.0'))
+
+ def test_path_version_solidus(self):
+ self.assertEqual(
+ 'http://localhost/identity/v2.0/',
+ auth.replace_version('http://localhost/identity/v3/', 'v2.0'))
+
+ def test_path_version_trailing_path(self):
+ self.assertEqual(
+ 'http://localhost/identity/v2.0/uuid',
+ auth.replace_version('http://localhost/identity/v3/uuid', 'v2.0'))
+
+ def test_path_version_trailing_path_solidus(self):
+ self.assertEqual(
+ 'http://localhost/identity/v2.0/uuid/',
+ auth.replace_version('http://localhost/identity/v3/uuid/', 'v2.0'))
diff --git a/tempest/tests/lib/test_credentials.py b/tempest/tests/lib/test_credentials.py
index 791fbb5..ca3baa1 100644
--- a/tempest/tests/lib/test_credentials.py
+++ b/tempest/tests/lib/test_credentials.py
@@ -19,7 +19,7 @@
from tempest.lib import exceptions
from tempest.lib.services.identity.v2 import token_client as v2_client
from tempest.lib.services.identity.v3 import token_client as v3_client
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_identity
@@ -72,8 +72,8 @@
def setUp(self):
super(KeystoneV2CredentialsTests, self).setUp()
- self.stubs.Set(self.tokenclient_class, 'raw_request',
- self.identity_response)
+ self.patchobject(self.tokenclient_class, 'raw_request',
+ self.identity_response)
def _verify_credentials(self, credentials_class, creds_dict, filled=True):
creds = auth.get_credentials(fake_identity.FAKE_AUTH_URL,
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index 07b577c..f3a4e9c 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -13,13 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
import testtools
from tempest.lib import base as test
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest.tests.lib import base
+from tempest.tests import base
class TestSkipBecauseDecorator(base.TestCase):
@@ -78,13 +77,13 @@
return foo
def test_positive(self):
- _id = str(uuid.uuid4())
+ _id = data_utils.rand_uuid()
foo = self._test_helper(_id)
self.assertIn('id-%s' % _id, getattr(foo, '__testtools_attrs'))
self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id))
def test_positive_without_doc(self):
- _id = str(uuid.uuid4())
+ _id = data_utils.rand_uuid()
foo = self._test_helper_without_doc(_id)
self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id))
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 87af455..2959294 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -15,14 +15,14 @@
import copy
import json
-import httplib2
import jsonschema
from oslotest import mockpatch
import six
+from tempest.lib.common import http
from tempest.lib.common import rest_client
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib import fake_http
import tempest.tests.utils as utils
@@ -37,7 +37,7 @@
self.fake_auth_provider = fake_auth_provider.FakeAuthProvider()
self.rest_client = rest_client.RestClient(
self.fake_auth_provider, None, None)
- self.stubs.Set(httplib2.Http, 'request', self.fake_http.request)
+ self.patchobject(http.ClosingHttp, 'request', self.fake_http.request)
self.useFixture(mockpatch.PatchObject(self.rest_client,
'_log_request'))
@@ -292,7 +292,9 @@
if absolute_limit is False:
resp_dict.update({'retry-after': 120})
resp_body.update({'overLimit': {'message': 'fake_message'}})
- resp = httplib2.Response(resp_dict)
+ resp = fake_http.fake_http_response(headers=resp_dict,
+ status=int(r_code),
+ body=json.dumps(resp_body))
data = {
"method": "fake_method",
"url": "fake_url",
diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py
index f6efd47..b07f6bc 100644
--- a/tempest/tests/lib/test_ssh.py
+++ b/tempest/tests/lib/test_ssh.py
@@ -21,7 +21,7 @@
from tempest.lib.common import ssh
from tempest.lib import exceptions
-from tempest.tests.lib import base
+from tempest.tests import base
import tempest.tests.utils as utils
@@ -141,8 +141,6 @@
def test_exec_command(self):
chan_mock, poll_mock, select_mock = (
self._set_mocks_for_select([[1, 0, 0]], True))
- closed_prop = mock.PropertyMock(return_value=True)
- type(chan_mock).closed = closed_prop
chan_mock.recv_exit_status.return_value = 0
chan_mock.recv.return_value = b''
@@ -164,7 +162,6 @@
chan_mock.recv_stderr_ready.assert_called_once_with()
chan_mock.recv_stderr.assert_called_once_with(1024)
chan_mock.recv_exit_status.assert_called_once_with()
- closed_prop.assert_called_once_with()
def _set_mocks_for_select(self, poll_data, ito_value=False):
gsc_mock = self.patch('tempest.lib.common.ssh.Client.'
@@ -184,7 +181,7 @@
gsc_mock.return_value = client_mock
ito_mock.return_value = ito_value
client_mock.get_transport.return_value = tran_mock
- tran_mock.open_session.return_value = chan_mock
+ tran_mock.open_session().__enter__.return_value = chan_mock
if isinstance(poll_data[0], list):
poll_mock.poll.side_effect = poll_data
else:
@@ -242,7 +239,7 @@
gsc_mock.return_value = client_mock
client_mock.get_transport.return_value = tran_mock
- tran_mock.open_session.return_value = chan_mock
+ tran_mock.open_session().__enter__.return_value = chan_mock
chan_mock.recv_exit_status.return_value = 0
std_out_mock = mock.MagicMock(StringIO)
diff --git a/tempest/tests/lib/test_tempest_lib.py b/tempest/tests/lib/test_tempest_lib.py
index 9731e96..d70e53d 100644
--- a/tempest/tests/lib/test_tempest_lib.py
+++ b/tempest/tests/lib/test_tempest_lib.py
@@ -19,7 +19,7 @@
Tests for `tempest.lib` module.
"""
-from tempest.tests.lib import base
+from tempest.tests import base
class TestTempest_lib(base.TestCase):
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
index 7a127cd..44ce567 100644
--- a/tempest/tests/negative/test_negative_auto_test.py
+++ b/tempest/tests/negative/test_negative_auto_test.py
@@ -37,7 +37,8 @@
def setUp(self):
super(TestNegativeAutoTest, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
def _check_prop_entries(self, result, entry):
entries = [a for a in result if entry in a[0]]
diff --git a/tempest/tests/services/compute/base.py b/tempest/tests/services/compute/base.py
deleted file mode 100644
index a35a87c..0000000
--- a/tempest/tests/services/compute/base.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2015 Deutsche Telekom AG. 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 httplib2
-
-from oslo_serialization import jsonutils as json
-from oslotest import mockpatch
-
-from tempest.tests import base
-
-
-class BaseComputeServiceTest(base.TestCase):
- def create_response(self, body, to_utf=False, status=200):
- json_body = {}
- if body:
- json_body = json.dumps(body)
- if to_utf:
- json_body = json_body.encode('utf-8')
- response = (httplib2.Response({'status': status}), json_body)
- return response
-
- def check_service_client_function(self, function, function2mock,
- body, to_utf=False, status=200,
- **kwargs):
- mocked_response = self.create_response(body, to_utf, status)
- self.useFixture(mockpatch.Patch(
- function2mock, return_value=mocked_response))
- if kwargs:
- resp = function(**kwargs)
- else:
- resp = function()
- self.assertEqual(body, resp)
diff --git a/tempest/tests/services/compute/test_keypairs_client.py b/tempest/tests/services/compute/test_keypairs_client.py
deleted file mode 100644
index e8f8280..0000000
--- a/tempest/tests/services/compute/test_keypairs_client.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright 2015 NEC Corporation. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-
-from tempest.services.compute.json import keypairs_client
-from tempest.tests.lib import fake_auth_provider
-from tempest.tests.services.compute import base
-
-
-class TestKeyPairsClient(base.BaseComputeServiceTest):
-
- FAKE_KEYPAIR = {"keypair": {
- "public_key": "ssh-rsa foo Generated-by-Nova",
- "name": u'\u2740(*\xb4\u25e1`*)\u2740',
- "user_id": "525d55f98980415ba98e634972fa4a10",
- "fingerprint": "76:24:66:49:d7:ca:6e:5c:77:ea:8e:bb:9c:15:5f:98"
- }}
-
- def setUp(self):
- super(TestKeyPairsClient, self).setUp()
- fake_auth = fake_auth_provider.FakeAuthProvider()
- self.client = keypairs_client.KeyPairsClient(
- fake_auth, 'compute', 'regionOne')
-
- def _test_list_keypairs(self, bytes_body=False):
- self.check_service_client_function(
- self.client.list_keypairs,
- 'tempest.lib.common.rest_client.RestClient.get',
- {"keypairs": []},
- bytes_body)
-
- def test_list_keypairs_with_str_body(self):
- self._test_list_keypairs()
-
- def test_list_keypairs_with_bytes_body(self):
- self._test_list_keypairs(bytes_body=True)
-
- def _test_show_keypair(self, bytes_body=False):
- fake_keypair = copy.deepcopy(self.FAKE_KEYPAIR)
- fake_keypair["keypair"].update({
- "deleted": False,
- "created_at": "2015-07-22T04:53:52.000000",
- "updated_at": None,
- "deleted_at": None,
- "id": 1
- })
-
- self.check_service_client_function(
- self.client.show_keypair,
- 'tempest.lib.common.rest_client.RestClient.get',
- fake_keypair,
- bytes_body,
- keypair_name="test")
-
- def test_show_keypair_with_str_body(self):
- self._test_show_keypair()
-
- def test_show_keypair_with_bytes_body(self):
- self._test_show_keypair(bytes_body=True)
-
- def _test_create_keypair(self, bytes_body=False):
- fake_keypair = copy.deepcopy(self.FAKE_KEYPAIR)
- fake_keypair["keypair"].update({"private_key": "foo"})
-
- self.check_service_client_function(
- self.client.create_keypair,
- 'tempest.lib.common.rest_client.RestClient.post',
- fake_keypair,
- bytes_body,
- name="test")
-
- def test_create_keypair_with_str_body(self):
- self._test_create_keypair()
-
- def test_create_keypair_with_bytes_body(self):
- self._test_create_keypair(bytes_body=True)
-
- def test_delete_keypair(self):
- self.check_service_client_function(
- self.client.delete_keypair,
- 'tempest.lib.common.rest_client.RestClient.delete',
- {}, status=202, keypair_name='test')
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index 98b045a..af5fc09 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -12,8 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
import mock
from oslo_config import cfg
from oslotest import mockpatch
@@ -21,6 +19,7 @@
from tempest import config
from tempest import exceptions
+from tempest.lib.common.utils import data_utils
from tempest import test
from tempest.tests import base
from tempest.tests import fake_config
@@ -30,7 +29,8 @@
def setUp(self):
super(BaseDecoratorsTest, self).setUp()
self.config_fixture = self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
class TestAttrDecorator(BaseDecoratorsTest):
@@ -79,13 +79,13 @@
return foo
def test_positive(self):
- _id = str(uuid.uuid4())
+ _id = data_utils.rand_uuid()
foo = self._test_helper(_id)
self.assertIn('id-%s' % _id, getattr(foo, '__testtools_attrs'))
self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id))
def test_positive_without_doc(self):
- _id = str(uuid.uuid4())
+ _id = data_utils.rand_uuid()
foo = self._test_helper_without_doc(_id)
self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id))
@@ -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',
@@ -240,3 +246,75 @@
self.assertIn("test_fake_negative", dir(obj))
obj.test_fake_negative()
mock.assert_called_once_with(self.FakeNegativeJSONTest._schema)
+
+
+class TestConfigDecorators(BaseDecoratorsTest):
+ def setUp(self):
+ super(TestConfigDecorators, self).setUp()
+ cfg.CONF.set_default('nova', True, 'service_available')
+ cfg.CONF.set_default('glance', False, 'service_available')
+
+ def _test_skip_unless_config(self, expected_to_skip=True, *decorator_args):
+
+ class TestFoo(test.BaseTestCase):
+ @config.skip_unless_config(*decorator_args)
+ def test_bar(self):
+ return 0
+
+ t = TestFoo('test_bar')
+ if expected_to_skip:
+ self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ else:
+ try:
+ self.assertEqual(t.test_bar(), 0)
+ except testtools.TestCase.skipException:
+ # We caught a skipException but we didn't expect to skip
+ # this test so raise a hard test failure instead.
+ raise testtools.TestCase.failureException(
+ "Not supposed to skip")
+
+ def _test_skip_if_config(self, expected_to_skip=True,
+ *decorator_args):
+
+ class TestFoo(test.BaseTestCase):
+ @config.skip_if_config(*decorator_args)
+ def test_bar(self):
+ return 0
+
+ t = TestFoo('test_bar')
+ if expected_to_skip:
+ self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ else:
+ try:
+ self.assertEqual(t.test_bar(), 0)
+ except testtools.TestCase.skipException:
+ # We caught a skipException but we didn't expect to skip
+ # this test so raise a hard test failure instead.
+ raise testtools.TestCase.failureException(
+ "Not supposed to skip")
+
+ def test_skip_unless_no_group(self):
+ self._test_skip_unless_config(True, 'fake_group', 'an_option')
+
+ def test_skip_unless_no_option(self):
+ self._test_skip_unless_config(True, 'service_available',
+ 'not_an_option')
+
+ def test_skip_unless_false_option(self):
+ self._test_skip_unless_config(True, 'service_available', 'glance')
+
+ def test_skip_unless_true_option(self):
+ self._test_skip_unless_config(False,
+ 'service_available', 'nova')
+
+ def test_skip_if_no_group(self):
+ self._test_skip_if_config(False, 'fake_group', 'an_option')
+
+ def test_skip_if_no_option(self):
+ self._test_skip_if_config(False, 'service_available', 'not_an_option')
+
+ def test_skip_if_false_option(self):
+ self._test_skip_if_config(False, 'service_available', 'glance')
+
+ def test_skip_if_true_option(self):
+ self._test_skip_if_config(True, 'service_available', 'nova')
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index 1811141..768cd05 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -24,18 +24,13 @@
from tempest import exceptions
from tempest.tests import base
from tempest.tests import fake_auth_provider
-from tempest.tests import fake_http
+from tempest.tests.lib import fake_http
class TestGlanceHTTPClient(base.TestCase):
def setUp(self):
super(TestGlanceHTTPClient, self).setUp()
- self.fake_http = fake_http.fake_httplib2(return_type=200)
- # NOTE(maurosr): using http here implies that we will be using httplib
- # directly. With https glance_client would use an httpS version, but
- # the real backend would still be httplib anyway and since we mock it
- # that there is no reason to care.
self.endpoint = 'http://fake_url.com'
self.fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -44,12 +39,12 @@
self.useFixture(mockpatch.PatchObject(
httplib.HTTPConnection,
'request',
- side_effect=self.fake_http.request(self.endpoint)[1]))
+ side_effect=b'fake_body'))
self.client = glance_http.HTTPClient(self.fake_auth, {})
def _set_response_fixture(self, header, status, resp_body):
- resp = fake_http.fake_httplib(header, status=status,
- body=six.StringIO(resp_body))
+ resp = fake_http.fake_http_response(header, status=status,
+ body=six.StringIO(resp_body))
self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
'getresponse', return_value=resp))
return resp
@@ -93,29 +88,29 @@
self.assertEqual(httplib.HTTPConnection, conn_class)
def test_get_connection_http(self):
- self.assertTrue(isinstance(self.client._get_connection(),
- httplib.HTTPConnection))
+ self.assertIsInstance(self.client._get_connection(),
+ httplib.HTTPConnection)
def test_get_connection_https(self):
endpoint = 'https://fake_url.com'
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))
+ self.assertIsInstance(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))
+ self.assertIsInstance(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))
+ self.assertIsInstance(self.client._get_connection(),
+ glance_http.VerifiedHTTPSConnection)
def test_get_connection_url_not_fount(self):
self.useFixture(mockpatch.PatchObject(self.client, 'connection_class',
@@ -223,7 +218,7 @@
class TestResponseBodyIterator(base.TestCase):
def test_iter_default_chunk_size_64k(self):
- resp = fake_http.fake_httplib({}, six.StringIO(
+ resp = fake_http.fake_http_response({}, six.StringIO(
'X' * (glance_http.CHUNKSIZE + 1)))
iterator = glance_http.ResponseBodyIterator(resp)
chunks = list(iterator)
diff --git a/tempest/tests/test_microversions.py b/tempest/tests/test_microversions.py
index cef7975..173accb 100644
--- a/tempest/tests/test_microversions.py
+++ b/tempest/tests/test_microversions.py
@@ -57,8 +57,8 @@
def setUp(self):
super(TestMicroversionsTestsClass, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate',
- fake_config.FakePrivate)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
def _test_version(self, cfg_min, cfg_max,
expected_pass_tests,
diff --git a/tempest/tests/test_negative_rest_client.py b/tempest/tests/test_negative_rest_client.py
index ce95739..9d9c20f 100644
--- a/tempest/tests/test_negative_rest_client.py
+++ b/tempest/tests/test_negative_rest_client.py
@@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import httplib2
+import mock
from oslotest import mockpatch
from tempest.common import negative_rest_client
@@ -23,7 +23,6 @@
from tempest.tests import base
from tempest.tests import fake_auth_provider
from tempest.tests import fake_config
-from tempest.tests import fake_http
class TestNegativeRestClient(base.TestCase):
@@ -31,59 +30,70 @@
url = 'fake_endpoint'
def setUp(self):
- self.fake_http = fake_http.fake_httplib2()
super(TestNegativeRestClient, self).setUp()
self.useFixture(fake_config.ConfigFixture())
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
- self.stubs.Set(httplib2.Http, 'request', self.fake_http.request)
+ self.patchobject(config, 'TempestConfigPrivate',
+ fake_config.FakePrivate)
self.negative_rest_client = negative_rest_client.NegativeRestClient(
fake_auth_provider.FakeAuthProvider(), None)
self.useFixture(mockpatch.PatchObject(self.negative_rest_client,
'_log_request'))
- def test_post(self):
+ @mock.patch('tempest.lib.common.rest_client.RestClient.post',
+ return_value=(mock.Mock(), mock.Mock()))
+ def test_post(self, mock_post):
__, return_dict = self.negative_rest_client.send_request('POST',
self.url,
[], {})
- self.assertEqual('POST', return_dict['method'])
+ mock_post.assert_called_once_with(self.url, {})
- def test_get(self):
+ @mock.patch('tempest.lib.common.rest_client.RestClient.get',
+ return_value=(mock.Mock(), mock.Mock()))
+ def test_get(self, mock_get):
__, return_dict = self.negative_rest_client.send_request('GET',
self.url,
[])
- self.assertEqual('GET', return_dict['method'])
+ mock_get.assert_called_once_with(self.url)
- def test_delete(self):
+ @mock.patch('tempest.lib.common.rest_client.RestClient.delete',
+ return_value=(mock.Mock(), mock.Mock()))
+ def test_delete(self, mock_delete):
__, return_dict = self.negative_rest_client.send_request('DELETE',
self.url,
[])
- self.assertEqual('DELETE', return_dict['method'])
+ mock_delete.assert_called_once_with(self.url)
- def test_patch(self):
+ @mock.patch('tempest.lib.common.rest_client.RestClient.patch',
+ return_value=(mock.Mock(), mock.Mock()))
+ def test_patch(self, mock_patch):
__, return_dict = self.negative_rest_client.send_request('PATCH',
self.url,
[], {})
- self.assertEqual('PATCH', return_dict['method'])
+ mock_patch.assert_called_once_with(self.url, {})
- def test_put(self):
+ @mock.patch('tempest.lib.common.rest_client.RestClient.put',
+ return_value=(mock.Mock(), mock.Mock()))
+ def test_put(self, mock_put):
__, return_dict = self.negative_rest_client.send_request('PUT',
self.url,
[], {})
- self.assertEqual('PUT', return_dict['method'])
+ mock_put.assert_called_once_with(self.url, {})
- def test_head(self):
- self.useFixture(mockpatch.PatchObject(self.negative_rest_client,
- 'response_checker'))
+ @mock.patch('tempest.lib.common.rest_client.RestClient.head',
+ return_value=(mock.Mock(), mock.Mock()))
+ def test_head(self, mock_head):
__, return_dict = self.negative_rest_client.send_request('HEAD',
self.url,
[])
- self.assertEqual('HEAD', return_dict['method'])
+ mock_head.assert_called_once_with(self.url)
- def test_copy(self):
+ @mock.patch('tempest.lib.common.rest_client.RestClient.copy',
+ return_value=(mock.Mock(), mock.Mock()))
+ def test_copy(self, mock_copy):
__, return_dict = self.negative_rest_client.send_request('COPY',
self.url,
[])
- self.assertEqual('COPY', return_dict['method'])
+ mock_copy.assert_called_once_with(self.url)
def test_other(self):
self.assertRaises(AssertionError,
diff --git a/test-requirements.txt b/test-requirements.txt
index be3a4f1..9ef956a 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -6,8 +6,7 @@
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
python-subunit>=0.0.18 # Apache-2.0/BSD
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
-reno>=0.1.1 # Apache2
-mox>=0.5.3 # Apache-2.0
+reno>=1.6.2 # Apache2
mock>=1.2 # BSD
coverage>=3.6 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
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
diff --git a/tools/install_venv.py b/tools/install_venv.py
deleted file mode 100644
index d6d9c8e..0000000
--- a/tools/install_venv.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright 2010 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# All Rights Reserved.
-#
-# Copyright 2010 OpenStack Foundation
-# Copyright 2013 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 os
-import sys
-
-import install_venv_common as install_venv # noqa
-
-
-def print_help(venv, root):
- help = """
- OpenStack development environment setup is complete.
-
- OpenStack development uses virtualenv to track and manage Python
- dependencies while in development and testing.
-
- To activate the OpenStack virtualenv for the extent of your current shell
- session you can run:
-
- $ source %s/bin/activate
-
- Or, if you prefer, you can run commands in the virtualenv on a case by case
- basis by running:
-
- $ %s/tools/with_venv.sh <your command>
-
- Also, make test will automatically use the virtualenv.
- """
- print(help % (venv, root))
-
-
-def main(argv):
- root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-
- if os.environ.get('TOOLS_PATH'):
- root = os.environ['TOOLS_PATH']
- venv = os.path.join(root, '.venv')
- if os.environ.get('VENV'):
- venv = os.environ['VENV']
-
- pip_requires = os.path.join(root, 'requirements.txt')
- test_requires = os.path.join(root, 'test-requirements.txt')
- py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
- project = 'Tempest'
- install = install_venv.InstallVenv(root, venv, pip_requires, test_requires,
- py_version, project)
- options = install.parse_args(argv)
- install.check_python_version()
- install.check_dependencies()
- install.create_virtualenv(no_site_packages=options.no_site_packages)
- install.install_dependencies()
- print_help(venv, root)
-
-if __name__ == '__main__':
- main(sys.argv)
diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py
deleted file mode 100644
index e279159..0000000
--- a/tools/install_venv_common.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-# Copyright 2013 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.
-
-"""Provides methods needed by installation script for OpenStack development
-virtual environments.
-
-Since this script is used to bootstrap a virtualenv from the system's Python
-environment, it should be kept strictly compatible with Python 2.6.
-
-Synced in from openstack-common
-"""
-
-from __future__ import print_function
-
-import optparse
-import os
-import subprocess
-import sys
-
-
-class InstallVenv(object):
-
- def __init__(self, root, venv, requirements,
- test_requirements, py_version,
- project):
- self.root = root
- self.venv = venv
- self.requirements = requirements
- self.test_requirements = test_requirements
- self.py_version = py_version
- self.project = project
-
- def die(self, message, *args):
- print(message % args, file=sys.stderr)
- sys.exit(1)
-
- def check_python_version(self):
- if sys.version_info < (2, 6):
- self.die("Need Python Version >= 2.6")
-
- def run_command_with_code(self, cmd, redirect_output=True,
- check_exit_code=True):
- """Runs a command in an out-of-process shell.
-
- Returns the output of that command. Working directory is self.root.
- """
- if redirect_output:
- stdout = subprocess.PIPE
- else:
- stdout = None
-
- proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
- output = proc.communicate()[0]
- if check_exit_code and proc.returncode != 0:
- self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
- return (output, proc.returncode)
-
- def run_command(self, cmd, redirect_output=True, check_exit_code=True):
- return self.run_command_with_code(cmd, redirect_output,
- check_exit_code)[0]
-
- def get_distro(self):
- if (os.path.exists('/etc/fedora-release') or
- os.path.exists('/etc/redhat-release')):
- return Fedora(
- self.root, self.venv, self.requirements,
- self.test_requirements, self.py_version, self.project)
- else:
- return Distro(
- self.root, self.venv, self.requirements,
- self.test_requirements, self.py_version, self.project)
-
- def check_dependencies(self):
- self.get_distro().install_virtualenv()
-
- def create_virtualenv(self, no_site_packages=True):
- """Creates the virtual environment and installs PIP.
-
- Creates the virtual environment and installs PIP only into the
- virtual environment.
- """
- if not os.path.isdir(self.venv):
- print('Creating venv...', end=' ')
- if no_site_packages:
- self.run_command(['virtualenv', '-q', '--no-site-packages',
- self.venv])
- else:
- self.run_command(['virtualenv', '-q', self.venv])
- print('done.')
- else:
- print("venv already exists...")
- pass
-
- def pip_install(self, *args):
- self.run_command(['tools/with_venv.sh',
- 'pip', 'install', '--upgrade'] + list(args),
- redirect_output=False)
-
- def install_dependencies(self):
- print('Installing dependencies with pip (this can take a while)...')
-
- # First things first, make sure our venv has the latest pip and
- # setuptools and pbr
- self.pip_install('pip>=1.4')
- self.pip_install('setuptools')
- self.pip_install('pbr')
-
- self.pip_install('-r', self.requirements, '-r', self.test_requirements)
-
- def parse_args(self, argv):
- """Parses command-line arguments."""
- parser = optparse.OptionParser()
- parser.add_option('-n', '--no-site-packages',
- action='store_true',
- help="Do not inherit packages from global Python "
- "install.")
- return parser.parse_args(argv[1:])[0]
-
-
-class Distro(InstallVenv):
-
- def check_cmd(self, cmd):
- return bool(self.run_command(['which', cmd],
- check_exit_code=False).strip())
-
- def install_virtualenv(self):
- if self.check_cmd('virtualenv'):
- return
-
- if self.check_cmd('easy_install'):
- print('Installing virtualenv via easy_install...', end=' ')
- if self.run_command(['easy_install', 'virtualenv']):
- print('Succeeded')
- return
- else:
- print('Failed')
-
- self.die('ERROR: virtualenv not found.\n\n%s development'
- ' requires virtualenv, please install it using your'
- ' favorite package management tool' % self.project)
-
-
-class Fedora(Distro):
- """This covers all Fedora-based distributions.
-
- Includes: Fedora, RHEL, CentOS, Scientific Linux
- """
-
- def check_pkg(self, pkg):
- return self.run_command_with_code(['rpm', '-q', pkg],
- check_exit_code=False)[1] == 0
-
- def install_virtualenv(self):
- if self.check_cmd('virtualenv'):
- return
-
- if not self.check_pkg('python-virtualenv'):
- self.die("Please install 'python-virtualenv'.")
-
- super(Fedora, self).install_virtualenv()
diff --git a/tox.ini b/tox.ini
index 28dfe8b..44162fd 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,23 +5,27 @@
[tempestenv]
sitepackages = False
-setenv = VIRTUAL_ENV={envdir}
- OS_TEST_PATH=./tempest/test_discover
-deps = setuptools
- -r{toxinidir}/requirements.txt
+setenv =
+ VIRTUAL_ENV={envdir}
+ OS_TEST_PATH=./tempest/test_discover
+deps =
+ setuptools
+ -r{toxinidir}/requirements.txt
[testenv]
-setenv = VIRTUAL_ENV={envdir}
- OS_TEST_PATH=./tempest/tests
+setenv =
+ VIRTUAL_ENV={envdir}
+ OS_TEST_PATH=./tempest/tests
passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH OS_TEST_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
usedevelop = True
install_command = pip install -U {opts} {packages}
whitelist_externals = *
-deps = -r{toxinidir}/requirements.txt
- -r{toxinidir}/test-requirements.txt
+deps =
+ -r{toxinidir}/requirements.txt
+ -r{toxinidir}/test-requirements.txt
commands =
- find . -type f -name "*.pyc" -delete
- bash tools/pretty_tox.sh '{posargs}'
+ find . -type f -name "*.pyc" -delete
+ bash tools/pretty_tox.sh '{posargs}'
[testenv:genconfig]
commands = oslo-config-generator --config-file etc/config-generator.tempest.conf
@@ -31,54 +35,72 @@
commands = python setup.py testr --coverage --testr-arg='tempest\.tests {posargs}'
[testenv:all]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
# 'all' includes slow tests
-setenv = {[tempestenv]setenv}
- OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
+setenv =
+ {[tempestenv]setenv}
+ OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
deps = {[tempestenv]deps}
commands =
- find . -type f -name "*.pyc" -delete
- bash tools/pretty_tox.sh '{posargs}'
+ find . -type f -name "*.pyc" -delete
+ bash tools/pretty_tox.sh '{posargs}'
+
+[testenv:ostestr]
+sitepackages = {[tempestenv]sitepackages}
+# 'all' includes slow tests
+setenv =
+ {[tempestenv]setenv}
+ OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
+deps = {[tempestenv]deps}
+commands =
+ find . -type f -name "*.pyc" -delete
+ ostestr {posargs}
[testenv:all-plugin]
sitepackages = True
# 'all' includes slow tests
-setenv = {[tempestenv]setenv}
- OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
+setenv =
+ {[tempestenv]setenv}
+ OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
deps = {[tempestenv]deps}
commands =
- find . -type f -name "*.pyc" -delete
- bash tools/pretty_tox.sh '{posargs}'
+ find . -type f -name "*.pyc" -delete
+ bash tools/pretty_tox.sh '{posargs}'
[testenv:full]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
# The regex below is used to select which tests to run and exclude the slow tag:
# See the testrepository bug: https://bugs.launchpad.net/testrepository/+bug/1208610
commands =
- find . -type f -name "*.pyc" -delete
- bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
+ find . -type f -name "*.pyc" -delete
+ bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
[testenv:full-serial]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
# The regex below is used to select which tests to run and exclude the slow tag:
# See the testrepository bug: https://bugs.launchpad.net/testrepository/+bug/1208610
commands =
- find . -type f -name "*.pyc" -delete
- bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
+ find . -type f -name "*.pyc" -delete
+ bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
[testenv:smoke]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
commands =
- find . -type f -name "*.pyc" -delete
- bash tools/pretty_tox.sh '\[.*\bsmoke\b.*\] {posargs}'
+ find . -type f -name "*.pyc" -delete
+ bash tools/pretty_tox.sh '\[.*\bsmoke\b.*\] {posargs}'
[testenv:smoke-serial]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
@@ -86,10 +108,11 @@
# https://bugs.launchpad.net/tempest/+bug/1216076 so the neutron smoke
# job would fail if we moved it to parallel.
commands =
- find . -type f -name "*.pyc" -delete
- bash tools/pretty_tox_serial.sh '\[.*\bsmoke\b.*\] {posargs}'
+ find . -type f -name "*.pyc" -delete
+ bash tools/pretty_tox_serial.sh '\[.*\bsmoke\b.*\] {posargs}'
[testenv:stress]
+envdir = .tox/tempest
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}
deps = {[tempestenv]deps}
@@ -99,18 +122,25 @@
[testenv:venv]
commands = {posargs}
+[testenv:venv-tempest]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+commands = {posargs}
+
[testenv:docs]
commands =
- python setup.py build_sphinx {posargs}
+ python setup.py build_sphinx {posargs}
[testenv:pep8]
commands =
- flake8 {posargs}
- check-uuid
+ flake8 {posargs}
+ check-uuid
[testenv:uuidgen]
commands =
- check-uuid --fix
+ check-uuid --fix
[hacking]
local-check-factory = tempest.hacking.checks.factory