Merge "Ensure test for Neutron GET / uses right URL"
diff --git a/HACKING.rst b/HACKING.rst
index dbb758b..e5f45ac 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -2,7 +2,7 @@
====================
- Step 1: Read the OpenStack Style Commandments
- http://docs.openstack.org/developer/hacking/
+ https://docs.openstack.org/hacking/latest/
- Step 2: Read on
Tempest Specific Commandments
@@ -102,20 +102,20 @@
Service Tagging
---------------
Service tagging is used to specify which services are exercised by a particular
-test method. You specify the services with the tempest.test.services decorator.
-For example:
+test method. You specify the services with the ``tempest.test.services``
+decorator. For example:
@services('compute', 'image')
Valid service tag names are the same as the list of directories in tempest.api
that have tests.
-For scenario tests having a service tag is required. For the api tests service
-tags are only needed if the test method makes an api call (either directly or
+For scenario tests having a service tag is required. For the API tests service
+tags are only needed if the test method makes an API call (either directly or
indirectly through another service) that differs from the parent directory
-name. For example, any test that make an api call to a service other than nova
-in tempest.api.compute would require a service tag for those services, however
-they do not need to be tagged as compute.
+name. For example, any test that make an API call to a service other than Nova
+in ``tempest.api.compute`` would require a service tag for those services,
+however they do not need to be tagged as ``compute``.
Test fixtures and resources
---------------------------
@@ -198,7 +198,7 @@
Test skips because of Known Bugs
--------------------------------
If a test is broken because of a bug it is appropriate to skip the test until
-bug has been fixed. You should use the skip_because decorator so that
+bug has been fixed. You should use the ``skip_because`` decorator so that
Tempest's skip tracking tool can watch the bug status.
Example::
@@ -229,7 +229,7 @@
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
+ parallelization in Tempest is at the test class level, but if there is a json
and xml version of the same test class there could still be a race between
methods.
@@ -238,8 +238,8 @@
avoided to prevent resource conflicts.
- If the execution of a set of tests is required to be serialized then locking
- can be used to perform this. See AggregatesAdminTest in
- tempest.api.compute.admin for an example of using locking.
+ can be used to perform this. See usage of ``LockFixture`` for examples of
+ using locking.
Sample Configuration File
-------------------------
@@ -251,7 +251,7 @@
Unit Tests
----------
-Unit tests are a separate class of tests in tempest. They verify tempest
+Unit tests are a separate class of tests in Tempest. They verify Tempest
itself, and thus have a different set of guidelines around them:
1. They can not require anything running externally. All you should need to
@@ -321,8 +321,8 @@
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 installed you run the tool against Tempest by calling from the
-tempest repo::
+Tempest installed you run the tool against Tempest by calling from the
+Tempest repo::
check-uuid
@@ -337,7 +337,7 @@
check-uuid --fix
-The ``check-uuid`` tool is used as part of the tempest gate job
+The ``check-uuid`` tool is used as part of the Tempest gate job
to ensure that all tests have an ``idempotent_id`` decorator.
Branchless Tempest Considerations
@@ -350,7 +350,7 @@
proposed commits to Tempest must work against both the master and all the
currently supported stable branches of the projects. As such there are a few
special considerations that have to be accounted for when pushing new changes
-to tempest.
+to Tempest.
1. New Tests for new features
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -370,14 +370,22 @@
When trying to land a bug fix which changes a tested API you'll have to use the
following procedure::
- - Propose change to the project, get a +2 on the change even with failing
- - Propose skip on Tempest which will only be approved after the
+ 1. Propose change to the project, get a +2 on the change even with failing
+ 2. Propose skip on Tempest which will only be approved after the
corresponding change in the project has a +2 on change
- - Land project change in master and all open stable branches (if required)
- - Land changed test in Tempest
+ 3. Land project change in master and all open stable branches (if required)
+ 4. Land changed test in Tempest
Otherwise the bug fix won't be able to land in the project.
+Handily, `Zuul’s cross-repository dependencies
+<https://docs.openstack.org/infra/zuul/gating.html#cross-repository-dependencies>`_.
+can be leveraged to do without step 2 and to have steps 3 and 4 happen
+"atomically". To do that, make the patch written in step 1 to depend (refer to
+Zuul's documentation above) on the patch written in step 4. The commit message
+for the Tempest change should have a link to the Gerrit review that justifies
+that change.
+
3. New Tests for existing features
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/README.rst b/README.rst
index ac93992..2e13fec 100644
--- a/README.rst
+++ b/README.rst
@@ -11,7 +11,7 @@
==============================================
The documentation for Tempest is officially hosted at:
-http://docs.openstack.org/developer/tempest/
+https://docs.openstack.org/tempest/latest/
This is a set of integration tests to be run against a live OpenStack
cluster. Tempest has batteries of tests for OpenStack API validation,
@@ -23,7 +23,7 @@
Tempest Design Principles that we strive to live by.
- Tempest should be able to run against any OpenStack cloud, be it a
- one node devstack install, a 20 node lxc cloud, or a 1000 node kvm
+ one node DevStack install, a 20 node LXC cloud, or a 1000 node KVM
cloud.
- Tempest should be explicit in testing features. It is easy to auto
discover features of a cloud incorrectly, and give people an
@@ -65,13 +65,13 @@
$ pip install tempest/
This can be done within a venv, but the assumption for this guide is that
- the Tempest cli entry point will be in your shell's PATH.
+ the Tempest CLI entry point will be in your shell's PATH.
-#. Installing Tempest may create a /etc/tempest dir, however if one isn't
- created you can create one or use ~/.tempest/etc or ~/.config/tempest in
- place of /etc/tempest. If none of these dirs are created tempest will create
- ~/.tempest/etc when it's needed. The contents of this dir will always
- automatically be copied to all etc/ dirs in local workspaces as an initial
+#. Installing Tempest may create a ``/etc/tempest dir``, however if one isn't
+ created you can create one or use ``~/.tempest/etc`` or ``~/.config/tempest`` in
+ place of ``/etc/tempest``. If none of these dirs are created Tempest will create
+ ``~/.tempest/etc`` when it's needed. The contents of this dir will always
+ automatically be copied to all ``etc/`` dirs in local workspaces as an initial
setup step. So if there is any common configuration you'd like to be shared
between local Tempest workspaces it's recommended that you pre-populate it
before running ``tempest init``.
@@ -90,12 +90,12 @@
is that you'll create a new working directory for each to maintain separate
configuration files and local artifact storage for each.
-#. Then cd into the newly created working dir and also modify the local
- config files located in the etc/ subdir created by the ``tempest init``
- command. Tempest is expecting a tempest.conf file in etc/ so if only a
+#. Then ``cd`` into the newly created working dir and also modify the local
+ config files located in the ``etc/`` subdir created by the ``tempest init``
+ command. Tempest is expecting a ``tempest.conf`` file in etc/ so if only a
sample exists you must rename or copy it to tempest.conf before making
any changes to it otherwise Tempest will not know how to load it. For
- details on configuring tempest refer to the :ref:`tempest-configuration`.
+ details on configuring Tempest refer to the :ref:`tempest-configuration`.
#. Once the configuration is done you're now ready to run Tempest. This can
be done using the :ref:`tempest_run` command. This can be done by either
@@ -117,15 +117,15 @@
will run the same set of tests as the default gate jobs.
.. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
-.. _ostestr: http://docs.openstack.org/developer/os-testr/
+.. _ostestr: https://docs.openstack.org/os-testr/latest/
Library
-------
Tempest exposes a library interface. This interface is a stable interface and
should be backwards compatible (including backwards compatibility with the
old tempest-lib package, with the exception of the import). If you plan to
-directly consume tempest in your project you should only import code from the
-tempest library interface, other pieces of tempest do not have the same
+directly consume Tempest in your project you should only import code from the
+Tempest library interface, other pieces of Tempest do not have the same
stable interface and there are no guarantees on the Python API unless otherwise
stated.
@@ -137,7 +137,7 @@
shows what changes have been released on each version.
Tempest's released versions are broken into 2 sets of information. Depending on
-how you intend to consume tempest you might need
+how you intend to consume Tempest you might need
The version is a set of 3 numbers:
@@ -146,12 +146,12 @@
While this is almost `semver`_ like, the way versioning is handled is slightly
different:
-X is used to represent the supported OpenStack releases for tempest tests
-in-tree, and to signify major feature changes to tempest. It's a monotonically
+X is used to represent the supported OpenStack releases for Tempest tests
+in-tree, and to signify major feature changes to Tempest. It's a monotonically
increasing integer where each version either indicates a new supported OpenStack
release, the drop of support for an OpenStack release (which will coincide with
the upstream stable branch going EOL), or a major feature lands (or is removed)
-from tempest.
+from Tempest.
Y.Z is used to represent library interface changes. This is treated the same
way as minor and patch versions from `semver`_ but only for the library
@@ -166,16 +166,16 @@
Detailed configuration of Tempest is beyond the scope of this
document see :ref:`tempest-configuration` for more details on configuring
-Tempest. The etc/tempest.conf.sample attempts to be a self-documenting version
-of the configuration.
+Tempest. The ``etc/tempest.conf.sample`` attempts to be a self-documenting
+version of the configuration.
You can generate a new sample tempest.conf file, run the following
command from the top level of the Tempest directory::
$ tox -e genconfig
-The most important pieces that are needed are the user ids, openstack
-endpoint, and basic flavors and images needed to run tests.
+The most important pieces that are needed are the user ids, OpenStack
+endpoints, and basic flavors and images needed to run tests.
Unit Tests
----------
@@ -190,13 +190,13 @@
is OS_TEST_PATH=./tempest/test_discover which will only run test discover on the
Tempest suite.
-Alternatively, there are the py27 and py34 tox jobs which will run the unit
+Alternatively, there are the py27 and py35 tox jobs which will run the unit
tests with the corresponding version of python.
Python 2.6
----------
-Starting in the kilo release the OpenStack services dropped all support for
+Starting in the Kilo release the OpenStack services dropped all support for
python 2.6. This change has been mirrored in Tempest, starting after the
tempest-2 tag. This means that proposed changes to Tempest which only fix
python 2.6 compatibility will be rejected, and moving forward more features not
@@ -208,8 +208,8 @@
Python 3.x
----------
-Starting during the Pike cycle Tempest has a gating CI job that runs tempest
-with Python 3. Any tempest release after 15.0.0 should fully support running
+Starting during the Pike cycle Tempest has a gating CI job that runs Tempest
+with Python 3. Any Tempest release after 15.0.0 should fully support running
under Python 3 as well as Python 2.7.
Legacy run method
@@ -239,10 +239,10 @@
.. note::
- If you have a running devstack environment, Tempest will be
+ If you have a running DevStack environment, Tempest will be
automatically configured and placed in ``/opt/stack/tempest``. It
will have a configuration file already set up to work with your
- devstack installation.
+ DevStack installation.
Tempest is not tied to any single test runner, but `testr`_ is the most commonly
used tool. Also, the nosetests test runner is **not** recommended to run Tempest.
diff --git a/REVIEWING.rst b/REVIEWING.rst
index 9b272bb..7d28320 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -21,6 +21,15 @@
to a Gerrit review.
+Execution time
+--------------
+While checking in the job logs that a new test is actually executed, also
+pay attention to the execution time of that test. Keep in mind that each test
+is going to be executed hundreds of time each day, because Tempest tests
+run in many OpenStack projects. It's worth considering how important/critical
+the feature under test is with how costly the new test is.
+
+
Unit Tests
----------
@@ -48,6 +57,17 @@
abstract the duplicated code into a function or method.
+Tests overlap
+-------------
+When a new test is being proposed, question whether this feature is not already
+tested with Tempest. Tempest has more than 1200 tests, spread amongst many
+directories, so it's easy to introduce test duplication. For example, testing
+volume attachment to a server could be a compute test or a volume test, depending
+on how you see it. So one must look carefully in the entire code base for possible
+overlap. As a rule of thumb, the older a feature is, the more likely it's
+already tested.
+
+
Being explicit
--------------
When tests are being added that depend on a configurable feature or extension,
@@ -60,8 +80,8 @@
Configuration Options
---------------------
-With the introduction of the tempest external test plugin interface we needed
-to provide a stable contract for tempest's configuration options. This means
+With the introduction of the Tempest external test plugin interface we needed
+to provide a stable contract for Tempest's configuration options. This means
we can no longer simply remove a configuration option when it's no longer used.
Patches proposed that remove options without a deprecation cycle should not
be approved. Similarly when changing default values with configuration we need
@@ -91,7 +111,7 @@
anything backwards incompatible or would require a user to take note or do
something extra.
-.. _reno: http://docs.openstack.org/developer/reno/
+.. _reno: https://docs.openstack.org/reno/latest/
Deprecated Code
---------------
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 23f732e..201d387 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -36,7 +36,8 @@
subprocess.call(['tools/generate-tempest-plugins-list.sh'], cwd=root_dir)
def setup(app):
- app.connect('builder-inited', build_plugin_registry)
+ if os.getenv('GENERATE_TEMPEST_PLUGIN_LIST', 'true').lower() == 'true':
+ app.connect('builder-inited', build_plugin_registry)
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 4accd94..8f2865a 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -28,7 +28,7 @@
- Run tests for admin APIs
- Generate test credentials on the fly (see `Dynamic Credentials`_)
-When keystone uses a policy that requires domain scoped tokens for admin
+When Keystone uses a policy that requires domain scoped tokens for admin
actions, the flag ``admin_domain_scope`` must be set to ``True``.
The admin user configured, if any, must have a role assigned to the domain to
be usable.
@@ -39,7 +39,7 @@
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.
+the ``accounts.yaml.sample`` file included in Tempest.
Keystone Connection Info
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -47,18 +47,17 @@
to provide it with information about how it communicates with keystone.
This involves configuring the following options in the ``identity`` section:
- #. ``auth_version``
- #. ``uri``
- #. ``uri_v3``
+ - ``auth_version``
+ - ``uri``
+ - ``uri_v3``
The ``auth_version`` option is used to tell Tempest whether it should be using
-keystone's v2 or v3 api for communicating with keystone. The two uri options are
+Keystone's v2 or v3 api for communicating with Keystone. The two uri options are
used to tell Tempest the url of the keystone endpoint. The ``uri`` option is
-used for keystone v2 request and ``uri_v3`` is used for keystone v3. You want to
+used for Keystone v2 request and ``uri_v3`` is used for Keystone v3. You want to
ensure that which ever version you set for ``auth_version`` has its uri option
defined.
-
Credential Provider Mechanisms
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -95,7 +94,7 @@
When the ``admin_domain_scope`` option is set to ``True``, provisioned admin
accounts will be assigned a role on domain configured in
``default_credentials_domain_name``. This will make the accounts provisioned
-usable in a cloud where domain scoped tokens are required by keystone for
+usable in a cloud where domain scoped tokens are required by Keystone for
admin operations. Note that the initial pre-provision admin accounts,
configured in tempest.conf, must have a role on the same domain as well, for
Dynamic Credentials to work.
@@ -140,10 +139,10 @@
tenants it's using are empty and may sporadically fail if there are unexpected
resources present.
-When the keystone in the target cloud requires domain scoped tokens to
+When the Keystone in the target cloud requires domain scoped tokens to
perform admin actions, all pre-provisioned admin users must have a role
assigned on the domain where test accounts a provisioned.
-The option ``admin_domain_scope`` is used to tell tempest that domain scoped
+The option ``admin_domain_scope`` is used to tell Tempest that domain scoped
tokens shall be used. ``default_credentials_domain_name`` is the domain where
test accounts are expected to be provisioned if no domain is specified.
@@ -167,7 +166,7 @@
#. ``flavor_ref_alt``
Both of these options are in the ``compute`` section of the config file and take
-in the flavor id (not the name) from nova. The ``flavor_ref`` option is what
+in the flavor id (not the name) from Nova. The ``flavor_ref`` option is what
will be used for booting almost all of the guests; ``flavor_ref_alt`` is only
used in tests where two different-sized servers are required (for example, a
resize test).
@@ -185,7 +184,7 @@
#. ``image_ref``
#. ``image_ref_alt``
-Both options are expecting an image id (not name) from nova. The ``image_ref``
+Both options are expecting an image id (not name) from Nova. The ``image_ref``
option is what will be used for booting the majority of servers in Tempest.
``image_ref_alt`` is used for tests that require two images such as rebuild. If
two images are not available you can set both options to the same image id and
@@ -224,7 +223,7 @@
Networking
----------
OpenStack has a myriad of different networking configurations possible and
-depending on which of the two network backends, nova-network or neutron, you are
+depending on which of the two network backends, nova-network or Neutron, you are
using things can vary drastically. Due to this complexity Tempest has to provide
a certain level of flexibility in its configuration to ensure it will work
against any cloud. This ends up causing a large number of permutations in
@@ -302,21 +301,21 @@
server with multiple networks. If this is not the case for your cloud then using
an accounts file is recommended because it provides the necessary flexibility to
describe your configuration. Dynamic credentials is not able to dynamically
-allocate things as necessary if neutron is not enabled.
+allocate things as necessary if Neutron is not enabled.
-With neutron and dynamic credentials enabled there should not be any additional
+With Neutron and dynamic credentials enabled there should not be any additional
configuration necessary to enable Tempest to create servers with working
networking, assuming you have properly configured the ``network`` section to
-work for your cloud. Tempest will dynamically create the neutron resources
+work for your cloud. Tempest will dynamically create the Neutron resources
necessary to enable using servers with that network. Also, just as with the
-accounts file, if you specify a fixed network name while using neutron and
+accounts file, if you specify a fixed network name while using Neutron and
dynamic credentials it will enable running tests which require a static network
and it will additionally be used as a fallback for server creation. However,
unlike accounts.yaml this should never be triggered.
However, there is an option ``create_isolated_networks`` to disable dynamic
credentials's automatic provisioning of network resources. If this option is set
-to False you will have to either rely on there only being a single/default
+to ``False`` you will have to either rely on there only being a single/default
network available for the server creation, or use ``fixed_network_name`` to
inform Tempest which network to use.
@@ -336,16 +335,16 @@
The ``run_validation`` is used to enable or disable ssh connectivity for
all tests (with the exception of scenario tests which do not have a flag for
-enabling or disabling ssh) To enable ssh connectivity this needs be set to ``true``.
+enabling or disabling ssh) To enable ssh connectivity this needs be set to ``True``.
-The ``connect_method`` option is used to tell tempest what kind of IP to use for
+The ``connect_method`` option is used to tell Tempest what kind of IP to use for
establishing a connection to the server. Two methods are available: ``fixed``
and ``floating``, the later being set by default. If this is set to floating
-tempest will create a floating ip for the server before attempted to connect
+Tempest will create a floating ip for the server before attempted to connect
to it. The IP for the floating ip is what is used for the connection.
For the ``auth_method`` option there is currently, only one valid option,
-``keypair``. With this set to ``keypair`` tempest will create an ssh keypair
+``keypair``. With this set to ``keypair`` Tempest will create an ssh keypair
and use that for authenticating against the created server.
Configuring Available Services
@@ -359,8 +358,8 @@
The ``service_available`` section of the config file is used to set which
services are available. It contains a boolean option for each service (except
-for keystone which is a hard requirement) set it to True if the service is
-available or False if it is not.
+for Keystone which is a hard requirement) set it to ``True`` if the service is
+available or ``False`` if it is not.
Service Catalog
^^^^^^^^^^^^^^^
@@ -382,17 +381,18 @@
service you can set the ``region`` option in that service's section.
It should also be noted that the default values for these options are set
-to what devstack uses (which is a de facto standard for service catalog
+to what DevStack uses (which is a de facto standard for service catalog
entries). So often nothing actually needs to be set on these options to enable
communication to a particular service. It is only if you are either not using
-the same ``catalog_type`` as devstack or you want Tempest to talk to a different
-endpoint type instead of publicURL for a service that these need to be changed.
+the same ``catalog_type`` as DevStack or you want Tempest to talk to a different
+endpoint type instead of ``publicURL`` for a service that these need to be
+changed.
.. note::
Tempest does not serve all kinds of fancy URLs in the service catalog. The
service catalog should be in a standard format (which is going to be
- standardized at the keystone level).
+ standardized at the Keystone level).
Tempest expects URLs in the Service catalog in the following format:
* ``http://example.com:1234/<version-info>``
@@ -420,7 +420,7 @@
configured as to which optional features are enabled. This is in order to
prevent bugs in the discovery mechanisms from masking failures.
-The service feature-enabled config sections are how Tempest addresses the
+The service ``feature-enabled`` config sections are how Tempest addresses the
optional feature question. Each service that has tests for optional features
contains one of these sections. The only options in it are boolean options
with the name of a feature which is used. If it is set to false any test which
@@ -431,7 +431,7 @@
API Extensions
^^^^^^^^^^^^^^
The service feature-enabled sections often contain an ``api-extensions`` option
-(or in the case of swift a ``discoverable_apis`` option). This is used to tell
+(or in the case of Swift a ``discoverable_apis`` option). This is used to tell
Tempest which api extensions (or configurable middleware) is used in your
deployment. It has two valid config states: either it contains a single value
``all`` (which is the default) which means that every api extension is assumed
diff --git a/doc/source/library.rst b/doc/source/library.rst
index 29248d1..a461a0f 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -68,3 +68,4 @@
library/api_microversion_testing
library/auth
library/clients
+ library/credential_providers
diff --git a/doc/source/library/credential_providers.rst b/doc/source/library/credential_providers.rst
new file mode 100644
index 0000000..7e831cc
--- /dev/null
+++ b/doc/source/library/credential_providers.rst
@@ -0,0 +1,148 @@
+.. _cred_providers:
+
+Credential Providers
+====================
+
+These library interfaces are used to deal with allocating credentials on demand
+either dynamically by calling keystone to allocate new credentials, or from
+a list of preprovisioned credentials. These 2 modules are implementations of
+the same abstract credential providers class and can be used interchangably.
+However, each implementation has some additional parameters that are used to
+influence the behavior of the modules. The API reference at the bottom of this
+doc shows the interface definitions for both modules, however that may be a bit
+opaque. You can see some examples of how to leverage this interface below.
+
+Initialization Example
+----------------------
+This example is from Tempest itself (from tempest/common/credentials_factory.py
+just modified slightly) and is how it initializes the credential provider based
+on config::
+
+ from tempest import config
+ from tempest.lib.common import dynamic_creds
+ from tempest.lib.common import preprov_creds
+
+ CONF = config.CONF
+
+ def get_credentials_provider(name, network_resources=None,
+ force_tenant_isolation=False,
+ identity_version=None):
+ # If a test requires a new account to work, it can have it via forcing
+ # dynamic credentials. A new account will be produced only for that test.
+ # In case admin credentials are not available for the account creation,
+ # the test should be skipped else it would fail.
+ identity_version = identity_version or CONF.identity.auth_version
+ if CONF.auth.use_dynamic_credentials or force_tenant_isolation:
+ admin_creds = get_configured_admin_credentials(
+ fill_in=True, identity_version=identity_version)
+ return dynamic_creds.DynamicCredentialProvider(
+ name=name,
+ network_resources=network_resources,
+ identity_version=identity_version,
+ admin_creds=admin_creds,
+ identity_admin_domain_scope=CONF.identity.admin_domain_scope,
+ identity_admin_role=CONF.identity.admin_role,
+ extra_roles=CONF.auth.tempest_roles,
+ neutron_available=CONF.service_available.neutron,
+ project_network_cidr=CONF.network.project_network_cidr,
+ project_network_mask_bits=CONF.network.project_network_mask_bits,
+ public_network_id=CONF.network.public_network_id,
+ create_networks=(CONF.auth.create_isolated_networks and not
+ CONF.network.shared_physical_network),
+ resource_prefix=CONF.resources_prefix,
+ credentials_domain=CONF.auth.default_credentials_domain_name,
+ admin_role=CONF.identity.admin_role,
+ identity_uri=CONF.identity.uri_v3,
+ identity_admin_endpoint_type=CONF.identity.v3_endpoint_type)
+ else:
+ if CONF.auth.test_accounts_file:
+ # Most params are not relevant for pre-created accounts
+ return preprov_creds.PreProvisionedCredentialProvider(
+ name=name, identity_version=identity_version,
+ accounts_lock_dir=lockutils.get_lock_path(CONF),
+ test_accounts_file=CONF.auth.test_accounts_file,
+ object_storage_operator_role=CONF.object_storage.operator_role,
+ object_storage_reseller_admin_role=reseller_admin_role,
+ credentials_domain=CONF.auth.default_credentials_domain_name,
+ admin_role=CONF.identity.admin_role,
+ identity_uri=CONF.identity.uri_v3,
+ identity_admin_endpoint_type=CONF.identity.v3_endpoint_type)
+ else:
+ raise exceptions.InvalidConfiguration(
+ 'A valid credential provider is needed')
+
+This function just returns an initialized credential provider class based on the
+config file. The consumer of this function treats the output as the same
+regardless of whether it's a dynamic or preprovisioned provider object.
+
+Dealing with Credentials
+------------------------
+
+Once you have a credential provider object created the access patterns for
+allocating and removing credentials are the same across both the dynamic
+and preprovisioned credentials. These are defined in the abstract
+CredentialProvider class. At a high level the credentials provider enables
+you to get 3 basic types of credentials at once (per object): a primary, alt,
+and admin. You're also able to allocate a credential by role. These credentials
+are tracked by the provider object and delete must manually be called otherwise
+the created resources will not be deleted (or returned to the pool in the case
+of preprovisioned creds)
+
+Examples
+''''''''
+
+Continuing from the example above, to allocate credentials by the 3 basic types
+you can do the following::
+
+ provider = get_credentials_provider('my_tests')
+ primary_creds = provider.get_primary_creds()
+ alt_creds = provider.get_alt_creds()
+ admin_creds = provider.get_admin_creds()
+ # Make sure to delete the credentials when you're finished
+ provider.clear_creds()
+
+To create and interact with credentials by role you can do the following::
+
+ provider = get_credentials_provider('my_tests')
+ my_role_creds = provider.get_creds_by_role({'roles': ['my_role']})
+ # provider.clear_creds() will clear all creds including those allocated by
+ # role
+ provider.clear_creds()
+
+When multiple roles are specified a set of creds with all the roles assigned
+will be allocated::
+
+ provider = get_credentials_provider('my_tests')
+ my_role_creds = provider.get_creds_by_role({'roles': ['my_role',
+ 'my_other_role']})
+ # provider.clear_creds() will clear all creds including those allocated by
+ # role
+ provider.clear_creds()
+
+If you need multiple sets of credentials with the same roles you can also do
+this by leveraging the ``force_new`` kwarg::
+
+ provider = get_credentials_provider('my_tests')
+ my_role_creds = provider.get_creds_by_role({'roles': ['my_role']})
+ my_role_other_creds = provider.get_creds_by_role({'roles': ['my_role']},
+ force_new=True)
+ # provider.clear_creds() will clear all creds including those allocated by
+ # role
+ provider.clear_creds()
+
+API Reference
+=============
+
+------------------------------
+The dynamic credentials module
+------------------------------
+
+.. automodule:: tempest.lib.common.dynamic_creds
+ :members:
+
+--------------------------------------
+The pre-provisioned credentials module
+--------------------------------------
+
+.. automodule:: tempest.lib.common.preprov_creds
+ :members:
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index d6d90ba..60f4f36 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -111,7 +111,7 @@
This document explains how to implement Microversion tests using those
interfaces.
-.. _API Microversion testing Framework: http://docs.openstack.org/developer/tempest/library/api_microversion_testing.html
+.. _API Microversion testing Framework: https://docs.openstack.org/tempest/latest/library/api_microversion_testing.html
Step1: Add skip logic based on configured Microversion range
@@ -296,46 +296,46 @@
* `2.1`_
- .. _2.1: http://docs.openstack.org/developer/nova/api_microversion_history.html#id1
+ .. _2.1: https://docs.openstack.org/nova/latest/api_microversion_history.html#id1
* `2.2`_
- .. _2.2: http://docs.openstack.org/developer/nova/api_microversion_history.html#id2
+ .. _2.2: http://docs.openstack.org/nova/latest/api_microversion_history.html#id2
* `2.10`_
- .. _2.10: http://docs.openstack.org/developer/nova/api_microversion_history.html#id9
+ .. _2.10: http://docs.openstack.org/nova/latest/api_microversion_history.html#id9
* `2.20`_
- .. _2.20: http://docs.openstack.org/developer/nova/api_microversion_history.html#id18
+ .. _2.20: http://docs.openstack.org/nova/latest/api_microversion_history.html#id18
* `2.25`_
- .. _2.25: http://docs.openstack.org/developer/nova/api_microversion_history.html#maximum-in-mitaka
+ .. _2.25: http://docs.openstack.org/nova/latest/api_microversion_history.html#maximum-in-mitaka
* `2.32`_
- .. _2.32: http://docs.openstack.org/developer/nova/api_microversion_history.html#id29
+ .. _2.32: http://docs.openstack.org/nova/latest/api_microversion_history.html#id29
* `2.37`_
- .. _2.37: http://docs.openstack.org/developer/nova/api_microversion_history.html#id34
+ .. _2.37: http://docs.openstack.org/nova/latest/api_microversion_history.html#id34
* `2.42`_
- .. _2.42: http://docs.openstack.org/developer/nova/api_microversion_history.html#maximum-in-ocata
+ .. _2.42: http://docs.openstack.org/nova/latest/api_microversion_history.html#maximum-in-ocata
* `2.47`_
- .. _2.47: http://docs.openstack.org/developer/nova/api_microversion_history.html#id42
+ .. _2.47: http://docs.openstack.org/nova/latest/api_microversion_history.html#id42
* `2.48`_
- .. _2.48: http://docs.openstack.org/developer/nova/api_microversion_history.html#id43
+ .. _2.48: http://docs.openstack.org/nova/latest/api_microversion_history.html#id43
* Volume
* `3.3`_
- .. _3.3: https://docs.openstack.org/developer/cinder/devref/api_microversion_history.html#id4
+ .. _3.3: https://docs.openstack.org/cinder/latest/devref/api_microversion_history.html#id4
diff --git a/doc/source/test_removal.rst b/doc/source/test_removal.rst
index d06e4ba..07c3046 100644
--- a/doc/source/test_removal.rst
+++ b/doc/source/test_removal.rst
@@ -171,7 +171,7 @@
`tempest plugin mechanism`_
to maintain continuity after migrating the tests out of tempest.
-.. _tempest plugin mechanism: http://docs.openstack.org/developer/tempest/plugin.html
+.. _tempest plugin mechanism: https://docs.openstack.org/tempest/latest/plugin.html
API Compatibility
"""""""""""""""""
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
index 4e3bfa2..aec55e9 100644
--- a/doc/source/write_tests.rst
+++ b/doc/source/write_tests.rst
@@ -6,13 +6,13 @@
This guide serves as a starting point for developers working on writing new
Tempest tests. At a high level tests in Tempest are just tests that conform to
the standard python `unit test`_ framework. But there are several aspects of
-that are unique to tempest and it's role as an integration test suite running
+that are unique to Tempest and its role as an integration test suite running
against a real cloud.
.. _unit test: https://docs.python.org/3.6/library/unittest.html
-.. note:: This guide is for writing tests in the tempest repository. While many
- parts of this guide are also applicable to tempest plugins, not all
+.. note:: This guide is for writing tests in the Tempest repository. While many
+ parts of this guide are also applicable to Tempest plugins, not all
the APIs mentioned are considered stable or recommended for use in
plugins. Please refer to :ref:`tempest_plugin` for details about
writing plugins
@@ -24,8 +24,8 @@
The base unit of testing in Tempest is the `TestCase`_ (also called the test
class). Each TestCase contains test methods which are the individual tests that
will be executed by the test runner. But, the TestCase is the smallest self
-contained unit for tests from the tempest perspective. It's also the level at
-which tempest is parallel safe. In other words, multiple TestCases can be
+contained unit for tests from the Tempest perspective. It's also the level at
+which Tempest is parallel safe. In other words, multiple TestCases can be
executed in parallel, but individual test methods in the same TestCase can not.
Also, all test methods within a TestCase are assumed to be executed serially. As
such you can use the test case to store variables that are shared between
@@ -151,7 +151,7 @@
You can also specify credentials with specific roles assigned. This is useful
for cases where there are specific RBAC requirements hard coded into an API.
The canonical example of this are swift tests which often want to test swift's
-concepts of operator and reseller_admin. An actual example from tempest on how
+concepts of operator and reseller_admin. An actual example from Tempest on how
to do this is::
class PublicObjectTest(base.BaseObjectTest):
@@ -187,7 +187,7 @@
| [$label, $role] | cls.os_roles_$label |
+-------------------+---------------------+
-By default cls.os_primary is available since it is allocated in the base tempest test
+By default cls.os_primary is available since it is allocated in the base Tempest test
class (located in tempest/test.py). If your TestCase inherits from a different
direct parent class (it'll still inherit from the BaseTestCase, just not
directly) be sure to check if that class overrides allocated credentials.
@@ -195,8 +195,8 @@
Dealing with Network Allocation
'''''''''''''''''''''''''''''''
-When neutron is enabled and a testing requires networking this isn't normally
-automatically setup when a tenant is created. Since tempest needs isolated
+When Neutron is enabled and a testing requires networking this isn't normally
+automatically setup when a tenant is created. Since Tempest needs isolated
tenants to function properly it also needs to handle network allocation. By
default the base test class will allocate a network, subnet, and router
automatically (this depends on the configured credential provider, for more
diff --git a/releasenotes/notes/10.0-supported-openstack-releases-b88db468695348f6.yaml b/releasenotes/notes/10/10.0-supported-openstack-releases-b88db468695348f6.yaml
similarity index 100%
rename from releasenotes/notes/10.0-supported-openstack-releases-b88db468695348f6.yaml
rename to releasenotes/notes/10/10.0-supported-openstack-releases-b88db468695348f6.yaml
diff --git a/releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml b/releasenotes/notes/10/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
similarity index 75%
rename from releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
rename to releasenotes/notes/10/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
index 0ed3130..c1edd63 100644
--- a/releasenotes/notes/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
+++ b/releasenotes/notes/10/10.0.0-Tempest-library-interface-0eb680b810139a50.yaml
@@ -5,7 +5,7 @@
it lives directly in the tempest project. For more information refer to
the `library docs`_.
- .. _library docs: http://docs.openstack.org/developer/tempest/library.html#library
+ .. _library docs: https://docs.openstack.org/tempest/latest/library.html#current-library-apis
features:
- Tempest library interface
diff --git a/releasenotes/notes/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml b/releasenotes/notes/10/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
similarity index 100%
rename from releasenotes/notes/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
rename to releasenotes/notes/10/10.0.0-start-using-reno-ed9518126fd0e1a3.yaml
diff --git a/releasenotes/notes/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml b/releasenotes/notes/11/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml
similarity index 100%
rename from releasenotes/notes/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml
rename to releasenotes/notes/11/11.0.0-api-microversion-testing-support-2ceddd2255670932.yaml
diff --git a/releasenotes/notes/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml b/releasenotes/notes/11/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
similarity index 100%
rename from releasenotes/notes/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
rename to releasenotes/notes/11/11.0.0-compute-microversion-support-e0b23f960f894b9b.yaml
diff --git a/releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml b/releasenotes/notes/11/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
similarity index 100%
rename from releasenotes/notes/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
rename to releasenotes/notes/11/11.0.0-supported-openstack-releases-1e5d7295d939d439.yaml
diff --git a/releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml b/releasenotes/notes/12/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
similarity index 100%
rename from releasenotes/notes/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
rename to releasenotes/notes/12/12.0.0-supported-openstack-releases-f10aac381d933dd1.yaml
diff --git a/releasenotes/notes/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml b/releasenotes/notes/12/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
rename to releasenotes/notes/12/12.1.0-add-network-versions-client-d90e8334e1443f5c.yaml
diff --git a/releasenotes/notes/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml b/releasenotes/notes/12/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
rename to releasenotes/notes/12/12.1.0-add-scope-to-auth-b5a82493ea89f41e.yaml
diff --git a/releasenotes/notes/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml b/releasenotes/notes/12/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
rename to releasenotes/notes/12/12.1.0-add-tempest-run-3d0aaf69c2ca4115.yaml
diff --git a/releasenotes/notes/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml b/releasenotes/notes/12/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
rename to releasenotes/notes/12/12.1.0-add-tempest-workspaces-228a2ba4690b5589.yaml
diff --git a/releasenotes/notes/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml b/releasenotes/notes/12/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
rename to releasenotes/notes/12/12.1.0-add_subunit_describe_calls-5498a37e6cd66c4b.yaml
diff --git a/releasenotes/notes/12.1.0-bug-1486834-7ebca15836ae27a9.yaml b/releasenotes/notes/12/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
rename to releasenotes/notes/12/12.1.0-bug-1486834-7ebca15836ae27a9.yaml
diff --git a/releasenotes/notes/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml b/releasenotes/notes/12/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
rename to releasenotes/notes/12/12.1.0-identity-clients-as-library-e663c6132fcac6c2.yaml
diff --git a/releasenotes/notes/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml b/releasenotes/notes/12/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
rename to releasenotes/notes/12/12.1.0-image-clients-as-library-86d17caa26ce3961.yaml
diff --git a/releasenotes/notes/12.1.0-new-test-utils-module-adf34468c4d52719.yaml b/releasenotes/notes/12/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
rename to releasenotes/notes/12/12.1.0-new-test-utils-module-adf34468c4d52719.yaml
diff --git a/releasenotes/notes/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml b/releasenotes/notes/12/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
rename to releasenotes/notes/12/12.1.0-remove-input-scenarios-functionality-01308e6d4307f580.yaml
diff --git a/releasenotes/notes/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml b/releasenotes/notes/12/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
rename to releasenotes/notes/12/12.1.0-remove-integrated-horizon-bb57551c1e5f5be3.yaml
diff --git a/releasenotes/notes/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml b/releasenotes/notes/12/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
rename to releasenotes/notes/12/12.1.0-remove-legacy-credential-providers-3d653ac3ba1ada2b.yaml
diff --git a/releasenotes/notes/12.1.0-remove-trove-tests-666522e9113549f9.yaml b/releasenotes/notes/12/12.1.0-remove-trove-tests-666522e9113549f9.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-remove-trove-tests-666522e9113549f9.yaml
rename to releasenotes/notes/12/12.1.0-remove-trove-tests-666522e9113549f9.yaml
diff --git a/releasenotes/notes/12.1.0-routers-client-as-library-25a363379da351f6.yaml b/releasenotes/notes/12/12.1.0-routers-client-as-library-25a363379da351f6.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-routers-client-as-library-25a363379da351f6.yaml
rename to releasenotes/notes/12/12.1.0-routers-client-as-library-25a363379da351f6.yaml
diff --git a/releasenotes/notes/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml b/releasenotes/notes/12/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
rename to releasenotes/notes/12/12.1.0-support-chunked-encoding-d71f53225f68edf3.yaml
diff --git a/releasenotes/notes/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml b/releasenotes/notes/12/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
similarity index 100%
rename from releasenotes/notes/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
rename to releasenotes/notes/12/12.1.0-tempest-init-global-config-dir-location-changes-12260255871d3a2b.yaml
diff --git a/releasenotes/notes/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml b/releasenotes/notes/12/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
rename to releasenotes/notes/12/12.2.0-add-httptimeout-in-restclient-ax78061900e3f3d7.yaml
diff --git a/releasenotes/notes/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml b/releasenotes/notes/12/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml
rename to releasenotes/notes/12/12.2.0-add-new-identity-clients-3c3afd674a395bde.yaml
diff --git a/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml b/releasenotes/notes/12/12.2.0-clients_module-16f3025f515bf9ec.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml
rename to releasenotes/notes/12/12.2.0-clients_module-16f3025f515bf9ec.yaml
diff --git a/releasenotes/notes/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml b/releasenotes/notes/12/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml
rename to releasenotes/notes/12/12.2.0-nova_cert_default-90eb7c1e3cde624a.yaml
diff --git a/releasenotes/notes/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml b/releasenotes/notes/12/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml
rename to releasenotes/notes/12/12.2.0-plugin-service-client-registration-00b19a2dd4935ba0.yaml
diff --git a/releasenotes/notes/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml b/releasenotes/notes/12/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml
rename to releasenotes/notes/12/12.2.0-remove-javelin-276f62d04f7e4a1d.yaml
diff --git a/releasenotes/notes/12.2.0-service_client_config-8a1d7b4de769c633.yaml b/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-service_client_config-8a1d7b4de769c633.yaml
rename to releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
diff --git a/releasenotes/notes/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml b/releasenotes/notes/12/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml
similarity index 100%
rename from releasenotes/notes/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml
rename to releasenotes/notes/12/12.2.0-volume-clients-as-library-9a3444dd63c134b3.yaml
diff --git a/releasenotes/notes/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml b/releasenotes/notes/13/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml
rename to releasenotes/notes/13/13.0.0-add-new-identity-clients-as-library-5f7ndha733nwdsn9.yaml
diff --git a/releasenotes/notes/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml b/releasenotes/notes/13/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
rename to releasenotes/notes/13/13.0.0-add-volume-clients-as-a-library-d05b6bc35e66c6ef.yaml
diff --git a/releasenotes/notes/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml b/releasenotes/notes/13/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
rename to releasenotes/notes/13/13.0.0-deprecate-get_ipv6_addr_by_EUI64-4673f07677289cf6.yaml
diff --git a/releasenotes/notes/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml b/releasenotes/notes/13/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
rename to releasenotes/notes/13/13.0.0-move-call-until-true-to-tempest-lib-c9ea70dd6fe9bd15.yaml
diff --git a/releasenotes/notes/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml b/releasenotes/notes/13/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml
rename to releasenotes/notes/13/13.0.0-start-of-newton-support-3ebb274f300f28eb.yaml
diff --git a/releasenotes/notes/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml b/releasenotes/notes/13/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
rename to releasenotes/notes/13/13.0.0-tempest-cleanup-nostandalone-39df2aafb2545d35.yaml
diff --git a/releasenotes/notes/13.0.0-volume-clients-as-library-660811011be29d1a.yaml b/releasenotes/notes/13/13.0.0-volume-clients-as-library-660811011be29d1a.yaml
similarity index 100%
rename from releasenotes/notes/13.0.0-volume-clients-as-library-660811011be29d1a.yaml
rename to releasenotes/notes/13/13.0.0-volume-clients-as-library-660811011be29d1a.yaml
diff --git a/releasenotes/notes/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml b/releasenotes/notes/14/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml
rename to releasenotes/notes/14/14.0.0-add-cred-provider-abstract-class-to-lib-70ff513221f8a871.yaml
diff --git a/releasenotes/notes/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml b/releasenotes/notes/14/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
rename to releasenotes/notes/14/14.0.0-add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
diff --git a/releasenotes/notes/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml b/releasenotes/notes/14/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml
rename to releasenotes/notes/14/14.0.0-add-error-code-translation-to-versions-clients-acbc78292e24b014.yaml
diff --git a/releasenotes/notes/14.0.0-add-image-clients-af94564fb34ddca6.yaml b/releasenotes/notes/14/14.0.0-add-image-clients-af94564fb34ddca6.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-image-clients-af94564fb34ddca6.yaml
rename to releasenotes/notes/14/14.0.0-add-image-clients-af94564fb34ddca6.yaml
diff --git a/releasenotes/notes/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml b/releasenotes/notes/14/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
rename to releasenotes/notes/14/14.0.0-add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
diff --git a/releasenotes/notes/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml b/releasenotes/notes/14/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml
rename to releasenotes/notes/14/14.0.0-add-service-provider-client-cbba77d424a30dd3.yaml
diff --git a/releasenotes/notes/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml b/releasenotes/notes/14/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml
rename to releasenotes/notes/14/14.0.0-add-ssh-port-parameter-to-client-6d16c374ac4456c1.yaml
diff --git a/releasenotes/notes/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml b/releasenotes/notes/14/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml
rename to releasenotes/notes/14/14.0.0-deprecate-nova-api-extensions-df16b02485dae203.yaml
diff --git a/releasenotes/notes/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml b/releasenotes/notes/14/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
rename to releasenotes/notes/14/14.0.0-move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
diff --git a/releasenotes/notes/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml b/releasenotes/notes/14/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml
rename to releasenotes/notes/14/14.0.0-new-volume-limit-client-517c17d9090f4df4.yaml
diff --git a/releasenotes/notes/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml b/releasenotes/notes/14/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml
rename to releasenotes/notes/14/14.0.0-remo-stress-tests-81052b211ad95d2e.yaml
diff --git a/releasenotes/notes/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml b/releasenotes/notes/14/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml
rename to releasenotes/notes/14/14.0.0-remove-baremetal-tests-65186d9e15d5b8fb.yaml
diff --git a/releasenotes/notes/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml b/releasenotes/notes/14/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml
rename to releasenotes/notes/14/14.0.0-remove-bootable-option-024f8944c056a3e0.yaml
diff --git a/releasenotes/notes/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml b/releasenotes/notes/14/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml
rename to releasenotes/notes/14/14.0.0-remove-negative-test-generator-1653f4c0f86ccf75.yaml
diff --git a/releasenotes/notes/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml b/releasenotes/notes/14/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml
rename to releasenotes/notes/14/14.0.0-remove-sahara-tests-1532c47c7df80e3a.yaml
diff --git a/releasenotes/notes/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml b/releasenotes/notes/14/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml
similarity index 100%
rename from releasenotes/notes/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml
rename to releasenotes/notes/14/14.0.0-volume-clients-as-library-309030c7a16e62ab.yaml
diff --git a/releasenotes/notes/15.0.0-add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml b/releasenotes/notes/15/15.0.0-add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml
rename to releasenotes/notes/15/15.0.0-add-identity-v3-clients-as-a-library-d34b4fdf376984ad.yaml
diff --git a/releasenotes/notes/15.0.0-add-image-clients-tests-49dbc0a0a4281a77.yaml b/releasenotes/notes/15/15.0.0-add-image-clients-tests-49dbc0a0a4281a77.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-add-image-clients-tests-49dbc0a0a4281a77.yaml
rename to releasenotes/notes/15/15.0.0-add-image-clients-tests-49dbc0a0a4281a77.yaml
diff --git a/releasenotes/notes/15.0.0-add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml b/releasenotes/notes/15/15.0.0-add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml
rename to releasenotes/notes/15/15.0.0-add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml
diff --git a/releasenotes/notes/15.0.0-add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml b/releasenotes/notes/15/15.0.0-add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
rename to releasenotes/notes/15/15.0.0-add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
diff --git a/releasenotes/notes/15.0.0-deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml b/releasenotes/notes/15/15.0.0-deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml
rename to releasenotes/notes/15/15.0.0-deprecate-allow_port_security_disabled-option-2d3d87f6bd11d03a.yaml
diff --git a/releasenotes/notes/15.0.0-deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml b/releasenotes/notes/15/15.0.0-deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
rename to releasenotes/notes/15/15.0.0-deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
diff --git a/releasenotes/notes/15.0.0-deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml b/releasenotes/notes/15/15.0.0-deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
rename to releasenotes/notes/15/15.0.0-deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
diff --git a/releasenotes/notes/15.0.0-jsonschema-validator-2377ba131e12d3c7.yaml b/releasenotes/notes/15/15.0.0-jsonschema-validator-2377ba131e12d3c7.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-jsonschema-validator-2377ba131e12d3c7.yaml
rename to releasenotes/notes/15/15.0.0-jsonschema-validator-2377ba131e12d3c7.yaml
diff --git a/releasenotes/notes/15.0.0-remove-deprecated-compute-microversion-config-options-eaee6a7d2f8390a8.yaml b/releasenotes/notes/15/15.0.0-remove-deprecated-compute-microversion-config-options-eaee6a7d2f8390a8.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-remove-deprecated-compute-microversion-config-options-eaee6a7d2f8390a8.yaml
rename to releasenotes/notes/15/15.0.0-remove-deprecated-compute-microversion-config-options-eaee6a7d2f8390a8.yaml
diff --git a/releasenotes/notes/15.0.0-remove-deprecated-compute-validation-config-options-e3d1b89ce074d71c.yaml b/releasenotes/notes/15/15.0.0-remove-deprecated-compute-validation-config-options-e3d1b89ce074d71c.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-remove-deprecated-compute-validation-config-options-e3d1b89ce074d71c.yaml
rename to releasenotes/notes/15/15.0.0-remove-deprecated-compute-validation-config-options-e3d1b89ce074d71c.yaml
diff --git a/releasenotes/notes/15.0.0-remove-deprecated-input-scenario-config-options-414e0c5442e967e9.yaml b/releasenotes/notes/15/15.0.0-remove-deprecated-input-scenario-config-options-414e0c5442e967e9.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-remove-deprecated-input-scenario-config-options-414e0c5442e967e9.yaml
rename to releasenotes/notes/15/15.0.0-remove-deprecated-input-scenario-config-options-414e0c5442e967e9.yaml
diff --git a/releasenotes/notes/15.0.0-remove-deprecated-network-config-options-f9ce276231578fe6.yaml b/releasenotes/notes/15/15.0.0-remove-deprecated-network-config-options-f9ce276231578fe6.yaml
similarity index 100%
rename from releasenotes/notes/15.0.0-remove-deprecated-network-config-options-f9ce276231578fe6.yaml
rename to releasenotes/notes/15/15.0.0-remove-deprecated-network-config-options-f9ce276231578fe6.yaml
diff --git a/releasenotes/notes/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml b/releasenotes/notes/16/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
rename to releasenotes/notes/16/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
diff --git a/releasenotes/notes/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml b/releasenotes/notes/16/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
rename to releasenotes/notes/16/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
diff --git a/releasenotes/notes/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml b/releasenotes/notes/16/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
rename to releasenotes/notes/16/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
diff --git a/releasenotes/notes/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml b/releasenotes/notes/16/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
rename to releasenotes/notes/16/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
diff --git a/releasenotes/notes/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml b/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
rename to releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
diff --git a/releasenotes/notes/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml b/releasenotes/notes/16/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml
rename to releasenotes/notes/16/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml
diff --git a/releasenotes/notes/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml b/releasenotes/notes/16/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
rename to releasenotes/notes/16/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
diff --git a/releasenotes/notes/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml b/releasenotes/notes/16/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
rename to releasenotes/notes/16/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
diff --git a/releasenotes/notes/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml b/releasenotes/notes/16/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml
rename to releasenotes/notes/16/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml
diff --git a/releasenotes/notes/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml b/releasenotes/notes/16/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
rename to releasenotes/notes/16/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
diff --git a/releasenotes/notes/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml b/releasenotes/notes/16/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
rename to releasenotes/notes/16/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
diff --git a/releasenotes/notes/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml b/releasenotes/notes/16/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
rename to releasenotes/notes/16/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
diff --git a/releasenotes/notes/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml b/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
rename to releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
diff --git a/releasenotes/notes/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml b/releasenotes/notes/16/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
rename to releasenotes/notes/16/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
diff --git a/releasenotes/notes/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml b/releasenotes/notes/16/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
rename to releasenotes/notes/16/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
diff --git a/releasenotes/notes/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml b/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
rename to releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
diff --git a/releasenotes/notes/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml b/releasenotes/notes/16/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml
rename to releasenotes/notes/16/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml
diff --git a/releasenotes/notes/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml b/releasenotes/notes/16/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
rename to releasenotes/notes/16/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
diff --git a/releasenotes/notes/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml b/releasenotes/notes/16/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
rename to releasenotes/notes/16/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
diff --git a/releasenotes/notes/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml b/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
rename to releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
diff --git a/releasenotes/notes/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml b/releasenotes/notes/16/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
rename to releasenotes/notes/16/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
diff --git a/releasenotes/notes/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml b/releasenotes/notes/16/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
rename to releasenotes/notes/16/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
diff --git a/releasenotes/notes/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml b/releasenotes/notes/16/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
rename to releasenotes/notes/16/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
diff --git a/releasenotes/notes/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml b/releasenotes/notes/16/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
rename to releasenotes/notes/16/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
diff --git a/releasenotes/notes/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml b/releasenotes/notes/16/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
rename to releasenotes/notes/16/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
diff --git a/releasenotes/notes/16.0.0-mitaka-eol-88ff8355fff81b55.yaml b/releasenotes/notes/16/16.0.0-mitaka-eol-88ff8355fff81b55.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-mitaka-eol-88ff8355fff81b55.yaml
rename to releasenotes/notes/16/16.0.0-mitaka-eol-88ff8355fff81b55.yaml
diff --git a/releasenotes/notes/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml b/releasenotes/notes/16/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml
rename to releasenotes/notes/16/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml
diff --git a/releasenotes/notes/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml b/releasenotes/notes/16/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
rename to releasenotes/notes/16/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
rename to releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
rename to releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
rename to releasenotes/notes/16/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
rename to releasenotes/notes/16/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
diff --git a/releasenotes/notes/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml b/releasenotes/notes/16/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml
rename to releasenotes/notes/16/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml
diff --git a/releasenotes/notes/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml b/releasenotes/notes/16/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
rename to releasenotes/notes/16/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
diff --git a/releasenotes/notes/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml b/releasenotes/notes/16/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml
rename to releasenotes/notes/16/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml
diff --git a/releasenotes/notes/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml b/releasenotes/notes/16/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
similarity index 100%
rename from releasenotes/notes/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
rename to releasenotes/notes/16/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
diff --git a/releasenotes/notes/add-volume-group-types-tempest-tests-1298ab8cb4fe8b7b.yaml b/releasenotes/notes/add-volume-group-types-tempest-tests-1298ab8cb4fe8b7b.yaml
new file mode 100644
index 0000000..4fd3bee
--- /dev/null
+++ b/releasenotes/notes/add-volume-group-types-tempest-tests-1298ab8cb4fe8b7b.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add list_group_type and show_group_type in the group_types client for
+ the volume service. Add tests for create/delete/show/list group types.
diff --git a/releasenotes/notes/extra-compute-services-tests-92b6c0618972e02f.yaml b/releasenotes/notes/extra-compute-services-tests-92b6c0618972e02f.yaml
new file mode 100644
index 0000000..414adf1
--- /dev/null
+++ b/releasenotes/notes/extra-compute-services-tests-92b6c0618972e02f.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add the ``disable_log_reason`` and the ``update_forced_down`` API endpoints
+ to the compute ``services_client``.
+ Add '2.11' compute validation schema for compute services API.
diff --git a/releasenotes/notes/migrate-dynamic-creds-ecebb47528080761.yaml b/releasenotes/notes/migrate-dynamic-creds-ecebb47528080761.yaml
new file mode 100644
index 0000000..c20cbc6
--- /dev/null
+++ b/releasenotes/notes/migrate-dynamic-creds-ecebb47528080761.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ The tempest module tempest.common.dynamic creds which is used for
+ dynamically allocating credentials has been migrated into tempest lib.
diff --git a/releasenotes/notes/migrate-preprov-creds-ef61a046ee1ec604.yaml b/releasenotes/notes/migrate-preprov-creds-ef61a046ee1ec604.yaml
new file mode 100644
index 0000000..aa5f71a
--- /dev/null
+++ b/releasenotes/notes/migrate-preprov-creds-ef61a046ee1ec604.yaml
@@ -0,0 +1,11 @@
+---
+features:
+ - The tempest module tempest.common.preprov_creds which is used to provide
+ credentials from a list of preprovisioned resources has been migrated into
+ tempest lib at tempest.lib.common.preprov_creds.
+ - The InvalidTestResource exception class from tempest.exceptions has been
+ migrated into tempest.lib.exceptions
+ - The tempest module tempest.common.fixed_network which provided utilities for
+ finding fixed networks by and helpers for picking the network to use when
+ multiple tenant networks are available has been migrated into tempest lib
+ at tempest.lib.common.fixed_network.
diff --git a/releasenotes/notes/tempest-identity-catalog-client-f5c8589a9d7c1eb5.yaml b/releasenotes/notes/tempest-identity-catalog-client-f5c8589a9d7c1eb5.yaml
new file mode 100644
index 0000000..dcaaceb
--- /dev/null
+++ b/releasenotes/notes/tempest-identity-catalog-client-f5c8589a9d7c1eb5.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - Add a new identity catalog client. At this point, the new client
+ contains a single functionality, "show_catalog", which returns a
+ catalog object.
diff --git a/requirements.txt b/requirements.txt
index 259a4cf..a74f5c2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr!=2.1.0,>=2.0.0 # Apache-2.0
-cliff>=2.6.0 # Apache-2.0
+cliff>=2.8.0 # Apache-2.0
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
testtools>=1.4.0 # MIT
paramiko>=2.0 # LGPLv2.1+
@@ -11,7 +11,7 @@
oslo.concurrency>=3.8.0 # Apache-2.0
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0
-oslo.serialization>=1.10.0 # Apache-2.0
+oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
six>=1.9.0 # MIT
fixtures>=3.0.0 # Apache-2.0/BSD
diff --git a/setup.cfg b/setup.cfg
index b292970..f52137e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
-home-page = http://docs.openstack.org/developer/tempest/
+home-page = https://docs.openstack.org/tempest/latest/
classifier =
Intended Audience :: Information Technology
Intended Audience :: System Administrators
diff --git a/tempest/README.rst b/tempest/README.rst
index 0feec41..663653e 100644
--- a/tempest/README.rst
+++ b/tempest/README.rst
@@ -9,12 +9,13 @@
OpenStack clouds.
As such Tempest tests come in many flavors, each with their own rules
-and guidelines. Below is the proposed Havana restructuring for Tempest
+and guidelines. Below is the overview of the Tempest respository structure
to make this clear.
| tempest/
| api/ - API tests
| scenario/ - complex scenario tests
+| tests/ - unit tests for Tempest internals
Each of these directories contains different types of tests. What
belongs in each directory, the rules and examples for good tests, are
@@ -24,8 +25,8 @@
----------------------
API tests are validation tests for the OpenStack API. They should not
-use the existing python clients for OpenStack, but should instead use
-the tempest implementations of clients. Having raw clients let us
+use the existing Python clients for OpenStack, but should instead use
+the Tempest implementations of clients. Having raw clients let us
pass invalid JSON to the APIs and see the results, something we could
not get with the native clients.
@@ -41,14 +42,14 @@
functionality. They are typically a series of steps where complicated
state requiring multiple services is set up exercised, and torn down.
-Scenario tests should not use the existing python clients for OpenStack,
-but should instead use the tempest implementations of clients.
+Scenario tests should not use the existing Python clients for OpenStack,
+but should instead use the Tempest implementations of clients.
:ref:`unit_tests_field_guide`
-----------------------------
Unit tests are the self checks for Tempest. They provide functional
-verification and regression checking for the internal components of tempest.
-They should be used to just verify that the individual pieces of tempest are
+verification and regression checking for the internal components of Tempest.
+They should be used to just verify that the individual pieces of Tempest are
working as expected.
diff --git a/tempest/api/README.rst b/tempest/api/README.rst
index 91e6ad6..a796922 100644
--- a/tempest/api/README.rst
+++ b/tempest/api/README.rst
@@ -13,7 +13,8 @@
It's also important to test not only the expected positive path on
APIs, but also to provide them with invalid data to ensure they fail
-in expected and documented ways. Over the course of the OpenStack
+in expected and documented ways. The latter type of tests is called
+``negative tests`` in Tempest source code. Over the course of the OpenStack
project Tempest has discovered many fundamental bugs by doing just
this.
@@ -22,7 +23,7 @@
spinning up a server, image, etc, then operating on it.
-Why are these tests in tempest?
+Why are these tests in Tempest?
-------------------------------
This is one of the core missions for the Tempest project, and where it
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 705e567..3f1bdce 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -48,7 +48,6 @@
@classmethod
def setup_clients(cls):
super(LiveMigrationTest, cls).setup_clients()
- cls.admin_hosts_client = cls.os_admin.hosts_client
cls.admin_migration_client = cls.os_admin.migrations_client
def _migrate_server_to(self, server_id, dest_host, volume_backed=False):
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 0521cca..d9a7800 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -14,8 +14,8 @@
from tempest.api.compute import base
from tempest.common import compute
-from tempest.common import fixed_network
from tempest.common import waiters
+from tempest.lib.common import fixed_network
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 9023759..3656770 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -32,7 +32,6 @@
def setup_clients(cls):
super(ServersAdminNegativeTestJSON, cls).setup_clients()
cls.client = cls.os_admin.servers_client
- cls.non_adm_client = cls.servers_client
cls.quotas_client = cls.os_admin.quotas_client
@classmethod
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index ffadd96..aa5c43d 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -28,6 +28,7 @@
class ServersTestJSON(base.BaseV2ComputeTest):
disk_config = 'AUTO'
+ volume_backed = False
@classmethod
def setup_credentials(cls):
@@ -38,8 +39,6 @@
def setup_clients(cls):
super(ServersTestJSON, cls).setup_clients()
cls.client = cls.servers_client
- cls.networks_client = cls.os_primary.networks_client
- cls.subnets_client = cls.os_primary.subnets_client
@classmethod
def resource_setup(cls):
@@ -59,24 +58,11 @@
accessIPv4=cls.accessIPv4,
accessIPv6=cls.accessIPv6,
disk_config=disk_config,
- adminPass=cls.password)
+ adminPass=cls.password,
+ volume_backed=cls.volume_backed)
cls.server = (cls.client.show_server(server_initial['id'])
['server'])
- def _create_net_subnet_ret_net_from_cidr(self, cidr):
- name_net = data_utils.rand_name(self.__class__.__name__)
- net = self.networks_client.create_network(name=name_net)
- self.addCleanup(self.networks_client.delete_network,
- net['network']['id'])
-
- subnet = self.subnets_client.create_subnet(
- network_id=net['network']['id'],
- cidr=cidr,
- ip_version=4)
- self.addCleanup(self.subnets_client.delete_subnet,
- subnet['subnet']['id'])
- return net
-
@decorators.attr(type='smoke')
@decorators.idempotent_id('5de47127-9977-400a-936f-abcfbec1218f')
def test_verify_server_details(self):
@@ -87,7 +73,11 @@
self.assertEqual(self.server['accessIPv6'],
str(netaddr.IPAddress(self.accessIPv6)))
self.assertEqual(self.name, self.server['name'])
- self.assertEqual(self.image_ref, self.server['image']['id'])
+ if self.volume_backed:
+ # Image is an empty string as per documentation
+ self.assertEqual("", self.server['image'])
+ else:
+ self.assertEqual(self.image_ref, self.server['image']['id'])
self.assertEqual(self.flavor_ref, self.server['flavor']['id'])
self.assertEqual(self.meta, self.server['metadata'])
@@ -158,73 +148,6 @@
['server_group'])
self.assertIn(server['id'], server_group['members'])
- @decorators.idempotent_id('0578d144-ed74-43f8-8e57-ab10dbf9b3c2')
- @testtools.skipUnless(CONF.service_available.neutron,
- 'Neutron service must be available.')
- def test_verify_multiple_nics_order(self):
- # Verify that the networks order given at the server creation is
- # preserved within the server.
- net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
- net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
-
- networks = [{'uuid': net1['network']['id']},
- {'uuid': net2['network']['id']}]
-
- server_multi_nics = self.create_test_server(
- networks=networks, wait_until='ACTIVE')
-
- # Cleanup server; this is needed in the test case because with the LIFO
- # nature of the cleanups, if we don't delete the server first, the port
- # will still be part of the subnet and we'll get a 409 from Neutron
- # when trying to delete the subnet. The tear down in the base class
- # will try to delete the server and get a 404 but it's ignored so
- # we're OK.
- self.addCleanup(self.delete_server, server_multi_nics['id'])
-
- addresses = (self.client.list_addresses(server_multi_nics['id'])
- ['addresses'])
-
- # We can't predict the ip addresses assigned to the server on networks.
- # Sometimes the assigned addresses are ['19.80.0.2', '19.86.0.2'], at
- # other times ['19.80.0.3', '19.86.0.3']. So we check if the first
- # address is in first network, similarly second address is in second
- # network.
- addr = [addresses[net1['network']['name']][0]['addr'],
- addresses[net2['network']['name']][0]['addr']]
- networks = [netaddr.IPNetwork('19.80.0.0/24'),
- netaddr.IPNetwork('19.86.0.0/24')]
- for address, network in zip(addr, networks):
- self.assertIn(address, network)
-
- @decorators.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
- @testtools.skipUnless(CONF.service_available.neutron,
- 'Neutron service must be available.')
- def test_verify_duplicate_network_nics(self):
- # Verify that server creation does not fail when more than one nic
- # is created on the same network.
- net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
- net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
-
- networks = [{'uuid': net1['network']['id']},
- {'uuid': net2['network']['id']},
- {'uuid': net1['network']['id']}]
-
- server_multi_nics = self.create_test_server(
- networks=networks, wait_until='ACTIVE')
- self.addCleanup(self.delete_server, server_multi_nics['id'])
-
- addresses = (self.client.list_addresses(server_multi_nics['id'])
- ['addresses'])
-
- addr = [addresses[net1['network']['name']][0]['addr'],
- addresses[net2['network']['name']][0]['addr'],
- addresses[net1['network']['name']][1]['addr']]
- networks = [netaddr.IPNetwork('19.80.0.0/24'),
- netaddr.IPNetwork('19.86.0.0/24'),
- netaddr.IPNetwork('19.80.0.0/24')]
- for address, network in zip(addr, networks):
- self.assertIn(address, network)
-
class ServersTestManualDisk(ServersTestJSON):
disk_config = 'MANUAL'
@@ -235,3 +158,15 @@
if not CONF.compute_feature_enabled.disk_config:
msg = "DiskConfig extension not enabled."
raise cls.skipException(msg)
+
+
+class ServersTestBootFromVolume(ServersTestJSON):
+ """Run the `ServersTestJSON` tests with a volume backed VM"""
+ volume_backed = True
+
+ @classmethod
+ def skip_checks(cls):
+ super(ServersTestBootFromVolume, cls).skip_checks()
+ if not test.get_service_list()['volume']:
+ msg = "Volume service not enabled."
+ raise cls.skipException(msg)
diff --git a/tempest/api/compute/servers/test_create_server_multi_nic.py b/tempest/api/compute/servers/test_create_server_multi_nic.py
new file mode 100644
index 0000000..3447d85
--- /dev/null
+++ b/tempest/api/compute/servers/test_create_server_multi_nic.py
@@ -0,0 +1,120 @@
+# 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 netaddr
+import testtools
+
+from tempest.api.compute import base
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class ServersTestMultiNic(base.BaseV2ComputeTest):
+
+ @classmethod
+ def setup_credentials(cls):
+ cls.prepare_instance_network()
+ super(ServersTestMultiNic, cls).setup_credentials()
+
+ @classmethod
+ def setup_clients(cls):
+ super(ServersTestMultiNic, cls).setup_clients()
+ cls.client = cls.servers_client
+ cls.networks_client = cls.os_primary.networks_client
+ cls.subnets_client = cls.os_primary.subnets_client
+
+ def _create_net_subnet_ret_net_from_cidr(self, cidr):
+ name_net = data_utils.rand_name(self.__class__.__name__)
+ net = self.networks_client.create_network(name=name_net)
+ self.addCleanup(self.networks_client.delete_network,
+ net['network']['id'])
+
+ subnet = self.subnets_client.create_subnet(
+ network_id=net['network']['id'],
+ cidr=cidr,
+ ip_version=4)
+ self.addCleanup(self.subnets_client.delete_subnet,
+ subnet['subnet']['id'])
+ return net
+
+ @decorators.idempotent_id('0578d144-ed74-43f8-8e57-ab10dbf9b3c2')
+ @testtools.skipUnless(CONF.service_available.neutron,
+ 'Neutron service must be available.')
+ def test_verify_multiple_nics_order(self):
+ # Verify that the networks order given at the server creation is
+ # preserved within the server.
+ net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
+ net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
+
+ networks = [{'uuid': net1['network']['id']},
+ {'uuid': net2['network']['id']}]
+
+ server_multi_nics = self.create_test_server(
+ networks=networks, wait_until='ACTIVE')
+
+ # Cleanup server; this is needed in the test case because with the LIFO
+ # nature of the cleanups, if we don't delete the server first, the port
+ # will still be part of the subnet and we'll get a 409 from Neutron
+ # when trying to delete the subnet. The tear down in the base class
+ # will try to delete the server and get a 404 but it's ignored so
+ # we're OK.
+ self.addCleanup(self.delete_server, server_multi_nics['id'])
+
+ addresses = (self.client.list_addresses(server_multi_nics['id'])
+ ['addresses'])
+
+ # We can't predict the ip addresses assigned to the server on networks.
+ # Sometimes the assigned addresses are ['19.80.0.2', '19.86.0.2'], at
+ # other times ['19.80.0.3', '19.86.0.3']. So we check if the first
+ # address is in first network, similarly second address is in second
+ # network.
+ addr = [addresses[net1['network']['name']][0]['addr'],
+ addresses[net2['network']['name']][0]['addr']]
+ networks = [netaddr.IPNetwork('19.80.0.0/24'),
+ netaddr.IPNetwork('19.86.0.0/24')]
+ for address, network in zip(addr, networks):
+ self.assertIn(address, network)
+
+ @decorators.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
+ @testtools.skipUnless(CONF.service_available.neutron,
+ 'Neutron service must be available.')
+ def test_verify_duplicate_network_nics(self):
+ # Verify that server creation does not fail when more than one nic
+ # is created on the same network.
+ net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
+ net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
+
+ networks = [{'uuid': net1['network']['id']},
+ {'uuid': net2['network']['id']},
+ {'uuid': net1['network']['id']}]
+
+ server_multi_nics = self.create_test_server(
+ networks=networks, wait_until='ACTIVE')
+ self.addCleanup(self.delete_server, server_multi_nics['id'])
+
+ addresses = (self.client.list_addresses(server_multi_nics['id'])
+ ['addresses'])
+
+ addr = [addresses[net1['network']['name']][0]['addr'],
+ addresses[net2['network']['name']][0]['addr'],
+ addresses[net1['network']['name']][1]['addr']]
+ networks = [netaddr.IPNetwork('19.80.0.0/24'),
+ netaddr.IPNetwork('19.86.0.0/24'),
+ netaddr.IPNetwork('19.80.0.0/24')]
+ for address, network in zip(addr, networks):
+ self.assertIn(address, network)
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 921b7da..a4ed8e1 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -15,9 +15,9 @@
import testtools
from tempest.api.compute import base
-from tempest.common import fixed_network
from tempest.common import waiters
from tempest import config
+from tempest.lib.common import fixed_network
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/identity/admin/v3/test_endpoint_groups.py b/tempest/api/identity/admin/v3/test_endpoint_groups.py
index 5cd456c..49dbba1 100644
--- a/tempest/api/identity/admin/v3/test_endpoint_groups.py
+++ b/tempest/api/identity/admin/v3/test_endpoint_groups.py
@@ -54,17 +54,17 @@
super(EndPointGroupsTest, cls).resource_cleanup()
@classmethod
- def _create_service(self):
+ def _create_service(cls):
s_name = data_utils.rand_name('service')
s_type = data_utils.rand_name('type')
s_description = data_utils.rand_name('description')
service_data = (
- self.services_client.create_service(name=s_name,
- type=s_type,
- description=s_description))
+ cls.services_client.create_service(name=s_name,
+ type=s_type,
+ description=s_description))
service_id = service_data['service']['id']
- self.service_ids.append(service_id)
+ cls.service_ids.append(service_id)
return service_id
@decorators.idempotent_id('7c69e7a1-f865-402d-a2ea-44493017315a')
diff --git a/tempest/api/identity/admin/v3/test_oauth_consumers.py b/tempest/api/identity/admin/v3/test_oauth_consumers.py
index 970ead3..f06fb8f 100644
--- a/tempest/api/identity/admin/v3/test_oauth_consumers.py
+++ b/tempest/api/identity/admin/v3/test_oauth_consumers.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.identity import base
-from tempest.lib.common.utils import data_utils
+from tempest.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as exceptions
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 4495cbf..30d2a36 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -187,6 +187,7 @@
cls.non_admin_users_client = cls.os_primary.users_v3_client
cls.non_admin_token = cls.os_primary.token_v3_client
cls.non_admin_projects_client = cls.os_primary.projects_client
+ cls.non_admin_catalog_client = cls.os_primary.catalog_client
cls.non_admin_versions_client =\
cls.os_primary.identity_versions_v3_client
diff --git a/tempest/api/identity/v3/test_catalog.py b/tempest/api/identity/v3/test_catalog.py
new file mode 100755
index 0000000..deec2dc
--- /dev/null
+++ b/tempest/api/identity/v3/test_catalog.py
@@ -0,0 +1,41 @@
+# 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.identity import base
+from tempest import config
+from tempest.lib import decorators
+
+
+CONF = config.CONF
+
+
+class IdentityCatalogTest(base.BaseIdentityV3Test):
+
+ @decorators.idempotent_id('56b57ced-22b8-4127-9b8a-565dfb0207e2')
+ def test_catalog_standardization(self):
+ # http://git.openstack.org/cgit/openstack/service-types-authority
+ # /tree/service-types.yaml
+ standard_service_values = [{'name': 'keystone', 'type': 'identity'},
+ {'name': 'nova', 'type': 'compute'},
+ {'name': 'glance', 'type': 'image'},
+ {'name': 'swift', 'type': 'object-store'}]
+ # next, we need to GET the catalog using the catalog client
+ catalog = self.non_admin_catalog_client.show_catalog()['catalog']
+ # get list of the service types present in the catalog
+ catalog_services = []
+ for service in catalog:
+ catalog_services.append(service['type'])
+ for service in standard_service_values:
+ # if service enabled, check if it has a standard typevalue
+ if service['name'] == 'keystone' or\
+ getattr(CONF.service_available, service['name']):
+ self.assertIn(service['type'], catalog_services)
diff --git a/tempest/api/network/admin/test_routers.py b/tempest/api/network/admin/test_routers.py
index 07c4157..f180cda 100644
--- a/tempest/api/network/admin/test_routers.py
+++ b/tempest/api/network/admin/test_routers.py
@@ -61,13 +61,6 @@
msg = "router extension not enabled."
raise cls.skipException(msg)
- @classmethod
- def resource_setup(cls):
- super(RoutersAdminTest, cls).resource_setup()
- cls.tenant_cidr = (CONF.network.project_network_cidr
- if cls._ip_version == 4 else
- CONF.network.project_network_v6_cidr)
-
@decorators.idempotent_id('e54dd3a3-4352-4921-b09d-44369ae17397')
def test_create_router_setting_project_id(self):
# Test creating router from admin user setting project_id.
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 13614cb..11273e4 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -98,7 +98,7 @@
cls.policies = None
if CONF.object_storage_feature_enabled.discoverability:
- _, body = cls.capabilities_client.list_capabilities()
+ body = cls.capabilities_client.list_capabilities()
if 'swift' in body and 'policies' in body['swift']:
cls.policies = body['swift']['policies']
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index e765414..7c538e8 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -58,9 +58,9 @@
# upload an archived file
with open(filepath) as fh:
mydata = fh.read()
- resp, body = self.bulk_client.upload_archive(
+ resp = self.bulk_client.upload_archive(
upload_path='', data=mydata, archive_file_format='tar')
- return resp, body
+ return resp
def _check_contents_deleted(self, container_name):
param = {'format': 'txt'}
@@ -73,21 +73,20 @@
def test_extract_archive(self):
# Test bulk operation of file upload with an archived file
filepath, container_name, object_name = self._create_archive()
- resp, _ = self._upload_archive(filepath)
-
+ resp = self._upload_archive(filepath)
self.containers.append(container_name)
# When uploading an archived file with the bulk operation, the response
# does not contain 'content-length' header. This is the special case,
# therefore the existence of response headers is checked without
# custom matcher.
- self.assertIn('transfer-encoding', resp)
- self.assertIn('content-type', resp)
- self.assertIn('x-trans-id', resp)
- self.assertIn('date', resp)
+ self.assertIn('transfer-encoding', resp.response)
+ self.assertIn('content-type', resp.response)
+ self.assertIn('x-trans-id', resp.response)
+ self.assertIn('date', resp.response)
# Check only the format of common headers with custom matcher
- self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+ self.assertThat(resp.response, custom_matchers.AreAllWellFormatted())
param = {'format': 'json'}
resp, body = self.account_client.list_account_containers(param)
@@ -112,19 +111,19 @@
self._upload_archive(filepath)
data = '%s/%s\n%s' % (container_name, object_name, container_name)
- resp, _ = self.bulk_client.delete_bulk_data(data=data)
+ resp = self.bulk_client.delete_bulk_data(data=data)
# When deleting multiple files using the bulk operation, the response
# does not contain 'content-length' header. This is the special case,
# therefore the existence of response headers is checked without
# custom matcher.
- self.assertIn('transfer-encoding', resp)
- self.assertIn('content-type', resp)
- self.assertIn('x-trans-id', resp)
- self.assertIn('date', resp)
+ self.assertIn('transfer-encoding', resp.response)
+ self.assertIn('content-type', resp.response)
+ self.assertIn('x-trans-id', resp.response)
+ self.assertIn('date', resp.response)
# Check only the format of common headers with custom matcher
- self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+ self.assertThat(resp.response, custom_matchers.AreAllWellFormatted())
# Check if uploaded contents are completely deleted
self._check_contents_deleted(container_name)
@@ -138,19 +137,19 @@
data = '%s/%s\n%s' % (container_name, object_name, container_name)
- resp, _ = self.bulk_client.delete_bulk_data_with_post(data=data)
+ resp = self.bulk_client.delete_bulk_data_with_post(data=data)
# When deleting multiple files using the bulk operation, the response
# does not contain 'content-length' header. This is the special case,
# therefore the existence of response headers is checked without
# custom matcher.
- self.assertIn('transfer-encoding', resp)
- self.assertIn('content-type', resp)
- self.assertIn('x-trans-id', resp)
- self.assertIn('date', resp)
+ self.assertIn('transfer-encoding', resp.response)
+ self.assertIn('content-type', resp.response)
+ self.assertIn('x-trans-id', resp.response)
+ self.assertIn('date', resp.response)
# Check only the format of common headers with custom matcher
- self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+ self.assertThat(resp.response, custom_matchers.AreAllWellFormatted())
# Check if uploaded contents are completely deleted
self._check_contents_deleted(container_name)
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 9e62046..2fb676f 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -135,7 +135,7 @@
not CONF.object_storage_feature_enabled.discoverability,
'Discoverability function is disabled')
def test_list_extensions(self):
- resp, _ = self.capabilities_client.list_capabilities()
+ resp = self.capabilities_client.list_capabilities()
self.assertThat(resp, custom_matchers.AreAllWellFormatted())
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index a8d70c5..387b7b6 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -32,7 +32,7 @@
if CONF.object_storage_feature_enabled.discoverability:
# use /info to get default constraints
- _, body = cls.capabilities_client.list_capabilities()
+ body = cls.capabilities_client.list_capabilities()
cls.constraints = body['swift']
@decorators.attr(type=["negative"])
diff --git a/tempest/api/volume/admin/test_group_types.py b/tempest/api/volume/admin/test_group_types.py
new file mode 100644
index 0000000..0df5fbd
--- /dev/null
+++ b/tempest/api/volume/admin/test_group_types.py
@@ -0,0 +1,54 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.volume import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+
+class GroupTypesTest(base.BaseVolumeAdminTest):
+ _api_version = 3
+ min_microversion = '3.11'
+ max_microversion = 'latest'
+
+ @decorators.idempotent_id('dd71e5f9-393e-4d4f-90e9-fa1b8d278864')
+ def test_group_type_create_list_show(self):
+ # Create/list/show group type.
+ name = data_utils.rand_name(self.__class__.__name__ + '-group-type')
+ description = data_utils.rand_name("group-type-description")
+ group_specs = {"consistent_group_snapshot_enabled": "<is> False"}
+ params = {'name': name,
+ 'description': description,
+ 'group_specs': group_specs,
+ 'is_public': True}
+ body = self.create_group_type(**params)
+ self.assertIn('name', body)
+ err_msg = ("The created group_type %(var)s is not equal to the "
+ "requested %(var)s")
+ self.assertEqual(name, body['name'], err_msg % {"var": "name"})
+ self.assertEqual(description, body['description'],
+ err_msg % {"var": "description"})
+
+ group_list = (
+ self.admin_group_types_client.list_group_types()['group_types'])
+ self.assertIsInstance(group_list, list)
+ self.assertNotEmpty(group_list)
+
+ fetched_group_type = self.admin_group_types_client.show_group_type(
+ body['id'])['group_type']
+ for key in params.keys():
+ self.assertEqual(params[key], fetched_group_type[key],
+ '%s of the fetched group_type is different '
+ 'from the created group_type' % key)
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index f358d7f..754104e 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -36,7 +36,7 @@
def setup_credentials(cls):
super(BaseVolumeQuotasAdminTestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os_primary.credentials.tenant_id
- cls.alt_client = cls.os_alt.volumes_client
+ cls.alt_client = cls.os_alt.volumes_client_latest
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/volume/admin/test_volume_type_access.py b/tempest/api/volume/admin/test_volume_type_access.py
index 297ab6e..e93bcb5 100644
--- a/tempest/api/volume/admin/test_volume_type_access.py
+++ b/tempest/api/volume/admin/test_volume_type_access.py
@@ -30,7 +30,7 @@
@classmethod
def setup_clients(cls):
super(VolumeTypesAccessTest, cls).setup_clients()
- cls.alt_client = cls.os_alt.volumes_client
+ cls.alt_client = cls.os_alt.volumes_client_latest
@decorators.idempotent_id('d4dd0027-835f-4554-a6e5-50903fb79184')
def test_volume_type_access_add(self):
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
index 4c13375..927bfa5 100644
--- a/tempest/api/volume/test_volumes_clone.py
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -30,6 +30,18 @@
if not CONF.volume_feature_enabled.clone:
raise cls.skipException("Cinder volume clones are disabled")
+ def _verify_volume_clone(self, source_volume, cloned_volume,
+ bootable='false', extra_size=0):
+
+ cloned_vol_details = self.volumes_client.show_volume(
+ cloned_volume['id'])['volume']
+
+ self.assertEqual(source_volume['id'],
+ cloned_vol_details['source_volid'])
+ self.assertEqual(source_volume['size'] + extra_size,
+ cloned_vol_details['size'])
+ self.assertEqual(bootable, cloned_vol_details['bootable'])
+
@decorators.idempotent_id('9adae371-a257-43a5-9555-dc7c88e66e0e')
def test_create_from_volume(self):
# Creates a volume from another volume passing a size different from
@@ -41,10 +53,7 @@
dst_vol = self.create_volume(source_volid=src_vol['id'],
size=src_size + 1)
- volume = self.volumes_client.show_volume(dst_vol['id'])['volume']
- # Should allow
- self.assertEqual(volume['source_volid'], src_vol['id'])
- self.assertEqual(volume['size'], src_size + 1)
+ self._verify_volume_clone(src_vol, dst_vol, extra_size=1)
@decorators.idempotent_id('cbbcd7c6-5a6c-481a-97ac-ca55ab715d16')
@test.services('image')
@@ -55,10 +64,5 @@
# Create a volume from the bootable volume
cloned_vol = self.create_volume(source_volid=src_vol['id'])
- cloned_vol_details = self.volumes_client.show_volume(
- cloned_vol['id'])['volume']
- # Verify cloned volume creation as expected
- self.assertEqual('true', cloned_vol_details['bootable'])
- self.assertEqual(src_vol['id'], cloned_vol_details['source_volid'])
- self.assertEqual(src_vol['size'], cloned_vol_details['size'])
+ self._verify_volume_clone(src_vol, cloned_vol, bootable='true')
diff --git a/tempest/clients.py b/tempest/clients.py
index 467ef9c..85c2242 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -13,17 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_log import log as logging
-
from tempest import config
from tempest.lib import auth
from tempest.lib import exceptions as lib_exc
from tempest.lib.services import clients
from tempest.services import object_storage
-from tempest.services import orchestration
CONF = config.CONF
-LOG = logging.getLogger(__name__)
class Manager(clients.ServiceClients):
@@ -52,15 +48,6 @@
self._set_image_clients()
self._set_network_clients()
- self.orchestration_client = orchestration.OrchestrationClient(
- self.auth_provider,
- CONF.orchestration.catalog_type,
- CONF.orchestration.region or CONF.identity.region,
- endpoint_type=CONF.orchestration.endpoint_type,
- build_interval=CONF.orchestration.build_interval,
- build_timeout=CONF.orchestration.build_timeout,
- **self.default_params)
-
def _set_network_clients(self):
self.network_agents_client = self.network.AgentsClient()
self.network_extensions_client = self.network.ExtensionsClient()
@@ -208,6 +195,7 @@
self.identity_v3.EndPointsFilterClient(**params_v3)
self.endpoint_groups_client = self.identity_v3.EndPointGroupsClient(
**params_v3)
+ self.catalog_client = self.identity_v3.CatalogClient(**params_v3)
# Token clients do not use the catalog. They only need default_params.
# They read auth_url, so they should only be set if the corresponding
@@ -229,49 +217,63 @@
def _set_volume_clients(self):
- self.volume_qos_client = self.volume_v1.QosSpecsClient()
- self.volume_qos_v2_client = self.volume_v2.QosSpecsClient()
- self.volume_services_client = self.volume_v1.ServicesClient()
- self.volume_services_v2_client = self.volume_v2.ServicesClient()
- self.backups_client = self.volume_v1.BackupsClient()
- self.backups_v2_client = self.volume_v2.BackupsClient()
- self.backups_v3_client = self.volume_v3.BackupsClient()
- self.encryption_types_client = self.volume_v1.EncryptionTypesClient()
- self.encryption_types_v2_client = \
- self.volume_v2.EncryptionTypesClient()
- self.snapshot_manage_v2_client = self.volume_v2.SnapshotManageClient()
- self.snapshots_client = self.volume_v1.SnapshotsClient()
- self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
- self.volume_manage_v2_client = self.volume_v2.VolumeManageClient()
- self.volumes_client = self.volume_v1.VolumesClient()
- self.volumes_v2_client = self.volume_v2.VolumesClient()
- self.volumes_v3_client = self.volume_v3.VolumesClient()
- self.volume_v3_messages_client = self.volume_v3.MessagesClient()
- self.volume_v3_versions_client = self.volume_v3.VersionsClient()
- self.volume_types_client = self.volume_v1.TypesClient()
- self.volume_types_v2_client = self.volume_v2.TypesClient()
- self.volume_hosts_client = self.volume_v1.HostsClient()
- self.volume_hosts_v2_client = self.volume_v2.HostsClient()
- self.volume_quotas_client = self.volume_v1.QuotasClient()
- self.volume_quotas_v2_client = self.volume_v2.QuotasClient()
- self.volume_quota_classes_v2_client = \
- self.volume_v2.QuotaClassesClient()
- self.volumes_extension_client = self.volume_v1.ExtensionsClient()
- self.volumes_v2_extension_client = self.volume_v2.ExtensionsClient()
- self.groups_v3_client = self.volume_v3.GroupsClient()
- self.group_types_v3_client = self.volume_v3.GroupTypesClient()
- self.volume_availability_zone_client = \
- self.volume_v1.AvailabilityZoneClient()
- self.volume_v2_availability_zone_client = \
- self.volume_v2.AvailabilityZoneClient()
- self.volume_limits_client = self.volume_v1.LimitsClient()
- self.volume_v2_limits_client = self.volume_v2.LimitsClient()
- self.volume_capabilities_v2_client = \
- self.volume_v2.CapabilitiesClient()
- self.volume_scheduler_stats_v2_client = \
- self.volume_v2.SchedulerStatsClient()
- self.volume_transfers_v2_client = \
- self.volume_v2.TransfersClient()
+ if CONF.volume_feature_enabled.api_v1:
+ self.backups_client = self.volume_v1.BackupsClient()
+ self.encryption_types_client = \
+ self.volume_v1.EncryptionTypesClient()
+ self.snapshots_client = self.volume_v1.SnapshotsClient()
+ self.volume_availability_zone_client = \
+ self.volume_v1.AvailabilityZoneClient()
+ self.volume_hosts_client = self.volume_v1.HostsClient()
+ self.volume_limits_client = self.volume_v1.LimitsClient()
+ self.volume_qos_client = self.volume_v1.QosSpecsClient()
+ self.volume_quotas_client = self.volume_v1.QuotasClient()
+ self.volume_services_client = self.volume_v1.ServicesClient()
+ self.volume_types_client = self.volume_v1.TypesClient()
+ self.volumes_client = self.volume_v1.VolumesClient()
+ self.volumes_extension_client = self.volume_v1.ExtensionsClient()
+
+ if CONF.volume_feature_enabled.api_v2:
+ self.backups_v2_client = self.volume_v2.BackupsClient()
+ self.encryption_types_v2_client = \
+ self.volume_v2.EncryptionTypesClient()
+ self.snapshot_manage_v2_client = \
+ self.volume_v2.SnapshotManageClient()
+ self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
+ self.volume_capabilities_v2_client = \
+ self.volume_v2.CapabilitiesClient()
+ self.volume_manage_v2_client = self.volume_v2.VolumeManageClient()
+ self.volume_qos_v2_client = self.volume_v2.QosSpecsClient()
+ self.volume_services_v2_client = self.volume_v2.ServicesClient()
+ self.volume_types_v2_client = self.volume_v2.TypesClient()
+ self.volume_hosts_v2_client = self.volume_v2.HostsClient()
+ self.volume_quotas_v2_client = self.volume_v2.QuotasClient()
+ self.volume_quota_classes_v2_client = \
+ self.volume_v2.QuotaClassesClient()
+ self.volume_scheduler_stats_v2_client = \
+ self.volume_v2.SchedulerStatsClient()
+ self.volume_transfers_v2_client = \
+ self.volume_v2.TransfersClient()
+ self.volume_v2_availability_zone_client = \
+ self.volume_v2.AvailabilityZoneClient()
+ self.volume_v2_limits_client = self.volume_v2.LimitsClient()
+ self.volumes_v2_client = self.volume_v2.VolumesClient()
+ self.volumes_v2_extension_client = \
+ self.volume_v2.ExtensionsClient()
+
+ # Set default client for users that don't need explicit version
+ self.volumes_client_latest = self.volumes_v2_client
+
+ if CONF.volume_feature_enabled.api_v3:
+ self.backups_v3_client = self.volume_v3.BackupsClient()
+ self.group_types_v3_client = self.volume_v3.GroupTypesClient()
+ self.groups_v3_client = self.volume_v3.GroupsClient()
+ self.volume_v3_messages_client = self.volume_v3.MessagesClient()
+ self.volume_v3_versions_client = self.volume_v3.VersionsClient()
+ self.volumes_v3_client = self.volume_v3.VolumesClient()
+
+ # Set default client for users that don't need explicit version
+ self.volumes_client_latest = self.volumes_v3_client
def _set_object_storage_clients(self):
# NOTE(andreaf) Load configuration from config. Once object storage
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 172d9e1..8636405 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -102,8 +102,8 @@
import yaml
from tempest.common import credentials_factory
-from tempest.common import dynamic_creds
from tempest import config
+from tempest.lib.common import dynamic_creds
LOG = None
@@ -141,18 +141,10 @@
admin_creds = credentials_factory.get_credentials(
fill_in=False, identity_version=identity_version, **admin_creds_dict)
return dynamic_creds.DynamicCredentialProvider(
- identity_version=identity_version,
name=opts.tag,
network_resources=network_resources,
- neutron_available=CONF.service_available.neutron,
- create_networks=CONF.auth.create_isolated_networks,
- identity_admin_role=CONF.identity.admin_role,
- identity_admin_domain_scope=CONF.identity.admin_domain_scope,
- project_network_cidr=CONF.network.project_network_cidr,
- project_network_mask_bits=CONF.network.project_network_mask_bits,
- public_network_id=CONF.network.public_network_id,
- admin_creds=admin_creds,
- **credentials_factory.get_dynamic_provider_params())
+ **credentials_factory.get_dynamic_provider_params(
+ identity_version, admin_creds=admin_creds))
def generate_resources(cred_provider, admin):
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index f1c0a3e..11cd4bb 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -213,7 +213,9 @@
class StackService(BaseService):
def __init__(self, manager, **kwargs):
super(StackService, self).__init__(kwargs)
- self.client = manager.orchestration_client
+ params = config.service_client_config('orchestration')
+ self.client = manager.orchestration.OrchestrationClient(
+ manager.auth_provider, **params)
def list(self):
client = self.client
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 0972a3c..8e71ecc 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -95,7 +95,7 @@
client_dict = {
'nova': os.servers_client,
'keystone': os.identity_client,
- 'cinder': os.volumes_client,
+ 'cinder': os.volumes_client_latest,
}
if service != 'keystone' and service != 'cinder':
# Since keystone and cinder may be listening on a path,
@@ -200,7 +200,7 @@
if service != 'swift':
resp = extensions_client.list_extensions()
else:
- __, resp = extensions_client.list_capabilities()
+ resp = extensions_client.list_capabilities()
# For Nova, Cinder and Neutron we use the alias name rather than the
# 'name' field because the alias is considered to be the canonical
# name.
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 9f467fe..e3fbfb8 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -25,9 +25,9 @@
from oslo_log import log as logging
from oslo_utils import excutils
-from tempest.common import fixed_network
from tempest.common import waiters
from tempest import config
+from tempest.lib.common import fixed_network
from tempest.lib.common import rest_client
from tempest.lib.common.utils import data_utils
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index e6b46ed..fd875be 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -14,10 +14,10 @@
from oslo_concurrency import lockutils
from tempest import clients
-from tempest.common import dynamic_creds
-from tempest.common import preprov_creds
from tempest import config
from tempest.lib import auth
+from tempest.lib.common import dynamic_creds
+from tempest.lib.common import preprov_creds
from tempest.lib import exceptions
CONF = config.CONF
@@ -39,19 +39,66 @@
# Subset of the parameters of credential providers that depend on configuration
-def get_common_provider_params():
+def _get_common_provider_params(identity_version):
+ if identity_version == 'v3':
+ identity_uri = CONF.identity.uri_v3
+ elif identity_version == 'v2':
+ identity_uri = CONF.identity.uri
return {
+ 'identity_version': identity_version,
+ 'identity_uri': identity_uri,
'credentials_domain': CONF.auth.default_credentials_domain_name,
'admin_role': CONF.identity.admin_role
}
-def get_dynamic_provider_params():
- return get_common_provider_params()
+def get_dynamic_provider_params(identity_version, admin_creds=None):
+ """Dynamic provider parameters setup from config
+
+ This helper returns a dict of parameter that can be used to initialise
+ a `DynamicCredentialProvider` according to tempest configuration.
+ Parameters that are not configuration specific (name, network_resources)
+ are not returned.
+
+ :param identity_version: 'v2' or 'v3'
+ :param admin_creds: An object of type `auth.Credentials`. If None, it
+ is built from the configuration file as well.
+ :returns A dict with the parameters
+ """
+ _common_params = _get_common_provider_params(identity_version)
+ admin_creds = admin_creds or get_configured_admin_credentials(
+ fill_in=True, identity_version=identity_version)
+ if identity_version == 'v3':
+ endpoint_type = CONF.identity.v3_endpoint_type
+ elif identity_version == 'v2':
+ endpoint_type = CONF.identity.v2_admin_endpoint_type
+ return dict(_common_params, **dict([
+ ('admin_creds', admin_creds),
+ ('identity_admin_domain_scope', CONF.identity.admin_domain_scope),
+ ('identity_admin_role', CONF.identity.admin_role),
+ ('extra_roles', CONF.auth.tempest_roles),
+ ('neutron_available', CONF.service_available.neutron),
+ ('project_network_cidr', CONF.network.project_network_cidr),
+ ('project_network_mask_bits', CONF.network.project_network_mask_bits),
+ ('public_network_id', CONF.network.public_network_id),
+ ('create_networks', (CONF.auth.create_isolated_networks and not
+ CONF.network.shared_physical_network)),
+ ('resource_prefix', CONF.resources_prefix),
+ ('identity_admin_endpoint_type', endpoint_type)
+ ]))
-def get_preprov_provider_params():
- _common_params = get_common_provider_params()
+def get_preprov_provider_params(identity_version):
+ """Pre-provisioned provider parameters setup from config
+
+ This helper returns a dict of parameter that can be used to initialise
+ a `PreProvisionedCredentialProvider` according to tempest configuration.
+ Parameters that are not configuration specific (name) are not returned.
+
+ :param identity_version: 'v2' or 'v3'
+ :returns A dict with the parameters
+ """
+ _common_params = _get_common_provider_params(identity_version)
reseller_admin_role = CONF.object_storage.reseller_admin_role
return dict(_common_params, **dict([
('accounts_lock_dir', lockutils.get_lock_path(CONF)),
@@ -61,53 +108,55 @@
]))
-# Return the right implementation of CredentialProvider based on config
-# Dropping interface and password, as they are never used anyways
-# TODO(andreaf) Drop them from the CredentialsProvider interface completely
def get_credentials_provider(name, network_resources=None,
force_tenant_isolation=False,
identity_version=None):
+ """Return the right implementation of CredentialProvider based on config
+
+ This helper returns the right implementation of CredentialProvider based on
+ config and on the value of force_tenant_isolation.
+
+ :param name: When provided, it makes it possible to associate credential
+ artifacts back to the owner (test class).
+ :param network_resources: Dictionary of network resources to be allocated
+ for each test account. Only valid for the dynamic
+ credentials provider.
+ :param force_tenant_isolation: Always return a `DynamicCredentialProvider`,
+ regardless of the configuration.
+ :param identity_version: Use the specified identity API version, regardless
+ of the configuration. Valid values are 'v2', 'v3'.
+ """
# If a test requires a new account to work, it can have it via forcing
# dynamic credentials. A new account will be produced only for that test.
# In case admin credentials are not available for the account creation,
# the test should be skipped else it would fail.
identity_version = identity_version or CONF.identity.auth_version
if CONF.auth.use_dynamic_credentials or force_tenant_isolation:
- admin_creds = get_configured_admin_credentials(
- fill_in=True, identity_version=identity_version)
return dynamic_creds.DynamicCredentialProvider(
name=name,
network_resources=network_resources,
- identity_version=identity_version,
- admin_creds=admin_creds,
- identity_admin_domain_scope=CONF.identity.admin_domain_scope,
- identity_admin_role=CONF.identity.admin_role,
- extra_roles=CONF.auth.tempest_roles,
- neutron_available=CONF.service_available.neutron,
- project_network_cidr=CONF.network.project_network_cidr,
- project_network_mask_bits=CONF.network.project_network_mask_bits,
- public_network_id=CONF.network.public_network_id,
- create_networks=(CONF.auth.create_isolated_networks and not
- CONF.network.shared_physical_network),
- resource_prefix=CONF.resources_prefix,
- **get_dynamic_provider_params())
+ **get_dynamic_provider_params(identity_version))
else:
if CONF.auth.test_accounts_file:
# Most params are not relevant for pre-created accounts
return preprov_creds.PreProvisionedCredentialProvider(
- name=name, identity_version=identity_version,
- **get_preprov_provider_params())
+ name=name,
+ **get_preprov_provider_params(identity_version))
else:
raise exceptions.InvalidConfiguration(
'A valid credential provider is needed')
-# We want a helper function here to check and see if admin credentials
-# are available so we can do a single call from skip_checks if admin
-# creds area available.
-# This depends on identity_version as there may be admin credentials
-# available for v2 but not for v3.
def is_admin_available(identity_version):
+ """Helper to check for admin credentials
+
+ Helper function to check if a set of admin credentials is available so we
+ can do a single call from skip_checks.
+ This helper depends on identity_version as there may be admin credentials
+ available for v2 but not for v3.
+
+ :param identity_version: 'v2' or 'v3'
+ """
is_admin = True
# If dynamic credentials is enabled admin will be available
if CONF.auth.use_dynamic_credentials:
@@ -115,8 +164,8 @@
# Check whether test accounts file has the admin specified or not
elif CONF.auth.test_accounts_file:
check_accounts = preprov_creds.PreProvisionedCredentialProvider(
- identity_version=identity_version, name='check_admin',
- **get_preprov_provider_params())
+ name='check_admin',
+ **get_preprov_provider_params(identity_version))
if not check_accounts.admin_available():
is_admin = False
else:
@@ -128,20 +177,24 @@
return is_admin
-# We want a helper function here to check and see if alt credentials
-# are available so we can do a single call from skip_checks if alt
-# creds area available.
-# This depends on identity_version as there may be alt credentials
-# available for v2 but not for v3.
def is_alt_available(identity_version):
+ """Helper to check for alt credentials
+
+ Helper function to check if a second set of credentials is available (aka
+ alt credentials) so we can do a single call from skip_checks.
+ This helper depends on identity_version as there may be alt credentials
+ available for v2 but not for v3.
+
+ :param identity_version: 'v2' or 'v3'
+ """
# If dynamic credentials is enabled alt will be available
if CONF.auth.use_dynamic_credentials:
return True
# Check whether test accounts file has the admin specified or not
if CONF.auth.test_accounts_file:
check_accounts = preprov_creds.PreProvisionedCredentialProvider(
- identity_version=identity_version, name='check_alt',
- **get_preprov_provider_params())
+ name='check_alt',
+ **get_preprov_provider_params(identity_version))
else:
raise exceptions.InvalidConfiguration(
'A valid credential provider is needed')
@@ -171,9 +224,19 @@
}
-# Read credentials from configuration, builds a Credentials object
-# based on the specified or configured version
def get_configured_admin_credentials(fill_in=True, identity_version=None):
+ """Get admin credentials from the config file
+
+ Read credentials from configuration, builds a Credentials object based on
+ the specified or configured version
+
+ :param fill_in: If True, a request to the Token API is submitted, and the
+ credential object is filled in with all names and IDs from
+ the token API response.
+ :param identity_version: The identity version to talk to and the type of
+ credentials object to be created. 'v2' or 'v3'.
+ :returns: An object of a sub-type of `auth.Credentials`
+ """
identity_version = identity_version or CONF.identity.auth_version
if identity_version not in ('v2', 'v3'):
@@ -205,6 +268,19 @@
# Wrapper around auth.get_credentials to use the configured identity version
# if none is specified
def get_credentials(fill_in=True, identity_version=None, **kwargs):
+ """Get credentials from dict based on config
+
+ Wrapper around auth.get_credentials to use the configured identity version
+ if none is specified.
+
+ :param fill_in: If True, a request to the Token API is submitted, and the
+ credential object is filled in with all names and IDs from
+ the token API response.
+ :param identity_version: The identity version to talk to and the type of
+ credentials object to be created. 'v2' or 'v3'.
+ :param kwargs: Attributes to be used to build the Credentials object.
+ :returns: An object of a sub-type of `auth.Credentials`
+ """
params = dict(DEFAULT_PARAMS, **kwargs)
identity_version = identity_version or CONF.identity.auth_version
# In case of "v3" add the domain from config if not specified
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index a437761..b5b2d71 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -52,12 +52,5 @@
"the configured network")
-# 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
-# be migrated along with them
-class InvalidTestResource(exceptions.TempestException):
- message = "%(name)s is not a valid %(type)s, or the name is ambiguous"
-
-
class RFCViolation(exceptions.RestClientException):
message = "RFC Violation"
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 7360396..2954de0 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -420,8 +420,13 @@
'properties': {
'event': {'type': 'string'},
'start_time': parameter_types.date_time,
- 'finish_time': parameter_types.date_time,
- 'result': {'type': 'string'},
+ # The finish_time, result and optionally traceback are all
+ # possibly None (null) until the event is actually finished.
+ # The traceback would only be set if there was an error, but
+ # when the event is complete both finish_time and result will
+ # be set.
+ 'finish_time': parameter_types.date_time_or_null,
+ 'result': {'type': ['string', 'null']},
'traceback': {'type': ['string', 'null']}
},
'additionalProperties': False,
diff --git a/tempest/lib/api_schema/response/compute/v2_1/services.py b/tempest/lib/api_schema/response/compute/v2_1/services.py
index 6949f86..3b58ece 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/services.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/services.py
@@ -65,3 +65,25 @@
'required': ['service']
}
}
+
+disable_log_reason = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'service': {
+ 'type': 'object',
+ 'properties': {
+ 'disabled_reason': {'type': 'string'},
+ 'binary': {'type': 'string'},
+ 'host': {'type': 'string'},
+ 'status': {'type': 'string'}
+ },
+ 'additionalProperties': False,
+ 'required': ['disabled_reason', 'binary', 'host', 'status']
+ }
+ },
+ 'additionalProperties': False,
+ 'required': ['service']
+ }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_11/__init__.py b/tempest/lib/api_schema/response/compute/v2_11/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_11/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_11/services.py b/tempest/lib/api_schema/response/compute/v2_11/services.py
new file mode 100644
index 0000000..18b833b
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_11/services.py
@@ -0,0 +1,46 @@
+# Copyright 2017 AT&T 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 services
+
+
+list_services = copy.deepcopy(services.list_services)
+list_services['response_body']['properties']['services']['items'][
+ 'properties']['forced_down'] = {'type': 'boolean'}
+list_services['response_body']['properties']['services']['items'][
+ 'required'].append('forced_down')
+
+update_forced_down = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'service': {
+ 'type': 'object',
+ 'properties': {
+ 'binary': {'type': 'string'},
+ 'host': {'type': 'string'},
+ 'forced_down': {'type': 'boolean'}
+ },
+ 'additionalProperties': False,
+ 'required': ['binary', 'host', 'forced_down']
+ }
+ },
+ 'additionalProperties': False,
+ 'required': ['service']
+ }
+}
diff --git a/tempest/lib/common/cred_provider.py b/tempest/lib/common/cred_provider.py
index 1b450ab..42ed41b 100644
--- a/tempest/lib/common/cred_provider.py
+++ b/tempest/lib/common/cred_provider.py
@@ -22,8 +22,9 @@
@six.add_metaclass(abc.ABCMeta)
class CredentialProvider(object):
- def __init__(self, identity_version, name=None, network_resources=None,
- credentials_domain=None, admin_role=None):
+ def __init__(self, identity_version, name=None,
+ network_resources=None, credentials_domain=None,
+ admin_role=None, identity_uri=None):
"""A CredentialProvider supplies credentials to test classes.
:param identity_version: Identity version of the credentials provided
@@ -33,8 +34,11 @@
credentials
:param credentials_domain: Domain credentials belong to
:param admin_role: Name of the role of the admin account
+ :param identity_uri: Identity URI of the target cloud. This *must* be
+ specified for anything to work.
"""
self.identity_version = identity_version
+ self.identity_uri = identity_uri
self.name = name or "test_creds"
self.network_resources = network_resources
self.credentials_domain = credentials_domain or 'Default'
diff --git a/tempest/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
similarity index 88%
rename from tempest/common/dynamic_creds.py
rename to tempest/lib/common/dynamic_creds.py
index 88fe26c..90e67b4 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -12,15 +12,17 @@
# License for the specific language governing permissions and limitations
# under the License.
+import ipaddress
+
import netaddr
from oslo_log import log as logging
import six
-from tempest import clients
from tempest.lib.common import cred_client
from tempest.lib.common import cred_provider
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
+from tempest.lib.services import clients
LOG = logging.getLogger(__name__)
@@ -33,7 +35,8 @@
identity_admin_role='admin', extra_roles=None,
neutron_available=False, create_networks=True,
project_network_cidr=None, project_network_mask_bits=None,
- public_network_id=None, resource_prefix=None):
+ public_network_id=None, resource_prefix=None,
+ identity_admin_endpoint_type='public', identity_uri=None):
"""Creates credentials dynamically for tests
A credential provider that, based on an initial set of
@@ -67,10 +70,14 @@
:param project_network_mask_bits: The network mask bits to use for
created project networks
:param public_network_id: The id for the public network to use
+ :param identity_admin_endpoint_type: The endpoint type for identity
+ admin clients. Defaults to public.
+ :param identity_uri: Identity URI of the target cloud
"""
super(DynamicCredentialProvider, self).__init__(
- identity_version=identity_version, admin_role=admin_role,
- name=name, credentials_domain=credentials_domain,
+ identity_version=identity_version, identity_uri=identity_uri,
+ admin_role=admin_role, name=name,
+ credentials_domain=credentials_domain,
network_resources=network_resources)
self.network_resources = network_resources
self._creds = {}
@@ -84,6 +91,7 @@
self.default_admin_creds = admin_creds
self.identity_admin_domain_scope = identity_admin_domain_scope
self.identity_admin_role = identity_admin_role or 'admin'
+ self.identity_admin_endpoint_type = identity_admin_endpoint_type
self.extra_roles = extra_roles or []
(self.identity_admin_client,
self.tenants_admin_client,
@@ -94,7 +102,8 @@
self.routers_admin_client,
self.subnets_admin_client,
self.ports_admin_client,
- self.security_groups_admin_client) = self._get_admin_clients()
+ self.security_groups_admin_client) = self._get_admin_clients(
+ identity_admin_endpoint_type)
# Domain where isolated credentials are provisioned (v3 only).
# Use that of the admin account is None is configured.
self.creds_domain_name = None
@@ -110,32 +119,43 @@
self.domains_admin_client,
self.creds_domain_name)
- def _get_admin_clients(self):
+ def _get_admin_clients(self, endpoint_type):
"""Returns a tuple with instances of the following admin clients
(in this order):
identity
network
"""
- os = clients.Manager(self.default_admin_creds)
+ os = clients.ServiceClients(self.default_admin_creds,
+ self.identity_uri)
+ params = {'endpoint_type': endpoint_type}
if self.identity_version == 'v2':
- return (os.identity_client, os.tenants_client, os.users_client,
- os.roles_client, None,
- os.networks_client, os.routers_client, os.subnets_client,
- os.ports_client, os.security_groups_client)
+ return (os.identity_v2.IdentityClient(**params),
+ os.identity_v2.TenantsClient(**params),
+ os.identity_v2.UsersClient(**params),
+ os.identity_v2.RolesClient(**params), None,
+ os.network.NetworksClient(),
+ os.network.RoutersClient(),
+ os.network.SubnetsClient(),
+ os.network.PortsClient(),
+ os.network.SecurityGroupsClient())
else:
# We use a dedicated client manager for identity client in case we
# need a different token scope for them.
scope = 'domain' if self.identity_admin_domain_scope else 'project'
- identity_os = clients.Manager(self.default_admin_creds,
- scope=scope)
- return (identity_os.identity_v3_client,
- identity_os.projects_client,
- identity_os.users_v3_client, identity_os.roles_v3_client,
- identity_os.domains_client,
- os.networks_client, os.routers_client,
- os.subnets_client, os.ports_client,
- os.security_groups_client)
+ identity_os = clients.ServiceClients(self.default_admin_creds,
+ self.identity_uri,
+ scope=scope)
+ return (identity_os.identity_v3.IdentityClient(**params),
+ identity_os.identity_v3.ProjectsClient(**params),
+ identity_os.identity_v3.UsersClient(**params),
+ identity_os.identity_v3.RolesClient(**params),
+ identity_os.identity_v3.DomainsClient(**params),
+ os.network.NetworksClient(),
+ os.network.RoutersClient(),
+ os.network.SubnetsClient(),
+ os.network.PortsClient(),
+ os.network.SecurityGroupsClient())
def _create_creds(self, admin=False, roles=None):
"""Create credentials with random name.
@@ -275,14 +295,16 @@
name=subnet_name,
tenant_id=tenant_id,
enable_dhcp=self.network_resources['dhcp'],
- ip_version=4)
+ ip_version=(ipaddress.ip_network(
+ six.text_type(subnet_cidr)).version))
else:
resp_body = self.subnets_admin_client.\
create_subnet(network_id=network_id,
cidr=str(subnet_cidr),
name=subnet_name,
tenant_id=tenant_id,
- ip_version=4)
+ ip_version=(ipaddress.ip_network(
+ six.text_type(subnet_cidr)).version))
break
except lib_exc.BadRequest as e:
if 'overlaps with another subnet' not in str(e):
diff --git a/tempest/common/fixed_network.py b/tempest/lib/common/fixed_network.py
similarity index 99%
rename from tempest/common/fixed_network.py
rename to tempest/lib/common/fixed_network.py
index 4032c90..e2054a4 100644
--- a/tempest/common/fixed_network.py
+++ b/tempest/lib/common/fixed_network.py
@@ -14,8 +14,8 @@
from oslo_log import log as logging
-from tempest import exceptions
from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
LOG = logging.getLogger(__name__)
diff --git a/tempest/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
similarity index 96%
rename from tempest/common/preprov_creds.py
rename to tempest/lib/common/preprov_creds.py
index 8053cac..cd3a10e 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -20,12 +20,11 @@
import six
import yaml
-from tempest import clients
-from tempest.common import fixed_network
-from tempest import exceptions
from tempest.lib import auth
from tempest.lib.common import cred_provider
+from tempest.lib.common import fixed_network
from tempest.lib import exceptions as lib_exc
+from tempest.lib.services import clients
LOG = logging.getLogger(__name__)
@@ -51,7 +50,7 @@
def __init__(self, identity_version, test_accounts_file,
accounts_lock_dir, name=None, credentials_domain=None,
admin_role=None, object_storage_operator_role=None,
- object_storage_reseller_admin_role=None):
+ object_storage_reseller_admin_role=None, identity_uri=None):
"""Credentials provider using pre-provisioned accounts
This credentials provider loads the details of pre-provisioned
@@ -79,10 +78,12 @@
(if no domain is configured)
:param object_storage_operator_role: name of the role
:param object_storage_reseller_admin_role: name of the role
+ :param identity_uri: Identity URI of the target cloud
"""
super(PreProvisionedCredentialProvider, self).__init__(
identity_version=identity_version, name=name,
- admin_role=admin_role, credentials_domain=credentials_domain)
+ admin_role=admin_role, credentials_domain=credentials_domain,
+ identity_uri=identity_uri)
self.test_accounts_file = test_accounts_file
if test_accounts_file:
accounts = read_accounts_yaml(self.test_accounts_file)
@@ -341,13 +342,14 @@
auth_url=None, fill_in=False,
identity_version=self.identity_version, **creds_dict)
net_creds = cred_provider.TestResources(credential)
- net_clients = clients.Manager(credentials=credential)
- compute_network_client = net_clients.compute_networks_client
+ net_clients = clients.ServiceClients(credentials=credential,
+ identity_uri=self.identity_uri)
+ compute_network_client = net_clients.compute.NetworksClient()
net_name = self.hash_dict['networks'].get(hash, None)
try:
network = fixed_network.get_network_from_name(
net_name, compute_network_client)
- except exceptions.InvalidTestResource:
+ except lib_exc.InvalidTestResource:
network = {}
net_creds.set_resources(network=network)
return net_creds
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 63cf07f..f58d737 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -371,7 +371,7 @@
on the endpoint in the catalog will return a list of supported API
versions.
- :return tuple with response headers and list of version numbers
+ :return: tuple with response headers and list of version numbers
:rtype: tuple
"""
resp, body = self.get('')
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index 68ce57a..cdb8be9 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -269,3 +269,7 @@
class DeleteErrorException(TempestException):
message = ("Resource %(resource_id)s failed to delete "
"and is in ERROR status")
+
+
+class InvalidTestResource(TempestException):
+ message = "%(name)s is not a valid %(type)s, or the name is ambiguous"
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index cd3bab0..5f230b7 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -161,7 +161,7 @@
:param kwargs: Parameters to be passed to all clients. Parameters
values can be overwritten when clients are initialised, but
parameters cannot be deleted.
- :raise ImportError if the specified module_path cannot be imported
+ :raise ImportError: if the specified module_path cannot be imported
Example::
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
index 77ac82f..857c435 100644
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -18,12 +18,18 @@
from six.moves.urllib import parse as urllib
from tempest.lib.api_schema.response.compute.v2_1 import services as schema
+from tempest.lib.api_schema.response.compute.v2_11 import services \
+ as schemav211
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
class ServicesClient(base_compute_client.BaseComputeClient):
+ schema_versions_info = [
+ {'min': None, 'max': '2.10', 'schema': schema},
+ {'min': '2.11', 'max': None, 'schema': schemav211}]
+
def list_services(self, **params):
"""Lists all running Compute services for a tenant.
@@ -37,7 +43,8 @@
resp, body = self.get(url)
body = json.loads(body)
- self.validate_response(schema.list_services, resp, body)
+ _schema = self.get_schema(self.schema_versions_info)
+ self.validate_response(_schema.list_services, resp, body)
return rest_client.ResponseBody(resp, body)
def enable_service(self, **kwargs):
@@ -65,3 +72,31 @@
body = json.loads(body)
self.validate_response(schema.enable_disable_service, resp, body)
return rest_client.ResponseBody(resp, body)
+
+ def disable_log_reason(self, **kwargs):
+ """Disables scheduling for a Compute service and logs reason.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#log-disabled-compute-service-information
+ """
+ post_body = json.dumps(kwargs)
+ resp, body = self.put('os-services/disable-log-reason', post_body)
+ body = json.loads(body)
+ self.validate_response(schema.disable_log_reason, resp, body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_forced_down(self, **kwargs):
+ """Set or unset ``forced_down`` flag for the service.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#update-forced-down
+ """
+ post_body = json.dumps(kwargs)
+ resp, body = self.put('os-services/force-down', post_body)
+ body = json.loads(body)
+ # NOTE: Use schemav211.update_forced_down directly because there is no
+ # update_forced_down schema for <2.11.
+ self.validate_response(schemav211.update_forced_down, resp, body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
index e271a58..a539d08 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations under
# the License.
+from tempest.lib.services.identity.v3.catalog_client import \
+ CatalogClient
from tempest.lib.services.identity.v3.credentials_client import \
CredentialsClient
from tempest.lib.services.identity.v3.domain_configuration_client \
@@ -42,10 +44,11 @@
from tempest.lib.services.identity.v3.users_client import UsersClient
from tempest.lib.services.identity.v3.versions_client import VersionsClient
-__all__ = ['CredentialsClient', 'DomainsClient', 'DomainConfigurationClient',
- 'EndPointGroupsClient', 'EndPointsClient', 'EndPointsFilterClient',
- 'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
- 'OAUTHConsumerClient', 'OAUTHTokenClient', 'PoliciesClient',
- 'ProjectsClient', 'RegionsClient', 'RoleAssignmentsClient',
- 'RolesClient', 'ServicesClient', 'V3TokenClient', 'TrustsClient',
- 'UsersClient', 'VersionsClient']
+__all__ = ['CatalogClient', 'CredentialsClient', 'DomainsClient',
+ 'DomainConfigurationClient', 'EndPointGroupsClient',
+ 'EndPointsClient', 'EndPointsFilterClient', 'GroupsClient',
+ 'IdentityClient', 'InheritedRolesClient', 'OAUTHConsumerClient',
+ 'OAUTHTokenClient', 'PoliciesClient', 'ProjectsClient',
+ 'RegionsClient', 'RoleAssignmentsClient', 'RolesClient',
+ 'ServicesClient', 'V3TokenClient', 'TrustsClient', 'UsersClient',
+ 'VersionsClient']
diff --git a/tempest/lib/services/identity/v3/catalog_client.py b/tempest/lib/services/identity/v3/catalog_client.py
new file mode 100644
index 0000000..0f9d485
--- /dev/null
+++ b/tempest/lib/services/identity/v3/catalog_client.py
@@ -0,0 +1,30 @@
+# 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.
+
+"""
+https://developer.openstack.org/api-ref/identity/v3/index.html#\
+get-service-catalog
+"""
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class CatalogClient(rest_client.RestClient):
+ api_version = "v3"
+
+ def show_catalog(self):
+ resp, body = self.get('auth/catalog')
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/endpoint_groups_client.py b/tempest/lib/services/identity/v3/endpoint_groups_client.py
index 723aeaa..ce99389 100644
--- a/tempest/lib/services/identity/v3/endpoint_groups_client.py
+++ b/tempest/lib/services/identity/v3/endpoint_groups_client.py
@@ -1,78 +1,78 @@
-# Copyright 2017 AT&T 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 oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
-
-
-class EndPointGroupsClient(rest_client.RestClient):
- api_version = "v3"
-
- def create_endpoint_group(self, **kwargs):
- """Create endpoint group.
-
- For a full list of available parameters, please refer to the
- official API reference:
- https://developer.openstack.org/api-ref/identity/v3-ext/#create-endpoint-group
- """
- post_body = json.dumps({'endpoint_group': kwargs})
- resp, body = self.post('OS-EP-FILTER/endpoint_groups', post_body)
- self.expected_success(201, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
-
- def update_endpoint_group(self, endpoint_group_id, **kwargs):
- """Update endpoint group.
-
- For a full list of available parameters, please refer to the
- official API reference:
- https://developer.openstack.org/api-ref/identity/v3-ext/#update-endpoint-group
- """
- post_body = json.dumps({'endpoint_group': kwargs})
- resp, body = self.patch(
- 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id, post_body)
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
-
- def delete_endpoint_group(self, endpoint_group_id):
- """Delete endpoint group."""
- resp_header, resp_body = self.delete(
- 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)
- self.expected_success(204, resp_header.status)
- return rest_client.ResponseBody(resp_header, resp_body)
-
- def show_endpoint_group(self, endpoint_group_id):
- """Get endpoint group."""
- resp_header, resp_body = self.get(
- 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)
- self.expected_success(200, resp_header.status)
- resp_body = json.loads(resp_body)
- return rest_client.ResponseBody(resp_header, resp_body)
-
- def check_endpoint_group(self, endpoint_group_id):
- """Check endpoint group."""
- resp_header, resp_body = self.head(
- 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)
- self.expected_success(200, resp_header.status)
- return rest_client.ResponseBody(resp_header, resp_body)
-
- def list_endpoint_groups(self):
- """Get endpoint groups."""
- resp_header, resp_body = self.get('OS-EP-FILTER/endpoint_groups')
- self.expected_success(200, resp_header.status)
- resp_body = json.loads(resp_body)
- return rest_client.ResponseBody(resp_header, resp_body)
+# Copyright 2017 AT&T 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class EndPointGroupsClient(rest_client.RestClient):
+ api_version = "v3"
+
+ def create_endpoint_group(self, **kwargs):
+ """Create endpoint group.
+
+ For a full list of available parameters, please refer to the
+ official API reference:
+ https://developer.openstack.org/api-ref/identity/v3-ext/#create-endpoint-group
+ """
+ post_body = json.dumps({'endpoint_group': kwargs})
+ resp, body = self.post('OS-EP-FILTER/endpoint_groups', post_body)
+ self.expected_success(201, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_endpoint_group(self, endpoint_group_id, **kwargs):
+ """Update endpoint group.
+
+ For a full list of available parameters, please refer to the
+ official API reference:
+ https://developer.openstack.org/api-ref/identity/v3-ext/#update-endpoint-group
+ """
+ post_body = json.dumps({'endpoint_group': kwargs})
+ resp, body = self.patch(
+ 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id, post_body)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_endpoint_group(self, endpoint_group_id):
+ """Delete endpoint group."""
+ resp_header, resp_body = self.delete(
+ 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)
+ self.expected_success(204, resp_header.status)
+ return rest_client.ResponseBody(resp_header, resp_body)
+
+ def show_endpoint_group(self, endpoint_group_id):
+ """Get endpoint group."""
+ resp_header, resp_body = self.get(
+ 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)
+ self.expected_success(200, resp_header.status)
+ resp_body = json.loads(resp_body)
+ return rest_client.ResponseBody(resp_header, resp_body)
+
+ def check_endpoint_group(self, endpoint_group_id):
+ """Check endpoint group."""
+ resp_header, resp_body = self.head(
+ 'OS-EP-FILTER/endpoint_groups/%s' % endpoint_group_id)
+ self.expected_success(200, resp_header.status)
+ return rest_client.ResponseBody(resp_header, resp_body)
+
+ def list_endpoint_groups(self):
+ """Get endpoint groups."""
+ resp_header, resp_body = self.get('OS-EP-FILTER/endpoint_groups')
+ self.expected_success(200, resp_header.status)
+ resp_body = json.loads(resp_body)
+ return rest_client.ResponseBody(resp_header, resp_body)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index d31259f..62b9992 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -318,6 +318,7 @@
post_body = json.dumps({'os-retype': kwargs})
resp, body = self.post('volumes/%s/action' % volume_id, post_body)
self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
def force_detach_volume(self, volume_id, **kwargs):
"""Force detach a volume.
diff --git a/tempest/lib/services/volume/v3/group_types_client.py b/tempest/lib/services/volume/v3/group_types_client.py
index a6edbf5..97bac48 100644
--- a/tempest/lib/services/volume/v3/group_types_client.py
+++ b/tempest/lib/services/volume/v3/group_types_client.py
@@ -14,6 +14,7 @@
# under the License.
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
from tempest.lib.services.volume import base_client
@@ -46,3 +47,31 @@
resp, body = self.delete("group_types/%s" % group_type_id)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
+
+ def list_group_types(self, **params):
+ """List all the group_types created.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/#list-group-types
+ """
+ url = 'group_types'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_group_type(self, group_type_id):
+ """Returns the details of a single group_type.
+
+ For more information, please refer to the official API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/#show-group-type-details
+ """
+ url = "group_types/%s" % group_type_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml b/tempest/releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml
new file mode 100644
index 0000000..4abfe9e
--- /dev/null
+++ b/tempest/releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml
@@ -0,0 +1,5 @@
+---
+fixes:
+ Add a missing return statement to the retype_volume API in the v2 volumes_client library.
+ This changes the response body from None to an empty dictionary.
+
diff --git a/tempest/scenario/README.rst b/tempest/scenario/README.rst
index 38e0de9..c1dcccc 100644
--- a/tempest/scenario/README.rst
+++ b/tempest/scenario/README.rst
@@ -21,9 +21,9 @@
4. create a snapshot of the vm
-Why are these tests in tempest?
+Why are these tests in Tempest?
-------------------------------
-This is one of tempests core purposes, testing the integration between
+This is one of Tempest's core purposes, testing the integration between
projects.
@@ -43,7 +43,7 @@
specific in your interactions. A giant "this is my data center" smoke
test is hard to debug when it goes wrong.
-A flow of interactions between glance and nova, like in the
+A flow of interactions between Glance and Nova, like in the
introduction, is a good example. Especially if it involves a repeated
interaction when a resource is setup, modified, detached, and then
reused later again.
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index aecb374..9b8c7a0 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -238,9 +238,26 @@
volume = self.volumes_client.show_volume(volume['id'])['volume']
return volume
+ def create_volume_snapshot(self, volume_id, name=None, description=None,
+ metadata=None, force=False):
+ name = name or data_utils.rand_name(
+ self.__class__.__name__ + '-snapshot')
+ snapshot = self.snapshots_client.create_snapshot(
+ volume_id=volume_id,
+ force=force,
+ display_name=name,
+ description=description,
+ metadata=metadata)['snapshot']
+ self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
+ snapshot['id'])
+ self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
+ waiters.wait_for_volume_resource_status(self.snapshots_client,
+ snapshot['id'], 'available')
+ return snapshot
+
def create_volume_type(self, client=None, name=None, backend_name=None):
if not client:
- client = self.admin_volume_types_client
+ client = self.os_admin.volume_types_v2_client
if not name:
class_name = self.__class__.__name__
name = data_utils.rand_name(class_name + '-volume-type')
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index debd664..3632648 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -16,9 +16,7 @@
from oslo_log import log as logging
import testtools
-from tempest.common import waiters
from tempest import config
-from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
@@ -57,20 +55,6 @@
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
- def _create_volume_snapshot(self, volume):
- snapshot_name = data_utils.rand_name('scenario-snapshot')
- snapshot = self.snapshots_client.create_snapshot(
- volume_id=volume['id'], display_name=snapshot_name)['snapshot']
- self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
- snapshot['id'])
- self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
- waiters.wait_for_volume_resource_status(self.volumes_client,
- volume['id'], 'available')
- waiters.wait_for_volume_resource_status(self.snapshots_client,
- snapshot['id'], 'available')
- self.assertEqual(snapshot_name, snapshot['name'])
- return snapshot
-
def _wait_for_volume_available_on_the_system(self, ip_address,
private_key):
ssh = self.get_remote_client(ip_address, private_key=private_key)
@@ -116,7 +100,7 @@
self.nova_volume_detach(server, volume)
# snapshot the volume
- volume_snapshot = self._create_volume_snapshot(volume)
+ volume_snapshot = self.create_volume_snapshot(volume['id'])
# snapshot the instance
snapshot_image = self.create_server_snapshot(server=server)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 96d0474..b6f3b38 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -69,21 +69,6 @@
return self.create_server(image_id='', **create_kwargs)
- def _create_snapshot_from_volume(self, vol_id):
- snap_name = data_utils.rand_name(
- self.__class__.__name__ + '-snapshot')
- snap = self.snapshots_client.create_snapshot(
- volume_id=vol_id,
- force=True,
- display_name=snap_name)['snapshot']
- self.addCleanup(
- self.snapshots_client.wait_for_resource_deletion, snap['id'])
- self.addCleanup(self.snapshots_client.delete_snapshot, snap['id'])
- waiters.wait_for_volume_resource_status(self.snapshots_client,
- snap['id'], 'available')
- self.assertEqual(snap_name, snap['name'])
- return snap
-
def _delete_server(self, server):
self.servers_client.delete_server(server['id'])
waiters.wait_for_server_termination(self.servers_client, server['id'])
@@ -147,7 +132,7 @@
# snapshot a volume
LOG.info("Creating snapshot from volume: %s", volume_origin['id'])
- snapshot = self._create_snapshot_from_volume(volume_origin['id'])
+ snapshot = self.create_volume_snapshot(volume_origin['id'], force=True)
# create a 3rd instance from snapshot
LOG.info("Creating third instance from snapshot: %s", snapshot['id'])
@@ -177,7 +162,7 @@
boot_volume = self._create_volume_from_image()
# Create a snapshot
- boot_snapshot = self._create_snapshot_from_volume(boot_volume['id'])
+ boot_snapshot = self.create_volume_snapshot(boot_volume['id'])
# Create a server from a volume snapshot
server = self._boot_instance_from_resource(
@@ -229,9 +214,8 @@
self._delete_server(instance)
@decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
- @testtools.skipIf(CONF.volume.storage_protocol.lower() in ['ceph', 'nfs'],
- 'Currently, {} does not support volume encryption'
- .format(CONF.volume.storage_protocol))
+ @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
+ 'Encrypted volume attach is not supported')
@test.services('compute', 'volume')
def test_boot_server_from_encrypted_volume_luks(self):
# Create an encrypted volume
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index 81b71b1..5667fbb 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -40,7 +40,6 @@
@classmethod
def setup_clients(cls):
super(TestVolumeMigrateRetypeAttached, cls).setup_clients()
- cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
cls.admin_volumes_client = cls.os_admin.volumes_v2_client
@classmethod
diff --git a/tempest/services/object_storage/bulk_middleware_client.py b/tempest/services/object_storage/bulk_middleware_client.py
index c194ea9..c11a105 100644
--- a/tempest/services/object_storage/bulk_middleware_client.py
+++ b/tempest/services/object_storage/bulk_middleware_client.py
@@ -24,39 +24,39 @@
To extract containers and objects on Swift cluster from
uploaded archived file. For More information please check:
- http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.bulk
+ https://docs.openstack.org/swift/latest/middleware.html#module-swift.common.middleware.bulk
"""
url = '%s?extract-archive=%s' % (upload_path, archive_file_format)
if headers is None:
headers = {}
resp, body = self.put(url, data, headers)
self.expected_success(200, resp.status)
- return resp, body
+ return rest_client.ResponseBodyData(resp, body)
def delete_bulk_data(self, data=None, headers=None):
"""Delete multiple objects or containers from their account.
For More information please check:
- http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.bulk
+ https://docs.openstack.org/swift/latest/middleware.html#module-swift.common.middleware.bulk
"""
url = '?bulk-delete'
if headers is None:
headers = {}
- resp, body = self.delete(url, headers=headers, body=data)
+ resp, body = self.delete(url, headers, data)
self.expected_success(200, resp.status)
- return resp, body
+ return rest_client.ResponseBodyData(resp, body)
def delete_bulk_data_with_post(self, data=None, headers=None):
"""Delete multiple objects or containers with POST request.
For More information please check:
- http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.bulk
+ https://docs.openstack.org/swift/latest/middleware.html#module-swift.common.middleware.bulk
"""
url = '?bulk-delete'
if headers is None:
headers = {}
- resp, body = self.post(url, headers=headers, body=data)
+ resp, body = self.post(url, data, headers)
self.expected_success([200, 204], resp.status)
- return resp, body
+ return rest_client.ResponseBodyData(resp, body)
diff --git a/tempest/services/object_storage/capabilities_client.py b/tempest/services/object_storage/capabilities_client.py
index 0fe437f..d31bbc2 100644
--- a/tempest/services/object_storage/capabilities_client.py
+++ b/tempest/services/object_storage/capabilities_client.py
@@ -28,4 +28,4 @@
self.reset_path()
body = json.loads(body)
self.expected_success(200, resp.status)
- return resp, body
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/test.py b/tempest/test.py
index fc846ff..317c0a7 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -26,10 +26,10 @@
from tempest import clients
from tempest.common import credentials_factory as credentials
-from tempest.common import fixed_network
import tempest.common.validation_resources as vresources
from tempest import config
from tempest.lib.common import cred_client
+from tempest.lib.common import fixed_network
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
diff --git a/tempest/tests/README.rst b/tempest/tests/README.rst
index e54d4c0..0587e7b 100644
--- a/tempest/tests/README.rst
+++ b/tempest/tests/README.rst
@@ -7,16 +7,16 @@
---------------------
Unit tests are the self checks for Tempest. They provide functional
-verification and regression checking for the internal components of tempest.
-They should be used to just verify that the individual pieces of tempest are
+verification and regression checking for the internal components of Tempest.
+They should be used to just verify that the individual pieces of Tempest are
working as expected. They should not require an external service to be running
-and should be able to run solely from the tempest tree.
+and should be able to run solely from the Tempest tree.
-Why are these tests in tempest?
+Why are these tests in Tempest?
-------------------------------
These tests exist to make sure that the mechanisms that we use inside of
-tempest to are valid and remain functional. They are only here for self
-validation of tempest.
+Tempest are valid and remain functional. They are only here for self
+validation of Tempest.
Scope of these tests
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
index 6773b2f..f907bd0 100644
--- a/tempest/tests/cmd/test_account_generator.py
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -20,7 +20,6 @@
from tempest import config
from tempest.tests import base
from tempest.tests import fake_config
-from tempest.tests.lib import fake_identity
class FakeOpts(object):
@@ -85,14 +84,10 @@
class TestAccountGeneratorV2(base.TestCase, MockHelpersMixin):
identity_version = 2
- identity_response = fake_identity._fake_v2_response
def setUp(self):
super(TestAccountGeneratorV2, self).setUp()
self.mock_config_and_opts(self.identity_version)
- self.useFixture(fixtures.MockPatch(
- 'tempest.lib.auth.AuthProvider.set_auth',
- return_value=self.identity_response))
def test_get_credential_provider(self):
cp = account_generator.get_credential_provider(self.opts)
@@ -115,7 +110,6 @@
class TestAccountGeneratorV3(TestAccountGeneratorV2):
identity_version = 3
- identity_response = fake_identity._fake_v3_response
def setUp(self):
super(TestAccountGeneratorV3, self).setUp()
@@ -145,16 +139,13 @@
class TestGenerateResourcesV2(base.TestCase, MockHelpersMixin):
identity_version = 2
- identity_response = fake_identity._fake_v2_response
cred_client = 'tempest.lib.common.cred_client.V2CredsClient'
- dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+ dynamic_creds = ('tempest.lib.common.dynamic_creds.'
+ 'DynamicCredentialProvider')
def setUp(self):
super(TestGenerateResourcesV2, self).setUp()
self.mock_config_and_opts(self.identity_version)
- self.useFixture(fixtures.MockPatch(
- 'tempest.lib.auth.AuthProvider.set_auth',
- return_value=self.identity_response))
self.cred_provider = account_generator.get_credential_provider(
self.opts)
self.mock_resource_creation()
@@ -244,7 +235,6 @@
class TestGenerateResourcesV3(TestGenerateResourcesV2):
identity_version = 3
- identity_response = fake_identity._fake_v3_response
cred_client = 'tempest.lib.common.cred_client.V3CredsClient'
def setUp(self):
@@ -255,17 +245,14 @@
class TestDumpAccountsV2(base.TestCase, MockHelpersMixin):
identity_version = 2
- identity_response = fake_identity._fake_v2_response
cred_client = 'tempest.lib.common.cred_client.V2CredsClient'
- dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+ dynamic_creds = ('tempest.lib.common.dynamic_creds.'
+ 'DynamicCredentialProvider')
domain_is_in = False
def setUp(self):
super(TestDumpAccountsV2, self).setUp()
self.mock_config_and_opts(self.identity_version)
- self.useFixture(fixtures.MockPatch(
- 'tempest.lib.auth.AuthProvider.set_auth',
- return_value=self.identity_response))
self.cred_provider = account_generator.get_credential_provider(
self.opts)
self.mock_resource_creation()
@@ -337,7 +324,6 @@
class TestDumpAccountsV3(TestDumpAccountsV2):
identity_version = 3
- identity_response = fake_identity._fake_v3_response
cred_client = 'tempest.lib.common.cred_client.V3CredsClient'
domain_is_in = True
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index b0e74fb..1415111 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -392,10 +392,10 @@
def test_verify_extensions_swift(self):
def fake_list_extensions():
- return (None, {'fake1': 'metadata',
- 'fake2': 'metadata',
- 'not_fake': 'metadata',
- 'swift': 'metadata'})
+ return {'fake1': 'metadata',
+ 'fake2': 'metadata',
+ 'not_fake': 'metadata',
+ 'swift': 'metadata'}
fake_os = mock.MagicMock()
fake_os.capabilities_client.list_capabilities = fake_list_extensions
self.useFixture(fixtures.MockPatchObject(
@@ -414,10 +414,10 @@
def test_verify_extensions_swift_all(self):
def fake_list_extensions():
- return (None, {'fake1': 'metadata',
- 'fake2': 'metadata',
- 'not_fake': 'metadata',
- 'swift': 'metadata'})
+ return {'fake1': 'metadata',
+ 'fake2': 'metadata',
+ 'not_fake': 'metadata',
+ 'swift': 'metadata'}
fake_os = mock.MagicMock()
fake_os.capabilities_client.list_capabilities = fake_list_extensions
self.useFixture(fixtures.MockPatchObject(
diff --git a/tempest/tests/common/test_admin_available.py b/tempest/tests/common/test_admin_available.py
index c3d248c..7b3b1b0 100644
--- a/tempest/tests/common/test_admin_available.py
+++ b/tempest/tests/common/test_admin_available.py
@@ -53,7 +53,7 @@
'password': 'p',
'types': ['admin']})
self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=accounts))
cfg.CONF.set_default('test_accounts_file',
use_accounts_file, group='auth')
diff --git a/tempest/tests/common/test_alt_available.py b/tempest/tests/common/test_alt_available.py
index b9a8967..a425bb8 100644
--- a/tempest/tests/common/test_alt_available.py
+++ b/tempest/tests/common/test_alt_available.py
@@ -40,7 +40,7 @@
project_name="t%s" % ii,
password="p") for ii in creds]
self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=accounts))
cfg.CONF.set_default('test_accounts_file',
use_accounts_file, group='auth')
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/lib/common/test_dynamic_creds.py
similarity index 98%
rename from tempest/tests/common/test_dynamic_creds.py
rename to tempest/tests/lib/common/test_dynamic_creds.py
index c739619..6aa7a42 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/lib/common/test_dynamic_creds.py
@@ -17,8 +17,8 @@
from oslo_config import cfg
from tempest.common import credentials_factory as credentials
-from tempest.common import dynamic_creds
from tempest import config
+from tempest.lib.common import dynamic_creds
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
from tempest.lib.services.identity.v2 import identity_client as v2_iden_client
@@ -46,7 +46,8 @@
fixed_params = {'name': 'test class',
'identity_version': 'v2',
- 'admin_role': 'admin'}
+ 'admin_role': 'admin',
+ 'identity_uri': 'fake_uri'}
token_client = v2_token_client
iden_client = v2_iden_client
@@ -619,7 +620,8 @@
fixed_params = {'name': 'test class',
'identity_version': 'v3',
- 'admin_role': 'admin'}
+ 'admin_role': 'admin',
+ 'identity_uri': 'fake_uri'}
token_client = v3_token_client
iden_client = v3_iden_client
@@ -657,7 +659,7 @@
creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
creds.creds_client = mock.MagicMock()
creds.creds_client.create_user_role.side_effect = lib_exc.Conflict
- with mock.patch('tempest.common.dynamic_creds.LOG') as log_mock:
+ with mock.patch('tempest.lib.common.dynamic_creds.LOG') as log_mock:
creds._create_creds()
log_mock.warning.assert_called_once_with(
"Member role already exists, ignoring conflict.")
diff --git a/tempest/tests/common/test_preprov_creds.py b/tempest/tests/lib/common/test_preprov_creds.py
similarity index 96%
rename from tempest/tests/common/test_preprov_creds.py
rename to tempest/tests/lib/common/test_preprov_creds.py
index 414b106..5402e47 100644
--- a/tempest/tests/common/test_preprov_creds.py
+++ b/tempest/tests/lib/common/test_preprov_creds.py
@@ -24,10 +24,10 @@
from oslo_concurrency.fixture import lockutils as lockutils_fixtures
from oslo_config import cfg
-from tempest.common import preprov_creds
from tempest import config
from tempest.lib import auth
from tempest.lib.common import cred_provider
+from tempest.lib.common import preprov_creds
from tempest.lib import exceptions as lib_exc
from tempest.tests import base
from tempest.tests import fake_config
@@ -38,6 +38,7 @@
fixed_params = {'name': 'test class',
'identity_version': 'v2',
+ 'identity_uri': 'fake_uri',
'test_accounts_file': 'fake_accounts_file',
'accounts_lock_dir': 'fake_locks_dir',
'admin_role': 'admin',
@@ -87,10 +88,13 @@
self.useFixture(lockutils_fixtures.ExternalLockFixture())
self.test_accounts = self._fake_accounts(cfg.CONF.identity.admin_role)
self.accounts_mock = self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=self.test_accounts))
self.useFixture(fixtures.MockPatch(
'os.path.isfile', return_value=True))
+ # NOTE(andreaf) Ensure config is loaded so service clients are
+ # registered in the registry before tests
+ config.service_client_config()
def tearDown(self):
super(TestPreProvisionedCredentials, self).tearDown()
@@ -267,7 +271,7 @@
def test_is_not_multi_user(self):
self.test_accounts = [self.test_accounts[0]]
self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=self.test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
@@ -331,7 +335,7 @@
'password': 'p', 'roles': ['role-7', 'role-11'],
'resources': {'network': 'network-2'}}]
self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
@@ -359,7 +363,7 @@
admin_accounts = [x for x in self.test_accounts if 'test_admin'
in x['username']]
self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=admin_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
@@ -377,7 +381,7 @@
admin_accounts = [x for x in self.test_accounts if 'test_admin'
in x['username']]
self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=admin_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
@@ -398,7 +402,7 @@
{'username': 'test_admin1', 'tenant_name': 'test_tenant11',
'password': 'p', 'types': ['admin']}]
self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
@@ -412,7 +416,7 @@
{'username': 'test_admin1', 'tenant_name': 'test_tenant11',
'password': 'p', 'roles': [cfg.CONF.identity.admin_role]}]
self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
@@ -423,7 +427,7 @@
non_admin_accounts = [x for x in self.test_accounts if 'test_admin'
not in x['username']]
self.useFixture(fixtures.MockPatch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
+ 'tempest.lib.common.preprov_creds.read_accounts_yaml',
return_value=non_admin_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
@@ -436,6 +440,7 @@
fixed_params = {'name': 'test class',
'identity_version': 'v3',
+ 'identity_uri': 'fake_uri',
'test_accounts_file': 'fake_accounts_file',
'accounts_lock_dir': 'fake_locks_dir_v3',
'admin_role': 'admin',
diff --git a/tempest/tests/lib/services/base.py b/tempest/tests/lib/services/base.py
index 778c966..924f9f2 100644
--- a/tempest/tests/lib/services/base.py
+++ b/tempest/tests/lib/services/base.py
@@ -32,6 +32,7 @@
def check_service_client_function(self, function, function2mock,
body, to_utf=False, status=200,
headers=None, mock_args=None,
+ resp_as_string=False,
**kwargs):
"""Mock a service client function for unit testing.
@@ -53,6 +54,9 @@
``assert_called_once_with(foo='bar')`` is called.
* If mock_args='foo' then ``assert_called_once_with('foo')``
is called.
+ :param resp_as_string: Whether response body is retruned as string.
+ This is for service client methods which return ResponseBodyData
+ object.
:param kwargs: kwargs that are passed to function.
"""
mocked_response = self.create_response(body, to_utf, status, headers)
@@ -62,8 +66,9 @@
resp = function(**kwargs)
else:
resp = function()
+ if resp_as_string:
+ resp = resp.data
self.assertEqual(body, resp)
-
if isinstance(mock_args, list):
fixture.mock.assert_called_once_with(*mock_args)
elif isinstance(mock_args, dict):
diff --git a/tempest/tests/lib/services/compute/test_services_client.py b/tempest/tests/lib/services/compute/test_services_client.py
index 41da39c..2dd981c 100644
--- a/tempest/tests/lib/services/compute/test_services_client.py
+++ b/tempest/tests/lib/services/compute/test_services_client.py
@@ -14,6 +14,9 @@
import copy
+import mock
+
+from tempest.lib.services.compute import base_compute_client
from tempest.lib.services.compute import services_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -44,11 +47,21 @@
}
}
+ FAKE_UPDATE_FORCED_DOWN = {
+ "service":
+ {
+ "forced_down": True,
+ "binary": "nova-conductor",
+ "host": "controller"
+ }
+ }
+
def setUp(self):
super(TestServicesClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
self.client = services_client.ServicesClient(
fake_auth, 'compute', 'regionOne')
+ self.addCleanup(mock.patch.stopall)
def test_list_services_with_str_body(self):
self.check_service_client_function(
@@ -68,7 +81,7 @@
'tempest.lib.common.rest_client.RestClient.put',
self.FAKE_SERVICE,
bytes_body,
- host_name="nova-conductor", binary="controller")
+ host="nova-conductor", binary="controller")
def test_enable_service_with_str_body(self):
self._test_enable_service()
@@ -85,10 +98,49 @@
'tempest.lib.common.rest_client.RestClient.put',
fake_service,
bytes_body,
- host_name="nova-conductor", binary="controller")
+ host="nova-conductor", binary="controller")
def test_disable_service_with_str_body(self):
self._test_disable_service()
def test_disable_service_with_bytes_body(self):
self._test_disable_service(bytes_body=True)
+
+ def _test_log_reason_disabled_service(self, bytes_body=False):
+ resp_body = copy.deepcopy(self.FAKE_SERVICE)
+ resp_body['service']['disabled_reason'] = 'test reason'
+
+ self.check_service_client_function(
+ self.client.disable_log_reason,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ resp_body,
+ bytes_body,
+ host="nova-conductor",
+ binary="controller",
+ disabled_reason='test reason')
+
+ def test_log_reason_disabled_service_with_str_body(self):
+ self._test_log_reason_disabled_service()
+
+ def test_log_reason_disabled_service_with_bytes_body(self):
+ self._test_log_reason_disabled_service(bytes_body=True)
+
+ def _test_update_forced_down(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_forced_down,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_UPDATE_FORCED_DOWN,
+ bytes_body,
+ host="nova-conductor",
+ binary="controller",
+ forced_down=True)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.11'))
+ def test_update_forced_down_with_str_body(self, _):
+ self._test_update_forced_down()
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.11'))
+ def test_update_forced_down_with_bytes_body(self, _):
+ self._test_update_forced_down(bytes_body=True)
diff --git a/tempest/tests/lib/services/identity/v3/test_catalog_client.py b/tempest/tests/lib/services/identity/v3/test_catalog_client.py
new file mode 100644
index 0000000..0ac8fe4
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_catalog_client.py
@@ -0,0 +1,86 @@
+# 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.identity.v3 import catalog_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestCatalogClient(base.BaseServiceTest):
+ FAKE_CATALOG_INFO = {
+ 'catalog': [
+ {
+ 'endpoints': [
+ {
+ 'id': '39dc322ce86c4111b4f06c2eeae0841b',
+ 'interface': 'public',
+ 'region': 'RegionOne',
+ 'url': 'http://localhost:5000'
+ },
+ ],
+ 'id': 'ac58672276f848a7b1727850b3ebe826',
+ 'type': 'compute',
+ 'name': 'nova'
+ },
+ {
+ 'endpoints': [
+ {
+ 'id': '39dc322ce86c4111b4f06c2eeae0841b',
+ 'interface': 'public',
+ 'region': 'RegionOne',
+ 'url': 'http://localhost:5000'
+ },
+ ],
+ 'id': 'b7c5ed2b486a46dbb4c221499d22991c',
+ 'type': 'image',
+ 'name': 'glance'
+ },
+ {
+ 'endpoints': [
+ {
+ 'id': '39dc322ce86c4111b4f06c2eeae0841b',
+ 'interface': 'public',
+ 'region': 'RegionOne',
+ 'url': 'http://localhost:5000'
+ },
+ ],
+ 'id': '4363ae44bdf34a3981fde3b823cb9aa2',
+ 'type': 'identity',
+ 'name': 'keystone'
+ }
+
+ ],
+ 'links': {
+ 'self': 'http://localhost/identity/v3/catalog',
+ 'previous': None,
+ 'next': None
+ }
+ }
+
+ def setUp(self):
+ super(TestCatalogClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = catalog_client.CatalogClient(fake_auth, 'identity',
+ 'RegionOne')
+
+ def test_show_catalog_with_bytes_body(self):
+ self._test_show_catalog(bytes_body=True)
+
+ def test_show_catalog_with_str_body(self):
+ self._test_show_catalog()
+
+ def _test_show_catalog(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_catalog,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CATALOG_INFO,
+ bytes_body)
diff --git a/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py b/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py
index 8b034e6..c724f0a 100644
--- a/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_endpoint_groups_client.py
@@ -1,162 +1,162 @@
-# Copyright 2017 AT&T 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.lib.services.identity.v3 import endpoint_groups_client
-from tempest.tests.lib import fake_auth_provider
-from tempest.tests.lib.services import base
-
-
-class TestEndPointGroupsClient(base.BaseServiceTest):
- FAKE_CREATE_ENDPOINT_GROUP = {
- "endpoint_group": {
- "id": 1,
- "name": "FAKE_ENDPOINT_GROUP",
- "description": "FAKE SERVICE ENDPOINT GROUP",
- "filters": {
- "service_id": 1
- }
- }
- }
-
- FAKE_ENDPOINT_GROUP_INFO = {
- "endpoint_group": {
- "id": 1,
- "name": "FAKE_ENDPOINT_GROUP",
- "description": "FAKE SERVICE ENDPOINT GROUP",
- "links": {
- "self": "http://example.com/identity/v3/OS-EP-FILTER/" +
- "endpoint_groups/1"
- },
- "filters": {
- "service_id": 1
- }
- }
- }
-
- FAKE_LIST_ENDPOINT_GROUPS = {
- "endpoint_groups": [
- {
- "id": 1,
- "name": "SERVICE_GROUP1",
- "description": "FAKE SERVICE ENDPOINT GROUP",
- "links": {
- "self": "http://example.com/identity/v3/OS-EP-FILTER/" +
- "endpoint_groups/1"
- },
- "filters": {
- "service_id": 1
- }
- },
- {
- "id": 2,
- "name": "SERVICE_GROUP2",
- "description": "FAKE SERVICE ENDPOINT GROUP",
- "links": {
- "self": "http://example.com/identity/v3/OS-EP-FILTER/" +
- "endpoint_groups/2"
- },
- "filters": {
- "service_id": 2
- }
- }
- ]
- }
-
- def setUp(self):
- super(TestEndPointGroupsClient, self).setUp()
- fake_auth = fake_auth_provider.FakeAuthProvider()
- self.client = endpoint_groups_client.EndPointGroupsClient(
- fake_auth, 'identity', 'regionOne')
-
- def _test_create_endpoint_group(self, bytes_body=False):
- self.check_service_client_function(
- self.client.create_endpoint_group,
- 'tempest.lib.common.rest_client.RestClient.post',
- self.FAKE_CREATE_ENDPOINT_GROUP,
- bytes_body,
- status=201,
- name="FAKE_ENDPOINT_GROUP",
- filters={'service_id': "1"})
-
- def _test_show_endpoint_group(self, bytes_body=False):
- self.check_service_client_function(
- self.client.show_endpoint_group,
- 'tempest.lib.common.rest_client.RestClient.get',
- self.FAKE_ENDPOINT_GROUP_INFO,
- bytes_body,
- endpoint_group_id="1")
-
- def _test_check_endpoint_group(self, bytes_body=False):
- self.check_service_client_function(
- self.client.check_endpoint_group,
- 'tempest.lib.common.rest_client.RestClient.head',
- {},
- bytes_body,
- status=200,
- endpoint_group_id="1")
-
- def _test_update_endpoint_group(self, bytes_body=False):
- self.check_service_client_function(
- self.client.update_endpoint_group,
- 'tempest.lib.common.rest_client.RestClient.patch',
- self.FAKE_ENDPOINT_GROUP_INFO,
- bytes_body,
- endpoint_group_id="1",
- name="NewName")
-
- def _test_list_endpoint_groups(self, bytes_body=False):
- self.check_service_client_function(
- self.client.list_endpoint_groups,
- 'tempest.lib.common.rest_client.RestClient.get',
- self.FAKE_LIST_ENDPOINT_GROUPS,
- bytes_body)
-
- def test_create_endpoint_group_with_str_body(self):
- self._test_create_endpoint_group()
-
- def test_create_endpoint_group_with_bytes_body(self):
- self._test_create_endpoint_group(bytes_body=True)
-
- def test_show_endpoint_group_with_str_body(self):
- self._test_show_endpoint_group()
-
- def test_show_endpoint_group_with_bytes_body(self):
- self._test_show_endpoint_group(bytes_body=True)
-
- def test_check_endpoint_group_with_str_body(self):
- self._test_check_endpoint_group()
-
- def test_check_endpoint_group_with_bytes_body(self):
- self._test_check_endpoint_group(bytes_body=True)
-
- def test_list_endpoint_groups_with_str_body(self):
- self._test_list_endpoint_groups()
-
- def test_list_endpoint_groups_with_bytes_body(self):
- self._test_list_endpoint_groups(bytes_body=True)
-
- def test_update_endpoint_group_with_str_body(self):
- self._test_update_endpoint_group()
-
- def test_update_endpoint_group_with_bytes_body(self):
- self._test_update_endpoint_group(bytes_body=True)
-
- def test_delete_endpoint_group(self):
- self.check_service_client_function(
- self.client.delete_endpoint_group,
- 'tempest.lib.common.rest_client.RestClient.delete',
- {},
- endpoint_group_id="1",
- status=204)
+# Copyright 2017 AT&T 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.lib.services.identity.v3 import endpoint_groups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestEndPointGroupsClient(base.BaseServiceTest):
+ FAKE_CREATE_ENDPOINT_GROUP = {
+ "endpoint_group": {
+ "id": 1,
+ "name": "FAKE_ENDPOINT_GROUP",
+ "description": "FAKE SERVICE ENDPOINT GROUP",
+ "filters": {
+ "service_id": 1
+ }
+ }
+ }
+
+ FAKE_ENDPOINT_GROUP_INFO = {
+ "endpoint_group": {
+ "id": 1,
+ "name": "FAKE_ENDPOINT_GROUP",
+ "description": "FAKE SERVICE ENDPOINT GROUP",
+ "links": {
+ "self": "http://example.com/identity/v3/OS-EP-FILTER/" +
+ "endpoint_groups/1"
+ },
+ "filters": {
+ "service_id": 1
+ }
+ }
+ }
+
+ FAKE_LIST_ENDPOINT_GROUPS = {
+ "endpoint_groups": [
+ {
+ "id": 1,
+ "name": "SERVICE_GROUP1",
+ "description": "FAKE SERVICE ENDPOINT GROUP",
+ "links": {
+ "self": "http://example.com/identity/v3/OS-EP-FILTER/" +
+ "endpoint_groups/1"
+ },
+ "filters": {
+ "service_id": 1
+ }
+ },
+ {
+ "id": 2,
+ "name": "SERVICE_GROUP2",
+ "description": "FAKE SERVICE ENDPOINT GROUP",
+ "links": {
+ "self": "http://example.com/identity/v3/OS-EP-FILTER/" +
+ "endpoint_groups/2"
+ },
+ "filters": {
+ "service_id": 2
+ }
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestEndPointGroupsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = endpoint_groups_client.EndPointGroupsClient(
+ fake_auth, 'identity', 'regionOne')
+
+ def _test_create_endpoint_group(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_endpoint_group,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_ENDPOINT_GROUP,
+ bytes_body,
+ status=201,
+ name="FAKE_ENDPOINT_GROUP",
+ filters={'service_id': "1"})
+
+ def _test_show_endpoint_group(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_endpoint_group,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_ENDPOINT_GROUP_INFO,
+ bytes_body,
+ endpoint_group_id="1")
+
+ def _test_check_endpoint_group(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.check_endpoint_group,
+ 'tempest.lib.common.rest_client.RestClient.head',
+ {},
+ bytes_body,
+ status=200,
+ endpoint_group_id="1")
+
+ def _test_update_endpoint_group(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_endpoint_group,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_ENDPOINT_GROUP_INFO,
+ bytes_body,
+ endpoint_group_id="1",
+ name="NewName")
+
+ def _test_list_endpoint_groups(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_endpoint_groups,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ENDPOINT_GROUPS,
+ bytes_body)
+
+ def test_create_endpoint_group_with_str_body(self):
+ self._test_create_endpoint_group()
+
+ def test_create_endpoint_group_with_bytes_body(self):
+ self._test_create_endpoint_group(bytes_body=True)
+
+ def test_show_endpoint_group_with_str_body(self):
+ self._test_show_endpoint_group()
+
+ def test_show_endpoint_group_with_bytes_body(self):
+ self._test_show_endpoint_group(bytes_body=True)
+
+ def test_check_endpoint_group_with_str_body(self):
+ self._test_check_endpoint_group()
+
+ def test_check_endpoint_group_with_bytes_body(self):
+ self._test_check_endpoint_group(bytes_body=True)
+
+ def test_list_endpoint_groups_with_str_body(self):
+ self._test_list_endpoint_groups()
+
+ def test_list_endpoint_groups_with_bytes_body(self):
+ self._test_list_endpoint_groups(bytes_body=True)
+
+ def test_update_endpoint_group_with_str_body(self):
+ self._test_update_endpoint_group()
+
+ def test_update_endpoint_group_with_bytes_body(self):
+ self._test_update_endpoint_group(bytes_body=True)
+
+ def test_delete_endpoint_group(self):
+ self.check_service_client_function(
+ self.client.delete_endpoint_group,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ endpoint_group_id="1",
+ status=204)
diff --git a/tempest/tests/lib/services/volume/v2/test_extensions_client.py b/tempest/tests/lib/services/volume/v2/test_extensions_client.py
new file mode 100644
index 0000000..c0ee421
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_extensions_client.py
@@ -0,0 +1,70 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# 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.lib.services.volume.v2 import extensions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestExtensionsClient(base.BaseServiceTest):
+
+ FAKE_EXTENSION_LIST = {
+ "extensions": [
+ {
+ "updated": "2012-03-12T00:00:00+00:00",
+ "name": "QuotaClasses",
+ "links": [],
+ "namespace": "fake-namespace-1",
+ "alias": "os-quota-class-sets",
+ "description": "Quota classes management support."
+ },
+ {
+ "updated": "2013-05-29T00:00:00+00:00",
+ "name": "VolumeTransfer",
+ "links": [],
+ "namespace": "fake-namespace-2",
+ "alias": "os-volume-transfer",
+ "description": "Volume transfer management support."
+ },
+ {
+ "updated": "2014-02-10T00:00:00+00:00",
+ "name": "VolumeManage",
+ "links": [],
+ "namespace": "fake-namespace-3",
+ "alias": "os-volume-manage",
+ "description": "Manage existing backend storage by Cinder."
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestExtensionsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = extensions_client.ExtensionsClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_list_extensions(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_extensions,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_EXTENSION_LIST,
+ bytes_body)
+
+ def test_list_extensions_with_str_body(self):
+ self._test_list_extensions()
+
+ def test_list_extensions_with_bytes_body(self):
+ self._test_list_extensions(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_limits_client.py b/tempest/tests/lib/services/volume/v2/test_limits_client.py
new file mode 100644
index 0000000..202054c
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_limits_client.py
@@ -0,0 +1,59 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# 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.lib.services.volume.v2 import limits_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestLimitsClient(base.BaseServiceTest):
+
+ FAKE_LIMIT_INFO = {
+ "limits": {
+ "rate": [],
+ "absolute": {
+ "totalSnapshotsUsed": 0,
+ "maxTotalBackups": 10,
+ "maxTotalVolumeGigabytes": 1000,
+ "maxTotalSnapshots": 10,
+ "maxTotalBackupGigabytes": 1000,
+ "totalBackupGigabytesUsed": 0,
+ "maxTotalVolumes": 10,
+ "totalVolumesUsed": 0,
+ "totalBackupsUsed": 0,
+ "totalGigabytesUsed": 0
+ }
+ }
+ }
+
+ def setUp(self):
+ super(TestLimitsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = limits_client.LimitsClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_show_limits(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_limits,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIMIT_INFO,
+ bytes_body)
+
+ def test_show_limits_with_str_body(self):
+ self._test_show_limits()
+
+ def test_show_limits_with_bytes_body(self):
+ self._test_show_limits(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py b/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
new file mode 100644
index 0000000..3fe8970
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
@@ -0,0 +1,83 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.services.volume.v2 import snapshot_manage_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSnapshotManageClient(base.BaseServiceTest):
+
+ SNAPSHOT_MANAGE_REQUEST = {
+ "snapshot": {
+ "description": "snapshot-manage-description",
+ "metadata": None,
+ "ref": {
+ "source-name": "_snapshot-22b71da0-94f9-4aca-ad45-7522b3fa96bb"
+ },
+ "name": "snapshot-managed",
+ "volume_id": "7c064b34-1e4b-40bd-93ca-4ac5a973661b"
+ }
+ }
+
+ SNAPSHOT_MANAGE_RESPONSE = {
+ "snapshot": {
+ "status": "creating",
+ "description": "snapshot-manage-description",
+ "updated_at": None,
+ "volume_id": "32bafcc8-7109-42cd-8342-70d8de2bedef",
+ "id": "8fd6eb9d-0a82-456d-b1ec-dea4ac7f1ee2",
+ "size": 1,
+ "name": "snapshot-managed",
+ "created_at": "2017-07-11T10:07:58.000000",
+ "metadata": {}
+ }
+ }
+
+ def setUp(self):
+ super(TestSnapshotManageClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = snapshot_manage_client.SnapshotManageClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_manage_snapshot(self, bytes_body=False):
+ payload = json.dumps(self.SNAPSHOT_MANAGE_REQUEST, sort_keys=True)
+ json_dumps = json.dumps
+
+ # NOTE: Use sort_keys for json.dumps so that the expected and actual
+ # payloads are guaranteed to be identical for mock_args assert check.
+ with mock.patch.object(snapshot_manage_client.json,
+ 'dumps') as mock_dumps:
+ mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+ self.check_service_client_function(
+ self.client.manage_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.SNAPSHOT_MANAGE_RESPONSE,
+ to_utf=bytes_body,
+ status=202,
+ mock_args=['os-snapshot-manage', payload],
+ **self.SNAPSHOT_MANAGE_REQUEST['snapshot'])
+
+ def test_manage_snapshot_with_str_body(self):
+ self._test_manage_snapshot()
+
+ def test_manage_snapshot_with_bytes_body(self):
+ self._test_manage_snapshot(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py b/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
new file mode 100644
index 0000000..ea4a9f9
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
@@ -0,0 +1,111 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.services.volume.v2 import volume_manage_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVolumeManageClient(base.BaseServiceTest):
+
+ VOLUME_MANAGE_REQUEST = {
+ "volume": {
+ "host": "controller1@rbd#rbd",
+ "name": "volume-managed",
+ "availability_zone": "nova",
+ "bootable": False,
+ "metadata": None,
+ "ref": {
+ "source-name": "volume-2ce6ca46-e6c1-4fe5-8268-3a1c536fcbf3"
+ },
+ "volume_type": None,
+ "description": "volume-manage-description"
+ }
+ }
+
+ VOLUME_MANAGE_RESPONSE = {
+ "volume": {
+ "migration_status": None,
+ "attachments": [],
+ "links": [
+ {
+ "href": "fake-url-1",
+ "rel": "self"
+ },
+ {
+ "href": "fake-url-2",
+ "rel": "bookmark"
+ }
+ ],
+ "availability_zone": "nova",
+ "os-vol-host-attr:host": "controller1@rbd#rbd",
+ "encrypted": False,
+ "updated_at": None,
+ "replication_status": None,
+ "snapshot_id": None,
+ "id": "c07cd4a4-b52b-4511-a176-fbaa2011a227",
+ "size": 0,
+ "user_id": "142d8663efce464c89811c63e45bd82e",
+ "os-vol-tenant-attr:tenant_id": "f21a9c86d7114bf99c711f4874d80474",
+ "os-vol-mig-status-attr:migstat": None,
+ "metadata": {},
+ "status": "creating",
+ "description": "volume-manage-description",
+ "multiattach": False,
+ "source_volid": None,
+ "consistencygroup_id": None,
+ "os-vol-mig-status-attr:name_id": None,
+ "name": "volume-managed",
+ "bootable": "false",
+ "created_at": "2017-07-11T09:14:01.000000",
+ "volume_type": None
+ }
+ }
+
+ def setUp(self):
+ super(TestVolumeManageClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = volume_manage_client.VolumeManageClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_manage_volume(self, bytes_body=False):
+ payload = json.dumps(self.VOLUME_MANAGE_REQUEST, sort_keys=True)
+ json_dumps = json.dumps
+
+ # NOTE: Use sort_keys for json.dumps so that the expected and actual
+ # payloads are guaranteed to be identical for mock_args assert check.
+ with mock.patch.object(volume_manage_client.json,
+ 'dumps') as mock_dumps:
+ mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+ self.check_service_client_function(
+ self.client.manage_volume,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.VOLUME_MANAGE_RESPONSE,
+ to_utf=bytes_body,
+ status=202,
+ mock_args=['os-volume-manage', payload],
+ **self.VOLUME_MANAGE_REQUEST['volume'])
+
+ def test_manage_volume_with_str_body(self):
+ self._test_manage_volume()
+
+ def test_manage_volume_with_bytes_body(self):
+ self._test_manage_volume(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_volumes_client.py b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
index befb1f6..e53e0a2 100644
--- a/tempest/tests/lib/services/volume/v2/test_volumes_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
@@ -33,6 +33,22 @@
'volume',
'regionOne')
+ def _test_retype_volume(self, bytes_body=False):
+ kwargs = {
+ "new_type": "dedup-tier-replication",
+ "migration_policy": "never"
+ }
+
+ self.check_service_client_function(
+ self.client.retype_volume,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {},
+ to_utf=bytes_body,
+ status=202,
+ volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+ **kwargs
+ )
+
def _test_force_detach_volume(self, bytes_body=False):
kwargs = {
'attachment_id': '6980e295-920f-412e-b189-05c50d605acd',
@@ -71,3 +87,9 @@
def test_show_volume_metadata_item_with_bytes_body(self):
self._test_show_volume_metadata_item(bytes_body=True)
+
+ def test_retype_volume_with_str_body(self):
+ self._test_retype_volume()
+
+ def test_retype_volume_with_bytes_body(self):
+ self._test_retype_volume(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_group_types_client.py b/tempest/tests/lib/services/volume/v3/test_group_types_client.py
index 95498c7..0f456a2 100644
--- a/tempest/tests/lib/services/volume/v3/test_group_types_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_group_types_client.py
@@ -27,6 +27,46 @@
}
}
+ FAKE_INFO_GROUP_TYPE = {
+ "group_type": {
+ "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+ "name": "group-type-001",
+ "description": "Test group type 1",
+ "is_public": True,
+ "created_at": "20127-06-20T03:50:07Z",
+ "group_specs": {},
+ }
+ }
+
+ FAKE_LIST_GROUP_TYPES = {
+ "group_types": [
+ {
+ "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+ "name": "group-type-001",
+ "description": "Test group type 1",
+ "is_public": True,
+ "created_at": "2017-06-20T03:50:07Z",
+ "group_specs": {},
+ },
+ {
+ "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+ "name": "group-type-002",
+ "description": "Test group type 2",
+ "is_public": True,
+ "created_at": "2017-06-19T01:52:47Z",
+ "group_specs": {},
+ },
+ {
+ "id": "c5c4769e-213c-40a6-a568-8e797bb691d4",
+ "name": "group-type-003",
+ "description": "Test group type 3",
+ "is_public": True,
+ "created_at": "2017-06-18T06:34:32Z",
+ "group_specs": {},
+ }
+ ]
+ }
+
def setUp(self):
super(TestGroupTypesClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -42,6 +82,21 @@
bytes_body,
status=202)
+ def _test_show_group_type(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_group_type,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_INFO_GROUP_TYPE,
+ bytes_body,
+ group_type_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+ def _test_list_group_types(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_group_types,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_GROUP_TYPES,
+ bytes_body)
+
def test_create_group_type_with_str_body(self):
self._test_create_group_type()
@@ -55,3 +110,15 @@
{},
group_type_id='0e58433f-d108-4bf3-a22c-34e6b71ef86b',
status=202)
+
+ def test_show_group_type_with_str_body(self):
+ self._test_show_group_type()
+
+ def test_show_group_type_with_bytes_body(self):
+ self._test_show_group_type(bytes_body=True)
+
+ def test_list_group_types_with_str_body(self):
+ self._test_list_group_types()
+
+ def test_list_group_types_with_bytes_body(self):
+ self._test_list_group_types(bytes_body=True)
diff --git a/tempest/tests/services/object_storage/test_bulk_middleware_client.py b/tempest/tests/services/object_storage/test_bulk_middleware_client.py
new file mode 100644
index 0000000..163b48e
--- /dev/null
+++ b/tempest/tests/services/object_storage/test_bulk_middleware_client.py
@@ -0,0 +1,66 @@
+# Copyright 2017 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.services.object_storage import bulk_middleware_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestBulkMiddlewareClient(base.BaseServiceTest):
+
+ def setUp(self):
+ super(TestBulkMiddlewareClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = bulk_middleware_client.BulkMiddlewareClient(
+ fake_auth, 'object-storage', 'regionOne')
+
+ def test_upload_archive(self):
+ url = 'test_path?extract-archive=tar'
+ data = 'test_data'
+ self.check_service_client_function(
+ self.client.upload_archive,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ {},
+ mock_args=[url, data, {}],
+ resp_as_string=True,
+ upload_path='test_path', data=data, archive_file_format='tar')
+
+ def test_delete_bulk_data(self):
+ url = '?bulk-delete'
+ data = 'test_data'
+ self.check_service_client_function(
+ self.client.delete_bulk_data,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ mock_args=[url, {}, data],
+ resp_as_string=True,
+ data=data)
+
+ def _test_delete_bulk_data_with_post(self, status):
+ url = '?bulk-delete'
+ data = 'test_data'
+ self.check_service_client_function(
+ self.client.delete_bulk_data_with_post,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {},
+ mock_args=[url, data, {}],
+ resp_as_string=True,
+ status=status,
+ data=data)
+
+ def test_delete_bulk_data_with_post_200(self):
+ self._test_delete_bulk_data_with_post(200)
+
+ def test_delete_bulk_data_with_post_204(self):
+ self._test_delete_bulk_data_with_post(204)
diff --git a/tempest/tests/services/object_storage/test_capabilities_client.py b/tempest/tests/services/object_storage/test_capabilities_client.py
new file mode 100644
index 0000000..5279bf4
--- /dev/null
+++ b/tempest/tests/services/object_storage/test_capabilities_client.py
@@ -0,0 +1,54 @@
+# Copyright 2016 IBM Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from tempest.services.object_storage import capabilities_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestCapabilitiesClient(base.BaseServiceTest):
+
+ def setUp(self):
+ super(TestCapabilitiesClient, self).setUp()
+ self.fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.url = self.fake_auth.base_url(None)
+ self.client = capabilities_client.CapabilitiesClient(
+ self.fake_auth, 'swift', 'region1')
+
+ def _test_list_capabilities(self, bytes_body=False):
+ resp = {
+ "swift": {
+ "version": "1.11.0"
+ },
+ "slo": {
+ "max_manifest_segments": 1000,
+ "max_manifest_size": 2097152,
+ "min_segment_size": 1
+ },
+ "staticweb": {},
+ "tempurl": {}
+ }
+ self.check_service_client_function(
+ self.client.list_capabilities,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ resp,
+ bytes_body)
+
+ def test_list_capabilities_with_str_body(self):
+ self._test_list_capabilities()
+
+ def test_list_capabilities_with_bytes_body(self):
+ self._test_list_capabilities(True)
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index 01b8a72..6c6f612 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -16,8 +16,8 @@
from tempest import clients
from tempest.common import credentials_factory as credentials
-from tempest.common import fixed_network
from tempest import config
+from tempest.lib.common import fixed_network
from tempest import test
from tempest.tests import base
from tempest.tests import fake_config
diff --git a/tox.ini b/tox.ini
index 2120818..6f37d00 100644
--- a/tox.ini
+++ b/tox.ini
@@ -20,7 +20,7 @@
PYTHONWARNINGS=default::DeprecationWarning
BRANCH_NAME=master
CLIENT_NAME=tempest
-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 ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION
+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 ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION GENERATE_TEMPEST_PLUGIN_LIST
usedevelop = True
install_command =
{toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}