Merge "Bump hacking to 1.1.0"
diff --git a/.gitignore b/.gitignore
index 7cb052f..06a2281 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,9 +3,8 @@
*.pyc
__pycache__/
etc/accounts.yaml
-etc/tempest.conf
+etc/*.conf
etc/tempest.conf.sample
-etc/logging.conf
include/swift_objects/swift_small
include/swift_objects/swift_medium
include/swift_objects/swift_large
diff --git a/.zuul.yaml b/.zuul.yaml
index fd3aa2a..52c11c9 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -9,10 +9,10 @@
test setup. To run a multi-node test inherit from devstack-tempest and
set the nodeset to a multi-node one.
required-projects:
- - openstack/tempest
+ - git.openstack.org/openstack/tempest
timeout: 7200
roles:
- - zuul: openstack-dev/devstack
+ - zuul: git.openstack.org/openstack-dev/devstack
vars:
devstack_services:
tempest: true
@@ -101,13 +101,14 @@
- master
description: |
Base multinode integration test with Neutron networking and py27.
- Former names for this job where:
+ Former names for this job were:
* neutron-tempest-multinode-full
* legacy-tempest-dsvm-neutron-multinode-full
* gate-tempest-dsvm-neutron-multinode-full-ubuntu-xenial-nv
This job includes two nodes, controller / tempest plus a subnode, but
it can be used with different topologies, as long as a controller node
and a tempest one exist.
+ timeout: 10800
vars:
tox_envlist: full
devstack_localrc:
@@ -142,21 +143,25 @@
Base integration test with Neutron networking and py36.
voting: false
-# TODO(gmann): needs to migrate this to zuulv3
- job:
- name: tempest-scenario-all
- parent: legacy-dsvm-base-multinode
+ name: tempest-slow
+ parent: tempest-multinode-full
+ branches:
+ - master
description: |
- This job will run all scenario tests including slow tests
- with lvm multibackend setup. This job will not run any API tests.
- run: playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
- post-run: playbooks/tempest-scenario-multinode-lvm-multibackend/post.yaml
+ This multinode integration job will run all the tests tagged as slow.
+ It enables the lvm multibackend setup to cover few scenario tests.
+ This job will run only slow tests(API or Scenario) serially.
+
+ Former names for this job were:
+ * legacy-tempest-dsvm-neutron-scenario-multinode-lvm-multibackend
+ * tempest-scenario-multinode-lvm-multibackend
timeout: 10800
- required-projects:
- - openstack-infra/devstack-gate
- - openstack/neutron
- - openstack/tempest
- nodeset: ubuntu-xenial-2-node
+ vars:
+ tox_envlist: slow-serial
+ devstack_localrc:
+ CINDER_ENABLED_BACKENDS: lvm:lvmdriver-1,lvm:lvmdriver-2
+ tempest_concurrency: 2
- job:
name: tempest-full-queens
@@ -191,70 +196,70 @@
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
required-projects:
- - openstack/almanach
- - openstack/aodh
- - openstack/barbican-tempest-plugin
- - openstack/ceilometer
- - openstack/cinder
- - openstack/congress
- - openstack/designate-tempest-plugin
- - openstack/ec2-api
- - openstack/freezer
- - openstack/freezer-api
- - openstack/freezer-tempest-plugin
- - openstack/gce-api
- - openstack/glare
- - openstack/heat
- - openstack/intel-nfv-ci-tests
- - openstack/ironic
- - openstack/ironic-inspector
- - openstack/keystone-tempest-plugin
- - openstack/kingbird
- - openstack/kuryr-tempest-plugin
- - openstack/magnum
- - openstack/magnum-tempest-plugin
- - openstack/manila
- - openstack/manila-tempest-plugin
- - openstack/mistral
- - openstack/mogan
- - openstack/monasca-api
- - openstack/monasca-log-api
- - openstack/murano
- - openstack/networking-bgpvpn
- - openstack/networking-cisco
- - openstack/networking-fortinet
- - openstack/networking-generic-switch
- - openstack/networking-l2gw
- - openstack/networking-midonet
- - openstack/networking-plumgrid
- - openstack/networking-sfc
- - openstack/neutron
- - openstack/neutron-dynamic-routing
- - openstack/neutron-fwaas
- - openstack/neutron-lbaas
- - openstack/neutron-tempest-plugin
- - openstack/neutron-vpnaas
- - openstack/nova-lxd
- - openstack/novajoin-tempest-plugin
- - openstack/octavia-tempest-plugin
- - openstack/oswin-tempest-plugin
- - openstack/panko
- - openstack/patrole
- - openstack/qinling
- - openstack/requirements
- - openstack/sahara-tests
- - openstack/senlin
- - openstack/senlin-tempest-plugin
- - openstack/tap-as-a-service
- - openstack/tempest-horizon
- - openstack/trio2o
- - openstack/trove
- - openstack/valet
- - openstack/vitrage
- - openstack/vmware-nsx-tempest-plugin
- - openstack/watcher-tempest-plugin
- - openstack/zaqar-tempest-plugin
- - openstack/zun-tempest-plugin
+ - git.openstack.org/openstack/almanach
+ - git.openstack.org/openstack/aodh
+ - git.openstack.org/openstack/barbican-tempest-plugin
+ - git.openstack.org/openstack/ceilometer
+ - git.openstack.org/openstack/cinder
+ - git.openstack.org/openstack/congress
+ - git.openstack.org/openstack/designate-tempest-plugin
+ - git.openstack.org/openstack/ec2-api
+ - git.openstack.org/openstack/freezer
+ - git.openstack.org/openstack/freezer-api
+ - git.openstack.org/openstack/freezer-tempest-plugin
+ - git.openstack.org/openstack/gce-api
+ - git.openstack.org/openstack/glare
+ - git.openstack.org/openstack/heat
+ - git.openstack.org/openstack/intel-nfv-ci-tests
+ - git.openstack.org/openstack/ironic
+ - git.openstack.org/openstack/ironic-inspector
+ - git.openstack.org/openstack/keystone-tempest-plugin
+ - git.openstack.org/openstack/kingbird
+ - git.openstack.org/openstack/kuryr-tempest-plugin
+ - git.openstack.org/openstack/magnum
+ - git.openstack.org/openstack/magnum-tempest-plugin
+ - git.openstack.org/openstack/manila
+ - git.openstack.org/openstack/manila-tempest-plugin
+ - git.openstack.org/openstack/mistral
+ - git.openstack.org/openstack/mogan
+ - git.openstack.org/openstack/monasca-api
+ - git.openstack.org/openstack/monasca-log-api
+ - git.openstack.org/openstack/murano
+ - git.openstack.org/openstack/networking-bgpvpn
+ - git.openstack.org/openstack/networking-cisco
+ - git.openstack.org/openstack/networking-fortinet
+ - git.openstack.org/openstack/networking-generic-switch
+ - git.openstack.org/openstack/networking-l2gw
+ - git.openstack.org/openstack/networking-midonet
+ - git.openstack.org/openstack/networking-plumgrid
+ - git.openstack.org/openstack/networking-sfc
+ - git.openstack.org/openstack/neutron
+ - git.openstack.org/openstack/neutron-dynamic-routing
+ - git.openstack.org/openstack/neutron-fwaas
+ - git.openstack.org/openstack/neutron-lbaas
+ - git.openstack.org/openstack/neutron-tempest-plugin
+ - git.openstack.org/openstack/neutron-vpnaas
+ - git.openstack.org/openstack/nova-lxd
+ - git.openstack.org/openstack/novajoin-tempest-plugin
+ - git.openstack.org/openstack/octavia-tempest-plugin
+ - git.openstack.org/openstack/oswin-tempest-plugin
+ - git.openstack.org/openstack/panko
+ - git.openstack.org/openstack/patrole
+ - git.openstack.org/openstack/qinling
+ - git.openstack.org/openstack/requirements
+ - git.openstack.org/openstack/sahara-tests
+ - git.openstack.org/openstack/senlin
+ - git.openstack.org/openstack/senlin-tempest-plugin
+ - git.openstack.org/openstack/tap-as-a-service
+ - git.openstack.org/openstack/tempest-horizon
+ - git.openstack.org/openstack/trio2o
+ - git.openstack.org/openstack/trove
+ - git.openstack.org/openstack/valet
+ - git.openstack.org/openstack/vitrage
+ - git.openstack.org/openstack/vmware-nsx-tempest-plugin
+ - git.openstack.org/openstack/watcher-tempest-plugin
+ - git.openstack.org/openstack/zaqar-tempest-plugin
+ - git.openstack.org/openstack/zun-tempest-plugin
- job:
name: tempest-cinder-v2-api
@@ -277,7 +282,16 @@
- ^playbooks/
- ^roles/
- ^.zuul.yaml$
- - nova-multiattach
+ - nova-multiattach:
+ irrelevant-files:
+ - ^(test-|)requirements.txt$
+ - ^.*\.rst$
+ - ^doc/.*$
+ - ^etc/.*$
+ - ^releasenotes/.*$
+ - ^setup.cfg$
+ - ^tempest/hacking/.*$
+ - ^tempest/tests/.*$
- tempest-full-parallel:
irrelevant-files:
- ^(test-|)requirements.txt$
@@ -339,7 +353,7 @@
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
- tempest-tox-plugin-sanity-check
- - tempest-scenario-all:
+ - tempest-slow:
irrelevant-files:
- ^(test-|)requirements.txt$
- ^.*\.rst$
@@ -349,7 +363,6 @@
- ^setup.cfg$
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
- - ^tempest/api/.*$
- nova-cells-v1:
irrelevant-files:
- ^(test-|)requirements.txt$
@@ -373,8 +386,7 @@
- ^tempest/tests/.*$
gate:
jobs:
- - nova-multiattach
- - tempest-scenario-all:
+ - nova-multiattach:
irrelevant-files:
- ^(test-|)requirements.txt$
- ^.*\.rst$
@@ -384,7 +396,16 @@
- ^setup.cfg$
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
- - ^tempest/api/.*$
+ - tempest-slow:
+ irrelevant-files:
+ - ^(test-|)requirements.txt$
+ - ^.*\.rst$
+ - ^doc/.*$
+ - ^etc/.*$
+ - ^releasenotes/.*$
+ - ^setup.cfg$
+ - ^tempest/hacking/.*$
+ - ^tempest/tests/.*$
experimental:
jobs:
- tempest-cinder-v2-api:
diff --git a/HACKING.rst b/HACKING.rst
index 2a7ae1d..5b9c0f1 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -25,6 +25,8 @@
- [T115] Check that admin tests should exist under admin path
- [N322] Method's default argument shouldn't be mutable
- [T116] Unsupported 'message' Exception attribute in PY3
+- [T117] Check negative tests have ``@decorators.attr(type=['negative'])``
+ applied.
Test Data/Configuration
-----------------------
@@ -146,11 +148,6 @@
This attribute must be applied to each test that belongs to a negative test
class, i.e. a test class name ending with "Negative.*" substring.
-.. todo::
-
- Add a hacking check for ensuring that all classes that contain substring
- "Negative" have the negative attribute decorator applied above each test.
-
Slow Attribute
^^^^^^^^^^^^^^
The ``type='slow'`` attribute is used to signify that a test takes a long time
diff --git a/README.rst b/README.rst
index 2243536..c93e19f 100644
--- a/README.rst
+++ b/README.rst
@@ -95,10 +95,12 @@
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
+ `Tempest Configuration <https://docs.openstack.org/tempest/latest/configuration.html#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
+ be done using the `Tempest Run <https://docs.openstack.org/tempest/latest/run.html#tempest-run>`_
+ command. This can be done by either
running::
$ tempest run
@@ -129,7 +131,8 @@
stable interface and there are no guarantees on the Python API unless otherwise
stated.
-For more details refer to the library documentation here: :ref:`library`
+For more details refer to the `library documentation
+<https://docs.openstack.org/tempest/latest/library.html#library>`_
Release Versioning
------------------
@@ -165,8 +168,10 @@
-------------
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
+document, see `Tempest Configuration Documentation
+<https://docs.openstack.org/tempest/latest/configuration.html#tempest-configuration>`_
+for more details on configuring 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
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index d0d7320..2e5f706 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -172,7 +172,7 @@
resize test).
Using a smaller flavor is generally recommended. When larger flavors are used,
-the extra time required to bring up servers will likely affect total run time
+the extra time required to bring up servers will likely affect the total run time
and probably require tweaking timeout values to ensure tests have ample time to
finish.
@@ -207,7 +207,7 @@
The behavior of these options is a bit convoluted (which will likely be fixed in
future versions). You first need to specify ``img_dir``, which is the directory
-in which Tempest will look for the image files. First it will check if the
+in which Tempest will look for the image files. First, it will check if the
filename set for ``img_file`` could be found in ``img_dir``. If it is found then
the ``img_container_format`` and ``img_disk_format`` options are used to upload
that image to glance. However, if it is not found, Tempest will look for the
@@ -239,7 +239,7 @@
""""""""""""""""""""""""""""""""""
When Tempest creates servers for testing, some tests require being able to
connect those servers. Depending on the configuration of the cloud, the methods
-for doing this can be different. In certain configurations it is required to
+for doing this can be different. In certain configurations, it is required to
specify a single network with server create calls. Accordingly, Tempest provides
a few different methods for providing this information in configuration to try
and ensure that regardless of the cloud's configuration it'll still be able to
@@ -297,10 +297,10 @@
''''''''''''''''''''''''
With dynamic credentials enabled and using nova-network, your only option for
configuration is to either set a fixed network name or not. However, in most
-cases it shouldn't matter because nova-network should have no problem booting a
+cases, it shouldn't matter because nova-network should have no problem booting a
server with multiple networks. If this is not the case for your cloud then using
an accounts file is recommended because it provides the necessary flexibility to
-describe your configuration. Dynamic credentials is not able to dynamically
+describe your configuration. Dynamic credentials are not able to dynamically
allocate things as necessary if Neutron is not enabled.
With Neutron and dynamic credentials enabled there should not be any additional
@@ -352,7 +352,7 @@
OpenStack is really a constellation of several different projects which
are running together to create a cloud. However which projects you're running
is not set in stone, and which services are running is up to the deployer.
-Tempest however needs to know which services are available so it can figure
+Tempest, however, needs to know which services are available so it can figure
out which tests it is able to run and certain setup steps which differ based
on the available services.
@@ -390,8 +390,8 @@
.. 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
+ 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).
Tempest expects URLs in the Service catalog in the following format:
@@ -413,10 +413,10 @@
certain operations and features aren't supported depending on the configuration.
These features may or may not be discoverable from the API so the burden is
often on the user to figure out what is supported by the cloud they're talking
-to. Besides the obvious interoperability issues with this it also leaves
+to. Besides the obvious interoperability issues with this, it also leaves
Tempest in an interesting situation trying to figure out which tests are
expected to work. However, Tempest tests do not rely on dynamic API discovery
-for a feature (assuming one exists). Instead Tempest has to be explicitly
+for a feature (assuming one exists). Instead, Tempest has to be explicitly
configured as to which optional features are enabled. This is in order to
prevent bugs in the discovery mechanisms from masking failures.
@@ -432,8 +432,8 @@
^^^^^^^^^^^^^^
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
-Tempest which api extensions (or configurable middleware) is used in your
+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
+``all`` (which is the default) which means that every API extension is assumed
to be enabled, or it is set to a list of each individual extension that is
enabled for that service.
diff --git a/doc/source/data/tempest-plugins-registry.header b/doc/source/data/tempest-plugins-registry.header
index 9821e8e..0de12b7 100644
--- a/doc/source/data/tempest-plugins-registry.header
+++ b/doc/source/data/tempest-plugins-registry.header
@@ -17,7 +17,3 @@
The following are plugins that a script has found in the openstack/
namespace, which includes but is not limited to official OpenStack
projects.
-
-+----------------------------+-------------------------------------------------------------------------+
-|Plugin Name |URL |
-+----------------------------+-------------------------------------------------------------------------+
diff --git a/doc/source/library.rst b/doc/source/library.rst
index 14415ae..6a12c45 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -4,12 +4,12 @@
=============================
Tempest provides a stable library interface that provides external tools or
-test suites an interface for reusing pieces of tempest code. Any public
-interface that lives in tempest/lib in the tempest repo is treated as a stable
+test suites an interface for reusing pieces of Tempest code. Any public
+interface that lives in tempest/lib in the Tempest repo is treated as a stable
public interface and it should be safe to external consume that. Every effort
goes into maintaining backwards compatibility with any change.
The library is self contained and doesn't have any dependency on
-other tempest internals outside of lib (including no usage of tempest
+other Tempest internals outside of lib (including no usage of Tempest
configuration).
Stability
@@ -32,7 +32,7 @@
Making changes
''''''''''''''
When making changes to tempest/lib you have to be conscious of the effect of
-any changes on external consumers. If your proposed changeset will change the
+any changes on external consumers. If your proposed change set will change the
default behaviour of any interface, or make something which previously worked
not after your change, then it is not acceptable. Every effort needs to go into
preserving backwards compatibility in changes.
@@ -40,8 +40,8 @@
Reviewing
'''''''''
When reviewing a proposed change to tempest/lib code we need to be careful to
-ensure that we don't break backwards compatibility. For patches that change
-existing interfaces we have to be careful to make sure we don't break any
+ensure that we don't break backward compatibility. For patches that change
+existing interfaces, we have to be careful to make sure we don't break any
external consumers. Some common red flags are:
* a change to an existing API requires a change outside the library directory
@@ -52,7 +52,7 @@
'''''''
When adding a new interface to the library we need to at a minimum have unit
test coverage. A proposed change to add an interface to tempest/lib that
-doesn't have unit tests shouldn't be accepted. Ideally these unit tests will
+doesn't have unit tests shouldn't be accepted. Ideally, these unit tests will
provide sufficient coverage to ensure a stable interface moving forward.
Current Library APIs
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 6dd00d3..81a1cb5 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -334,14 +334,30 @@
.. _2.26: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id23
+* `2.28`_
+
+ .. _2.28: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id25
+
* `2.32`_
.. _2.32: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id29
+ * `2.36`_
+
+ .. _2.36: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#microversion
+
* `2.37`_
.. _2.37: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id34
+ * `2.39`_
+
+ .. _2.39: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id35
+
+ * `2.41`_
+
+ .. _2.41: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id37
+
* `2.42`_
.. _2.42: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-ocata
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 6f6621d..9958792 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -5,24 +5,24 @@
=============================
Tempest has an external test plugin interface which enables anyone to integrate
-an external test suite as part of a tempest run. This will let any project
-leverage being run with the rest of the tempest suite while not requiring the
-tests live in the tempest tree.
+an external test suite as part of a Tempest run. This will let any project
+leverage being run with the rest of the Tempest suite while not requiring the
+tests live in the Tempest tree.
Creating a plugin
=================
Creating a plugin is fairly straightforward and doesn't require much additional
effort on top of creating a test suite using tempest.lib. One thing to note with
-doing this is that the interfaces exposed by tempest are not considered stable
-(with the exception of configuration variables which ever effort goes into
-ensuring backwards compatibility). You should not need to import anything from
-tempest itself except where explicitly noted.
+doing this is that the interfaces exposed by Tempest are not considered stable
+(with the exception of configuration variables whichever effort goes into
+ensuring backward compatibility). You should not need to import anything from
+Tempest itself except where explicitly noted.
Stable Tempest APIs plugins may use
-----------------------------------
-As noted above, several tempest APIs are acceptable to use from plugins, while
+As noted above, several Tempest APIs are acceptable to use from plugins, while
others are not. A list of stable APIs available to plugins is provided below:
* tempest.lib.*
@@ -32,7 +32,7 @@
* tempest.clients
* tempest.test
-If there is an interface from tempest that you need to rely on in your plugin
+If there is an interface from Tempest that you need to rely on in your plugin
which is not listed above, it likely needs to be migrated to tempest.lib. In
that situation, file a bug, push a migration patch, etc. to expedite providing
the interface in a reliable manner.
@@ -62,7 +62,7 @@
-----------
Once you've created your plugin class you need to add an entry point to your
-project to enable tempest to find the plugin. The entry point must be added
+project to enable Tempest to find the plugin. The entry point must be added
to the "tempest.test_plugins" namespace.
If you are using pbr this is fairly straightforward, in the setup.cfg just add
@@ -77,9 +77,9 @@
Standalone Plugin vs In-repo Plugin
-----------------------------------
-Since all that's required for a plugin to be detected by tempest is a valid
+Since all that's required for a plugin to be detected by Tempest is a valid
setuptools entry point in the proper namespace there is no difference from the
-tempest perspective on either creating a separate python package to
+Tempest perspective on either creating a separate python package to
house the plugin or adding the code to an existing python project. However,
there are tradeoffs to consider when deciding which approach to take when
creating a new plugin.
@@ -91,9 +91,9 @@
single version of the test code across project release boundaries (see the
`Branchless Tempest Spec`_ for more details on this). It also greatly
simplifies the install time story for external users. Instead of having to
-install the right version of a project in the same python namespace as tempest
+install the right version of a project in the same python namespace as Tempest
they simply need to pip install the plugin in that namespace. It also means
-that users don't have to worry about inadvertently installing a tempest plugin
+that users don't have to worry about inadvertently installing a Tempest plugin
when they install another package.
.. _Branchless Tempest Spec: http://specs.openstack.org/openstack/qa-specs/specs/tempest/implemented/branchless-tempest.html
@@ -108,9 +108,9 @@
Plugin Class
============
-To provide tempest with all the required information it needs to be able to run
-your plugin you need to create a plugin class which tempest will load and call
-to get information when it needs. To simplify creating this tempest provides an
+To provide Tempest with all the required information it needs to be able to run
+your plugin you need to create a plugin class which Tempest will load and call
+to get information when it needs. To simplify creating this Tempest provides an
abstract class that should be used as the parent for your plugin. To use this
you would do something like the following:
@@ -147,7 +147,7 @@
services/
client.py
-That will mirror what people expect from tempest. The file
+That will mirror what people expect from Tempest. The file
* **config.py**: contains any plugin specific configuration variables
* **plugin.py**: contains the plugin class used for the entry point
@@ -156,14 +156,14 @@
* **services**: where the plugin specific service clients are
Additionally, when you're creating the plugin you likely want to follow all
-of the tempest developer and reviewer documentation to ensure that the tests
-being added in the plugin act and behave like the rest of tempest.
+of the Tempest developer and reviewer documentation to ensure that the tests
+being added in the plugin act and behave like the rest of Tempest.
Dealing with configuration options
----------------------------------
-Historically Tempest didn't provide external guarantees on its configuration
-options. However, with the introduction of the plugin interface this is no
+Historically, Tempest didn't provide external guarantees on its configuration
+options. However, with the introduction of the plugin interface, this is no
longer the case. An external plugin can rely on using any configuration option
coming from Tempest, there will be at least a full deprecation cycle for any
option before it's removed. However, just the options provided by Tempest
@@ -171,7 +171,7 @@
configuration options you should use the ``register_opts`` and
``get_opt_lists`` methods to pass them to Tempest when the plugin is loaded.
When adding configuration options the ``register_opts`` method gets passed the
-CONF object from tempest. This enables the plugin to add options to both
+CONF object from Tempest. This enables the plugin to add options to both
existing sections and also create new configuration sections for new options.
Service Clients
@@ -325,23 +325,23 @@
Tempest will automatically discover any installed plugins when it is run. So by
just installing the python packages which contain your plugin you'll be using
-them with tempest, nothing else is really required.
+them with Tempest, nothing else is really required.
However, you should take care when installing plugins. By their very nature
-there are no guarantees when running tempest with plugins enabled about the
+there are no guarantees when running Tempest with plugins enabled about the
quality of the plugin. Additionally, while there is no limitation on running
-with multiple plugins it's worth noting that poorly written plugins might not
+with multiple plugins, it's worth noting that poorly written plugins might not
properly isolate their tests which could cause unexpected cross interactions
between plugins.
Notes for using plugins with virtualenvs
----------------------------------------
-When using a tempest inside a virtualenv (like when running under tox) you have
+When using a Tempest inside a virtualenv (like when running under tox) you have
to ensure that the package that contains your plugin is either installed in the
venv too or that you have system site-packages enabled. The virtualenv will
-isolate the tempest install from the rest of your system so just installing the
-plugin package on your system and then running tempest inside a venv will not
+isolate the Tempest install from the rest of your system so just installing the
+plugin package on your system and then running Tempest inside a venv will not
work.
Tempest also exposes a tox job, all-plugin, which will setup a tox virtualenv
diff --git a/doc/source/test_removal.rst b/doc/source/test_removal.rst
index 917b014..e249bdd 100644
--- a/doc/source/test_removal.rst
+++ b/doc/source/test_removal.rst
@@ -93,7 +93,7 @@
#. paste the output table with numbers and the mysql command you ran to
generate it into the etherpad.
-Eventually a CLI interface will be created to make that a bit more friendly.
+Eventually, a CLI interface will be created to make that a bit more friendly.
Also a dashboard is in the works so we don't need to manually run the command.
The intent of the 2nd prong is to verify that moving the test into a project
@@ -104,7 +104,7 @@
blocking it from landing) and having the testing run in Tempest still has
value.
-However for the 3rd prong verification is a bit more subjective. The original
+However, for the 3rd prong verification is a bit more subjective. The original
intent of this prong was mostly for refstack/defcore and also for things that
running on the stable branches. We don't want to remove any tests if that
would break our API consistency checking between releases, or something that
@@ -138,7 +138,7 @@
Exceptions to this procedure
----------------------------
-For the most part all Tempest test removals have to go through this procedure
+For the most part, all Tempest test removals have to go through this procedure
there are a couple of exceptions though:
#. The class of testing has been decided to be outside the scope of Tempest.
@@ -150,7 +150,7 @@
Such tests cannot live in Tempest because of the branchless nature of
Tempest. Such tests must still honor `prong #3`_.
-For the first exception type the only types of testing in tree which have been
+For the first exception type, the only types of testing in the tree which have been
declared out of scope at this point are:
* The CLI tests (which should be completely removed at this point)
@@ -159,7 +159,7 @@
* XML API Tests (which should be completely removed at this point)
* EC2 API/boto tests (which should be completely removed at this point)
-For tests that fit into this category the only criteria for removal is that
+For tests that fit into this category, the only criteria for removal is that
there is equivalent testing elsewhere.
Tempest Scope
@@ -192,7 +192,7 @@
This is because tests would not be able to know or control which API response
to expect, and thus would not be able to enforce a specific behavior.
-If a test exists in Tempest that would meet this criteria as consequence of a
-change, the test must be removed according to the procedure discussed into
+If a test exists in Tempest that would meet these criteria as a consequence of a
+change, the test must be removed according to the procedure discussed in
this document. The API change should not be merged until all conditions
required for test removal can be met.
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
index fff2405..0a29b7b 100644
--- a/doc/source/write_tests.rst
+++ b/doc/source/write_tests.rst
@@ -4,7 +4,7 @@
##########################
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
+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 its role as an integration test suite running
against a real cloud.
diff --git a/playbooks/tempest-scenario-multinode-lvm-multibackend/post.yaml b/playbooks/tempest-scenario-multinode-lvm-multibackend/post.yaml
deleted file mode 100644
index e07f551..0000000
--- a/playbooks/tempest-scenario-multinode-lvm-multibackend/post.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-- hosts: primary
- tasks:
-
- - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
- synchronize:
- src: '{{ ansible_user_dir }}/workspace/'
- dest: '{{ zuul.executor.log_root }}'
- mode: pull
- copy_links: true
- verify_host: true
- rsync_opts:
- - --include=/logs/**
- - --include=*/
- - --exclude=*
- - --prune-empty-dirs
diff --git a/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml b/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
deleted file mode 100644
index 57b4074..0000000
--- a/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
+++ /dev/null
@@ -1,65 +0,0 @@
-- hosts: primary
- name: Autoconverted job tempest-scenario-multinode-lvm-multibackend
- from old job gate-tempest-dsvm-neutron-scenario-multinode-lvm-multibackend-ubuntu-xenial-nv
- tasks:
-
- - name: Ensure legacy workspace directory
- file:
- path: '{{ ansible_user_dir }}/workspace'
- state: directory
-
- - shell:
- cmd: |
- set -e
- set -x
- cat > clonemap.yaml << EOF
- clonemap:
- - name: openstack-infra/devstack-gate
- dest: devstack-gate
- EOF
- /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
- git://git.openstack.org \
- openstack-infra/devstack-gate
- executable: /bin/bash
- chdir: '{{ ansible_user_dir }}/workspace'
- environment: '{{ zuul | zuul_legacy_vars }}'
-
- - shell:
- cmd: |
- set -e
- set -x
- cat << 'EOF' >>"/tmp/dg-local.conf"
- [[local|localrc]]
- ENABLE_IDENTITY_V2=False
- TEMPEST_USE_TEST_ACCOUNTS=True
- # Enable lvm multiple backends to run multi backend slow scenario tests.
- # Note: multi backend experimental job exclude the slow scenario tests.
- CINDER_ENABLED_BACKENDS=lvm:lvmdriver-1,lvm:lvmdriver-2
-
- EOF
- executable: /bin/bash
- chdir: '{{ ansible_user_dir }}/workspace'
- environment: '{{ zuul | zuul_legacy_vars }}'
-
- - shell:
- cmd: |
- set -e
- set -x
- export PYTHONUNBUFFERED=true
- export DEVSTACK_GATE_TEMPEST=1
- # Run all scenario tests including slow tests with concurrency 2
- export DEVSTACK_GATE_TEMPEST_REGEX='(^tempest\.(scenario))'
- export TEMPEST_CONCURRENCY=2
- export DEVSTACK_GATE_NEUTRON=1
- export DEVSTACK_GATE_TLSPROXY=1
- export BRANCH_OVERRIDE=default
- if [ "$BRANCH_OVERRIDE" != "default" ] ; then
- export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
- fi
- export DEVSTACK_GATE_TOPOLOGY="multinode"
-
- cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
- ./safe-devstack-vm-gate-wrap.sh
- executable: /bin/bash
- chdir: '{{ ansible_user_dir }}/workspace'
- environment: '{{ zuul | zuul_legacy_vars }}'
diff --git a/releasenotes/notes/add-storyboard-in-skip-because-decorator-3e139aa8a4f7970f.yaml b/releasenotes/notes/add-storyboard-in-skip-because-decorator-3e139aa8a4f7970f.yaml
new file mode 100644
index 0000000..dd4a90b
--- /dev/null
+++ b/releasenotes/notes/add-storyboard-in-skip-because-decorator-3e139aa8a4f7970f.yaml
@@ -0,0 +1,17 @@
+---
+features:
+ - |
+ Add a new parameter called ``bug_type`` to
+ ``tempest.lib.decorators.related_bug`` and
+ ``tempest.lib.decorators.skip_because`` decorators, which accepts
+ 2 values:
+
+ * launchpad
+ * storyboard
+
+ This offers the possibility of tracking bugs related to tests using
+ launchpad or storyboard references. The default value is launchpad
+ for backward compatibility.
+
+ Passing in a non-digit ``bug`` value to either decorator will raise
+ a ``InvalidParam`` exception (previously ``ValueError``).
diff --git a/releasenotes/notes/cinder-use-os-endpoint-type-c11f63fd468ceb4c.yaml b/releasenotes/notes/cinder-use-os-endpoint-type-c11f63fd468ceb4c.yaml
new file mode 100644
index 0000000..1dda4e1
--- /dev/null
+++ b/releasenotes/notes/cinder-use-os-endpoint-type-c11f63fd468ceb4c.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+ - |
+ Cinder CLI calls have now been updated to use the ``--os-endpoint-type``
+ option instead of ``--endpoint-type``. The latter had been deprecated in
+ Cinder and has been removed in the Rocky release.
diff --git a/releasenotes/notes/deprecate-volume-api-selection-config-options-b95c5c0ccbf38916.yaml b/releasenotes/notes/deprecate-volume-api-selection-config-options-b95c5c0ccbf38916.yaml
new file mode 100644
index 0000000..1bea6d0
--- /dev/null
+++ b/releasenotes/notes/deprecate-volume-api-selection-config-options-b95c5c0ccbf38916.yaml
@@ -0,0 +1,19 @@
+---
+deprecations:
+ - |
+ The v2 volume API has been deprecated since Pike release.
+ Volume v3 API is current and Tempest volume tests can
+ be run against v2 or v3 API based on config option
+ ``CONF.volume.catalog_type``. If catalog_type is ``volumev2``, then
+ all the volume tests will run against v2 API. If catalog_type is
+ ``volumev3`` which is default in Tempest, then all the volume
+ tests will run against v3 API.
+ That makes below config options unusable in Tempest which used to
+ select the target volume API for volume tests.
+
+ * ``CONF.volume-feature-enabled.api_v2``
+ * ``CONF.volume-feature-enabled.api_v3``
+
+ Tempest deprecate the above two config options in Rocky release
+ and will be removed in future. Alternatively ``CONF.volume.catalog_type``
+ can be used to run the Tempest against volume v2 or v3 API.
diff --git a/releasenotes/notes/omit_X-Subject-Token_from_log-1bf5fef88c80334b.yaml b/releasenotes/notes/omit_X-Subject-Token_from_log-1bf5fef88c80334b.yaml
new file mode 100644
index 0000000..51c8f79
--- /dev/null
+++ b/releasenotes/notes/omit_X-Subject-Token_from_log-1bf5fef88c80334b.yaml
@@ -0,0 +1,7 @@
+---
+security:
+ - |
+ The x-subject-token of a response header is ommitted from log,
+ but clients specify the same token on a request header on
+ Keystone API and that was not omitted. In this release,
+ that has been omitted for a security reason.
diff --git a/releasenotes/notes/tempest-rocky-release-0fc3312053923380.yaml b/releasenotes/notes/tempest-rocky-release-0fc3312053923380.yaml
new file mode 100644
index 0000000..e9c77a6
--- /dev/null
+++ b/releasenotes/notes/tempest-rocky-release-0fc3312053923380.yaml
@@ -0,0 +1,16 @@
+---
+prelude: >
+ This release is to tag the Tempest for OpenStack Rocky release.
+ After this release, Tempest will support below OpenStack Releases:
+
+ * Rocky
+ * Queens
+ * Pike
+ * Ocata
+
+ Current development of Tempest is for OpenStack Stein development
+ cycle. Every Tempest commit is also tested against master during
+ the Stein cycle. However, this does not necessarily mean that using
+ Tempest as of this tag will work against a Stein (or future release)
+ cloud.
+ To be on safe side, use this tag to test the OpenStack Rocky release.
\ No newline at end of file
diff --git a/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
index 0da2ddc..d3a6851 100644
--- a/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
+++ b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
@@ -2,9 +2,9 @@
features:
- |
New string configuration option ``vnc_server_header`` is added
- to ``compute-feature-enabled`` section. It offers to provide VNC server
- name that is to be expected in the responce header. For example, obvious
- at hand names is 'WebSockify', 'nginx'.
+ to ``compute-feature-enabled`` section. It allows the expected
+ VNC server name in the response header to be specified. For example,
+ obvious at hand names are 'WebSockify', 'nginx'.
fixes:
- |
Fix VNC server response header issue when it is behind reverse proxy
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 2518703..3be014f 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
:maxdepth: 1
unreleased
+ v19.0.0
v18.0.0
v17.2.0
v17.1.0
diff --git a/releasenotes/source/v19.0.0.rst b/releasenotes/source/v19.0.0.rst
new file mode 100644
index 0000000..bcffe5d
--- /dev/null
+++ b/releasenotes/source/v19.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v19.0.0 Release Notes
+=====================
+
+.. release-notes:: 19.0.0 Release Notes
+ :version: 19.0.0
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index 384ca38..71b8e4f 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -39,3 +39,20 @@
:default: smoke
The Tempest tox environment to run.
+
+.. zuul:rolevar:: tempest_black_regex
+ :default: ''
+
+ A regular expression used to skip the tests.
+
+ It works only when used with some specific tox environments
+ ('all', 'all-plugin'.)
+
+ Multi-line and commented regexs can be achieved by doing this:
+
+ ::
+ vars:
+ tempest_black_regex: |
+ (?x) # Ignore comments and whitespaces
+ # Line with only a comment.
+ (tempest.api.identity).*$
diff --git a/roles/run-tempest/defaults/main.yaml b/roles/run-tempest/defaults/main.yaml
index 85e94f2..c89eb93 100644
--- a/roles/run-tempest/defaults/main.yaml
+++ b/roles/run-tempest/defaults/main.yaml
@@ -1,3 +1,4 @@
devstack_base_dir: /opt/stack
tempest_test_regex: ''
tox_envlist: smoke
+tempest_black_regex: ''
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index b68507a..54ddc71 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -35,7 +35,9 @@
when: blacklist_stat.stat.exists
- name: Run Tempest
- command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} {{blacklist_option|default('')}} --concurrency={{tempest_concurrency|default(default_concurrency)}}
+ command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} {{blacklist_option|default('')}} \
+ --concurrency={{tempest_concurrency|default(default_concurrency)}} \
+ --black-regex={{tempest_black_regex|quote}}
args:
chdir: "{{devstack_base_dir}}/tempest"
become: true
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 57d3983..c9d5733 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -25,17 +25,17 @@
CONF = config.CONF
-class AggregatesAdminTestJSON(base.BaseV2ComputeAdminTest):
+class AggregatesAdminTestBase(base.BaseV2ComputeAdminTest):
"""Tests Aggregates API that require admin privileges"""
@classmethod
def setup_clients(cls):
- super(AggregatesAdminTestJSON, cls).setup_clients()
+ super(AggregatesAdminTestBase, cls).setup_clients()
cls.client = cls.os_admin.aggregates_client
@classmethod
def resource_setup(cls):
- super(AggregatesAdminTestJSON, cls).resource_setup()
+ super(AggregatesAdminTestBase, cls).resource_setup()
cls.aggregate_name_prefix = 'test_aggregate'
cls.az_name_prefix = 'test_az'
@@ -48,11 +48,11 @@
if (hyper['hypervisor_type'] ==
CONF.compute.hypervisor_type)]
- hosts_available = [hyper['service']['host'] for hyper in hypers
- if (hyper['state'] == 'up' and
- hyper['status'] == 'enabled')]
- if hosts_available:
- cls.host = hosts_available[0]
+ cls.hosts_available = [hyper['service']['host'] for hyper in hypers
+ if (hyper['state'] == 'up' and
+ hyper['status'] == 'enabled')]
+ if cls.hosts_available:
+ cls.host = cls.hosts_available[0]
else:
msg = "no available compute node found"
if CONF.compute.hypervisor_type:
@@ -69,6 +69,9 @@
return aggregate
+
+class AggregatesAdminTestJSON(AggregatesAdminTestBase):
+
@decorators.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
def test_aggregate_create_delete(self):
# Create and delete an aggregate.
@@ -206,11 +209,48 @@
az_name = data_utils.rand_name(self.az_name_prefix)
aggregate = self._create_test_aggregate(availability_zone=az_name)
- self.client.add_host(aggregate['id'], host=self.host)
- self.addCleanup(self.client.remove_host, aggregate['id'],
- host=self.host)
+ # Find a host that has not been added to other zone,
+ # for one host can't be added to different zones.
+ aggregates = self.client.list_aggregates()['aggregates']
+ hosts_in_zone = []
+ for v in aggregates:
+ if v['availability_zone']:
+ hosts_in_zone.extend(v['hosts'])
+ hosts = [v for v in self.hosts_available if v not in hosts_in_zone]
+ if not hosts:
+ raise self.skipException("All hosts are already in other zones, "
+ "so can't add host to aggregate.")
+ host = hosts[0]
+
+ self.client.add_host(aggregate['id'], host=host)
+ self.addCleanup(self.client.remove_host, aggregate['id'], host=host)
admin_servers_client = self.os_admin.servers_client
server = self.create_test_server(availability_zone=az_name,
wait_until='ACTIVE')
body = admin_servers_client.show_server(server['id'])['server']
- self.assertEqual(self.host, body['OS-EXT-SRV-ATTR:host'])
+ self.assertEqual(host, body['OS-EXT-SRV-ATTR:host'])
+
+
+class AggregatesAdminTestV241(AggregatesAdminTestBase):
+ min_microversion = '2.41'
+
+ # NOTE(gmann): This test tests the Aggregate APIs response schema
+ # for 2.41 microversion. No specific assert or behaviour verification
+ # is needed.
+
+ @decorators.idempotent_id('fdf24d9e-8afa-4700-b6aa-9c498351504f')
+ def test_create_update_show_aggregate_add_remove_host(self):
+ # Update and add a host to the given aggregate and get details.
+ self.useFixture(fixtures.LockFixture('availability_zone'))
+ # Checking create aggregate API response schema
+ aggregate = self._create_test_aggregate()
+
+ new_aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ # Checking update aggregate API response schema
+ self.client.update_aggregate(aggregate['id'], name=new_aggregate_name)
+ # Checking show aggregate API response schema
+ self.client.show_aggregate(aggregate['id'])['aggregate']
+ # Checking add host to aggregate API response schema
+ self.client.add_host(aggregate['id'], host=self.host)
+ # Checking rempve host from aggregate API response schema
+ self.client.remove_host(aggregate['id'], host=self.host)
diff --git a/tempest/api/compute/admin/test_flavors_microversions.py b/tempest/api/compute/admin/test_flavors_microversions.py
index 9f014e6..31b9217 100644
--- a/tempest/api/compute/admin/test_flavors_microversions.py
+++ b/tempest/api/compute/admin/test_flavors_microversions.py
@@ -33,14 +33,14 @@
disk=10,
id=flavor_id)['id']
# Checking show API response schema
- self.flavors_client.show_flavor(new_flavor_id)['flavor']
+ self.flavors_client.show_flavor(new_flavor_id)
# Checking update API response schema
self.admin_flavors_client.update_flavor(new_flavor_id,
- description='new')['flavor']
+ description='new')
# Checking list details API response schema
- self.flavors_client.list_flavors(detail=True)['flavors']
+ self.flavors_client.list_flavors(detail=True)
# Checking list API response schema
- self.flavors_client.list_flavors()['flavors']
+ self.flavors_client.list_flavors()
class FlavorsV261TestJSON(FlavorsV255TestJSON):
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index 72d09ed..2d7e1a7 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -25,6 +25,8 @@
CONF = config.CONF
+# TODO(stephenfin): Remove this test class once the nova queens branch goes
+# into extended maintenance mode.
class FloatingIPsBulkAdminTestJSON(base.BaseV2ComputeAdminTest):
"""Tests Floating IPs Bulk APIs that require admin privileges.
@@ -32,6 +34,7 @@
content/ext-os-floating-ips-bulk.html
"""
max_microversion = '2.35'
+ depends_on_nova_network = True
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index b23c59f..14f51e9 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -104,6 +104,19 @@
"None of the hypervisors had a valid uptime: %s" % hypers)
+class HypervisorAdminV228Test(HypervisorAdminTestBase):
+ min_microversion = '2.28'
+
+ @decorators.idempotent_id('d46bab64-0fbe-4eb8-9133-e6ee56188cc5')
+ def test_get_list_hypervisor_details(self):
+ # NOTE(zhufl): This test tests the hypervisor APIs response schema
+ # for 2.28 microversion. No specific assert or behaviour verification
+ # is needed.
+ hypers = self._list_hypervisors()
+ self.assertNotEmpty(hypers, "No hypervisors found.")
+ self.client.show_hypervisor(hypers[0]['id'])
+
+
class HypervisorAdminUnderV252Test(HypervisorAdminTestBase):
max_microversion = '2.52'
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index bc38144..8350f7c 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -66,7 +66,8 @@
kwargs = dict()
block_migration = getattr(self, 'block_migration', None)
if self.block_migration is None:
- kwargs['disk_over_commit'] = False
+ if self.is_requested_microversion_compatible('2.24'):
+ kwargs['disk_over_commit'] = False
block_migration = (CONF.compute_feature_enabled.
block_migration_for_live_migration and
not volume_backed)
diff --git a/tempest/api/compute/admin/test_live_migration_negative.py b/tempest/api/compute/admin/test_live_migration_negative.py
index deabbc2..8327a3b 100644
--- a/tempest/api/compute/admin/test_live_migration_negative.py
+++ b/tempest/api/compute/admin/test_live_migration_negative.py
@@ -32,9 +32,10 @@
def _migrate_server_to(self, server_id, dest_host):
bmflm = CONF.compute_feature_enabled.block_migration_for_live_migration
- self.admin_servers_client.live_migrate_server(
- server_id, host=dest_host, block_migration=bmflm,
- disk_over_commit=False)
+ kwargs = dict(host=dest_host, block_migration=bmflm)
+ if self.is_requested_microversion_compatible('2.24'):
+ kwargs['disk_over_commit'] = False
+ self.admin_servers_client.live_migrate_server(server_id, **kwargs)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('7fb7856e-ae92-44c9-861a-af62d7830bcb')
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index a6b71b2..e030575 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -106,7 +106,7 @@
'ACTIVE')
server = self.servers_client.show_server(server['id'])['server']
- self.assertEqual(flavor['id'], server['flavor']['id'])
+ self.assert_flavor_equal(flavor['id'], server['flavor'])
def _test_cold_migrate_server(self, revert=False):
if CONF.compute.min_compute_nodes < 2:
diff --git a/tempest/api/compute/admin/test_networks.py b/tempest/api/compute/admin/test_networks.py
index 87ce39d..99907a8 100644
--- a/tempest/api/compute/admin/test_networks.py
+++ b/tempest/api/compute/admin/test_networks.py
@@ -26,6 +26,7 @@
API docs:
https://developer.openstack.org/api-ref/compute/#networks-os-networks-deprecated
"""
+ max_microversion = '2.35'
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index df534bc..12c7255 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -25,22 +25,57 @@
LOG = logging.getLogger(__name__)
-class QuotasAdminTestJSON(base.BaseV2ComputeAdminTest):
+class QuotasAdminTestBase(base.BaseV2ComputeAdminTest):
force_tenant_isolation = True
def setUp(self):
# NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
self.useFixture(fixtures.LockFixture('compute_quotas'))
- super(QuotasAdminTestJSON, self).setUp()
+ super(QuotasAdminTestBase, self).setUp()
@classmethod
def setup_clients(cls):
- super(QuotasAdminTestJSON, cls).setup_clients()
+ super(QuotasAdminTestBase, cls).setup_clients()
cls.adm_client = cls.os_admin.quotas_client
+ def _get_updated_quotas(self):
+ # Verify that GET shows the updated quota set of project
+ project_name = data_utils.rand_name('cpu_quota_project')
+ project_desc = project_name + '-desc'
+ project = identity.identity_utils(self.os_admin).create_project(
+ name=project_name, description=project_desc)
+ project_id = project['id']
+ self.addCleanup(identity.identity_utils(self.os_admin).delete_project,
+ project_id)
+
+ self.adm_client.update_quota_set(project_id, ram='5120')
+ # Call show_quota_set with detail=true to cover the
+ # get_quota_set_details response schema for microversion tests
+ quota_set = self.adm_client.show_quota_set(
+ project_id, detail=True)['quota_set']
+ self.assertEqual(5120, quota_set['ram']['limit'])
+
+ # Verify that GET shows the updated quota set of user
+ user_name = data_utils.rand_name('cpu_quota_user')
+ password = data_utils.rand_password()
+ email = user_name + '@testmail.tm'
+ user = identity.identity_utils(self.os_admin).create_user(
+ username=user_name, password=password, project=project,
+ email=email)
+ user_id = user['id']
+ self.addCleanup(identity.identity_utils(self.os_admin).delete_user,
+ user_id)
+
+ self.adm_client.update_quota_set(project_id,
+ user_id=user_id,
+ ram='2048')
+ quota_set = self.adm_client.show_quota_set(
+ project_id, user_id=user_id)['quota_set']
+ self.assertEqual(2048, quota_set['ram'])
+
@classmethod
def resource_setup(cls):
- super(QuotasAdminTestJSON, cls).resource_setup()
+ super(QuotasAdminTestBase, cls).resource_setup()
# NOTE(afazekas): these test cases should always create and use a new
# tenant most of them should be skipped if we can't do that
@@ -60,6 +95,8 @@
'injected_file_path_bytes',
'injected_files'])
+
+class QuotasAdminTestJSON(QuotasAdminTestBase):
@decorators.idempotent_id('3b0a7c8f-cf58-46b8-a60c-715a32a8ba7d')
def test_get_default_quotas(self):
# Admin can get the default resource quota set for a tenant
@@ -103,36 +140,7 @@
# TODO(afazekas): merge these test cases
@decorators.idempotent_id('ce9e0815-8091-4abd-8345-7fe5b85faa1d')
def test_get_updated_quotas(self):
- # Verify that GET shows the updated quota set of project
- project_name = data_utils.rand_name('cpu_quota_project')
- project_desc = project_name + '-desc'
- project = identity.identity_utils(self.os_admin).create_project(
- name=project_name, description=project_desc)
- project_id = project['id']
- self.addCleanup(identity.identity_utils(self.os_admin).delete_project,
- project_id)
-
- self.adm_client.update_quota_set(project_id, ram='5120')
- quota_set = self.adm_client.show_quota_set(project_id)['quota_set']
- self.assertEqual(5120, quota_set['ram'])
-
- # Verify that GET shows the updated quota set of user
- user_name = data_utils.rand_name('cpu_quota_user')
- password = data_utils.rand_password()
- email = user_name + '@testmail.tm'
- user = identity.identity_utils(self.os_admin).create_user(
- username=user_name, password=password, project=project,
- email=email)
- user_id = user['id']
- self.addCleanup(identity.identity_utils(self.os_admin).delete_user,
- user_id)
-
- self.adm_client.update_quota_set(project_id,
- user_id=user_id,
- ram='2048')
- quota_set = self.adm_client.show_quota_set(
- project_id, user_id=user_id)['quota_set']
- self.assertEqual(2048, quota_set['ram'])
+ self._get_updated_quotas()
@decorators.idempotent_id('389d04f0-3a41-405f-9317-e5f86e3c44f0')
def test_delete_quota(self):
@@ -156,6 +164,30 @@
self.assertEqual(ram_default, quota_set_new['ram'])
+class QuotasAdminTestV236(QuotasAdminTestBase):
+ min_microversion = '2.36'
+ # NOTE(gmann): This test tests the Quota APIs response schema
+ # for 2.36 microversion. No specific assert or behaviour verification
+ # is needed.
+
+ @decorators.idempotent_id('4268b5c9-92e5-4adc-acf1-3a2798f3d803')
+ def test_get_updated_quotas(self):
+ # Checking Quota update, get, get details APIs response schema
+ self._get_updated_quotas()
+
+
+class QuotasAdminTestV257(QuotasAdminTestBase):
+ min_microversion = '2.57'
+ # NOTE(gmann): This test tests the Quota APIs response schema
+ # for 2.57 microversion. No specific assert or behaviour verification
+ # is needed.
+
+ @decorators.idempotent_id('e641e6c6-e86c-41a4-9e5c-9493c0ae47ad')
+ def test_get_updated_quotas(self):
+ # Checking Quota update, get, get details APIs response schema
+ self._get_updated_quotas()
+
+
class QuotaClassesAdminTestJSON(base.BaseV2ComputeAdminTest):
"""Tests the os-quota-class-sets API to update default quotas."""
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index cdfc44a..170b2cc 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -176,7 +176,7 @@
self.assertEqual(self.s1_id, rebuilt_server['id'])
rebuilt_image_id = rebuilt_server['image']['id']
self.assertEqual(self.image_ref_alt, rebuilt_image_id)
- self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref, rebuilt_server['flavor'])
waiters.wait_for_server_status(self.non_admin_client,
rebuilt_server['id'], 'ACTIVE',
raise_on_error=False)
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index 993c8ec..d264829 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -74,8 +74,6 @@
@classmethod
def resource_setup(cls):
super(ServicesAdminNegativeV253TestJSON, cls).resource_setup()
- # Nova returns 400 if `binary` is not nova-compute.
- cls.binary = 'nova-compute'
cls.fake_service_id = data_utils.rand_uuid()
@decorators.attr(type=['negative'])
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index d0c1973..7fbb994 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -428,21 +428,16 @@
except Exception:
LOG.exception('Failed to delete server %s', server_id)
- @classmethod
- def resize_server(cls, server_id, new_flavor_id, **kwargs):
+ def resize_server(self, server_id, new_flavor_id, **kwargs):
"""resize and confirm_resize an server, waits for it to be ACTIVE."""
- cls.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
- waiters.wait_for_server_status(cls.servers_client, server_id,
+ self.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
+ waiters.wait_for_server_status(self.servers_client, server_id,
'VERIFY_RESIZE')
- cls.servers_client.confirm_resize_server(server_id)
- waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE')
- server = cls.servers_client.show_server(server_id)['server']
- # Nova API > 2.46 no longer includes flavor.id
- if server['flavor'].get('id'):
- if new_flavor_id != server['flavor']['id']:
- msg = ('Flavor id of %s is not equal to new_flavor_id.'
- % server_id)
- raise lib_exc.TempestException(msg)
+ self.servers_client.confirm_resize_server(server_id)
+ waiters.wait_for_server_status(
+ self.servers_client, server_id, 'ACTIVE')
+ server = self.servers_client.show_server(server_id)['server']
+ self.assert_flavor_equal(new_flavor_id, server['flavor'])
@classmethod
def delete_volume(cls, volume_id):
@@ -561,6 +556,27 @@
volume['id'], 'in-use')
return attachment
+ def assert_flavor_equal(self, flavor_id, server_flavor):
+ """Check whether server_flavor equals to flavor.
+
+ :param flavor_id: flavor id
+ :param server_flavor: flavor info returned by show_server.
+ """
+ # Nova API > 2.46 no longer includes flavor.id, and schema check
+ # will cover whether 'id' should be in flavor
+ if server_flavor.get('id'):
+ msg = ('server flavor is not same as flavor!')
+ self.assertEqual(flavor_id, server_flavor['id'], msg)
+ else:
+ flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
+ self.assertEqual(flavor['name'], server_flavor['original_name'],
+ "original_name in server flavor is not same as "
+ "flavor name!")
+ for key in ['ram', 'vcpus', 'disk']:
+ msg = ('attribute %s in server flavor is not same as '
+ 'flavor!' % key)
+ self.assertEqual(flavor[key], server_flavor[key], msg)
+
class BaseV2ComputeAdminTest(BaseV2ComputeTest):
"""Base test case class for Compute Admin API tests."""
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index a2e58c9..bebc6ca 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -33,8 +33,11 @@
def tearDown(self):
"""Terminate test instances created after a test is executed."""
- self.server_check_teardown()
super(ImagesOneServerNegativeTestJSON, self).tearDown()
+ # NOTE(zhufl): Because server_check_teardown will raise Exception
+ # which will prevent other cleanup steps from being executed, so
+ # server_check_teardown should be called after super's tearDown.
+ self.server_check_teardown()
def setUp(self):
# NOTE(afazekas): Normally we use the same server with all test cases,
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 0585fec..8c2202e 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -18,6 +18,7 @@
class AbsoluteLimitsTestJSON(base.BaseV2ComputeTest):
+ max_microversion = '2.56'
@classmethod
def setup_clients(cls):
@@ -26,22 +27,14 @@
@decorators.idempotent_id('b54c66af-6ab6-4cf0-a9e5-a0cb58d75e0b')
def test_absLimits_get(self):
- # To check if all limits are present in the response
- limits = self.client.show_limits()['limits']
- absolute_limits = limits['absolute']
- expected_elements = ['maxImageMeta', 'maxPersonality',
- 'maxPersonalitySize',
- 'maxServerMeta', 'maxTotalCores',
- 'maxTotalFloatingIps', 'maxSecurityGroups',
- 'maxSecurityGroupRules', 'maxTotalInstances',
- 'maxTotalKeypairs', 'maxTotalRAMSize',
- 'maxServerGroups', 'maxServerGroupMembers',
- 'totalCoresUsed', 'totalFloatingIpsUsed',
- 'totalSecurityGroupsUsed', 'totalInstancesUsed',
- 'totalRAMUsed', 'totalServerGroupsUsed']
- # check whether all expected elements exist
- missing_elements =\
- [ele for ele in expected_elements if ele not in absolute_limits]
- self.assertEmpty(missing_elements,
- "Failed to find element %s in absolute limits list"
- % ', '.join(ele for ele in missing_elements))
+ # To check if all limits are present in the response (will be checked
+ # by schema)
+ self.client.show_limits()
+
+
+class AbsoluteLimitsV257TestJSON(base.BaseV2ComputeTest):
+ min_microversion = '2.57'
+ max_microversion = 'latest'
+
+ # NOTE(felipemonteiro): This class tests the Absolute Limits APIs
+ # response schema for the 2.57 microversion.
diff --git a/tempest/api/compute/limits/test_absolute_limits_negative.py b/tempest/api/compute/limits/test_absolute_limits_negative.py
index bef4eb5..500638a 100644
--- a/tempest/api/compute/limits/test_absolute_limits_negative.py
+++ b/tempest/api/compute/limits/test_absolute_limits_negative.py
@@ -33,15 +33,15 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('215cd465-d8ae-49c9-bf33-9c911913a5c8')
- def test_max_image_meta_exceed_limit(self):
- # We should not create vm with image meta over maxImageMeta limit
+ def test_max_metadata_exceed_limit(self):
+ # We should not create vm with metadata over maxServerMeta limit
# Get max limit value
limits = self.client.show_limits()['limits']
- max_meta = limits['absolute']['maxImageMeta']
+ max_meta = limits['absolute']['maxServerMeta']
# No point in running this test if there is no limit.
if max_meta == -1:
- raise self.skipException('no limit for maxImageMeta')
+ raise self.skipException('no limit for maxServerMeta')
# Create server should fail, since we are passing > metadata Limit!
max_meta_data = max_meta + 1
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 7509ac6..0636ee4 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -29,11 +29,11 @@
CONF = config.CONF
-class AttachInterfacesTestJSON(base.BaseV2ComputeTest):
+class AttachInterfacesTestBase(base.BaseV2ComputeTest):
@classmethod
def skip_checks(cls):
- super(AttachInterfacesTestJSON, cls).skip_checks()
+ super(AttachInterfacesTestBase, cls).skip_checks()
if not CONF.service_available.neutron:
raise cls.skipException("Neutron is required")
if not CONF.compute_feature_enabled.interface_attach:
@@ -43,14 +43,26 @@
def setup_credentials(cls):
# This test class requires network and subnet
cls.set_network_resources(network=True, subnet=True)
- super(AttachInterfacesTestJSON, cls).setup_credentials()
+ super(AttachInterfacesTestBase, cls).setup_credentials()
@classmethod
def setup_clients(cls):
- super(AttachInterfacesTestJSON, cls).setup_clients()
+ super(AttachInterfacesTestBase, cls).setup_clients()
cls.subnets_client = cls.os_primary.subnets_client
cls.ports_client = cls.os_primary.ports_client
+ def _create_server_get_interfaces(self):
+ server = self.create_test_server(wait_until='ACTIVE')
+ ifs = (self.interfaces_client.list_interfaces(server['id'])
+ ['interfaceAttachments'])
+ body = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
+ ifs[0]['port_state'] = body['port_state']
+ return server, ifs
+
+
+class AttachInterfacesTestJSON(AttachInterfacesTestBase):
+
def wait_for_port_detach(self, port_id):
"""Waits for the port's device_id to be unset.
@@ -92,15 +104,6 @@
if mac_addr:
self.assertEqual(iface['mac_addr'], mac_addr)
- def _create_server_get_interfaces(self):
- server = self.create_test_server(wait_until='ACTIVE')
- ifs = (self.interfaces_client.list_interfaces(server['id'])
- ['interfaceAttachments'])
- body = waiters.wait_for_interface_status(
- self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
- ifs[0]['port_state'] = body['port_state']
- return server, ifs
-
def _test_create_interface(self, server):
iface = (self.interfaces_client.create_interface(server['id'])
['interfaceAttachment'])
@@ -254,30 +257,6 @@
_ifs = self._test_delete_interface(server, ifs)
self.assertEqual(len(ifs) - 1, len(_ifs))
- @decorators.attr(type='smoke')
- @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
- @utils.services('network')
- def test_add_remove_fixed_ip(self):
- # Add and Remove the fixed IP to server.
- server, ifs = self._create_server_get_interfaces()
- interface_count = len(ifs)
- self.assertGreater(interface_count, 0)
- network_id = ifs[0]['net_id']
- self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
- # Remove the fixed IP from server.
- server_detail = self.os_primary.servers_client.show_server(
- server['id'])['server']
- # Get the Fixed IP from server.
- fixed_ip = None
- for ip_set in server_detail['addresses']:
- for ip in server_detail['addresses'][ip_set]:
- if ip['OS-EXT-IPS:type'] == 'fixed':
- fixed_ip = ip['addr']
- break
- if fixed_ip is not None:
- break
- self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
-
@decorators.idempotent_id('2f3a0127-95c7-4977-92d2-bc5aec602fb4')
def test_reassign_port_between_servers(self):
"""Tests the following:
@@ -314,3 +293,31 @@
# API so we have to poll the port until the device_id is unset.
self.interfaces_client.delete_interface(server['id'], port_id)
self.wait_for_port_detach(port_id)
+
+
+class AttachInterfacesUnderV243Test(AttachInterfacesTestBase):
+ max_microversion = '2.43'
+
+ @decorators.attr(type='smoke')
+ @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
+ @utils.services('network')
+ def test_add_remove_fixed_ip(self):
+ # Add and Remove the fixed IP to server.
+ server, ifs = self._create_server_get_interfaces()
+ interface_count = len(ifs)
+ self.assertGreater(interface_count, 0)
+ network_id = ifs[0]['net_id']
+ self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
+ # Remove the fixed IP from server.
+ server_detail = self.os_primary.servers_client.show_server(
+ server['id'])['server']
+ # Get the Fixed IP from server.
+ fixed_ip = None
+ for ip_set in server_detail['addresses']:
+ for ip in server_detail['addresses'][ip_set]:
+ if ip['OS-EXT-IPS:type'] == 'fixed':
+ fixed_ip = ip['addr']
+ break
+ if fixed_ip is not None:
+ break
+ self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 122c4f5..4f0dbad 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -80,7 +80,7 @@
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.assert_flavor_equal(self.flavor_ref, self.server['flavor'])
self.assertEqual(self.meta, self.server['metadata'])
@decorators.attr(type='smoke')
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index ff8ed61..40681cb 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -320,7 +320,9 @@
try:
self.assertEmpty(md_dict['devices'])
return True
- except Exception:
+ except AssertionError:
+ LOG.debug("Related bug 1775947. Devices dict is not empty: %s",
+ md_dict['devices'])
return False
@decorators.idempotent_id('3e41c782-2a89-4922-a9d2-9a188c4e7c7c')
@@ -380,5 +382,8 @@
waiters.wait_for_interface_detach(self.interfaces_client,
server['id'],
interface['port_id'])
- self.verify_metadata_from_api(server, ssh_client,
- self.verify_empty_devices)
+ # FIXME(mriedem): The assertion that the tagged devices are removed
+ # from the metadata for the server is being skipped until bug 1775947
+ # is fixed.
+ # self.verify_metadata_from_api(server, ssh_client,
+ # self.verify_empty_devices)
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index 1dfd0f9..5801db1 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -44,10 +44,13 @@
self._websocket = None
def tearDown(self):
- self.server_check_teardown()
super(NoVNCConsoleTestJSON, self).tearDown()
if self._websocket is not None:
self._websocket.close()
+ # NOTE(zhufl): Because server_check_teardown will raise Exception
+ # which will prevent other cleanup steps from being executed, so
+ # server_check_teardown should be called after super's tearDown.
+ self.server_check_teardown()
@classmethod
def setup_clients(cls):
@@ -58,6 +61,9 @@
def resource_setup(cls):
super(NoVNCConsoleTestJSON, cls).resource_setup()
cls.server = cls.create_test_server(wait_until="ACTIVE")
+ cls.use_get_remote_console = False
+ if not cls.is_requested_microversion_compatible('2.5'):
+ cls.use_get_remote_console = True
def _validate_novnc_html(self, vnc_url):
"""Verify we can connect to novnc and get back the javascript."""
@@ -170,8 +176,13 @@
@decorators.idempotent_id('c640fdff-8ab4-45a4-a5d8-7e6146cbd0dc')
def test_novnc(self):
- body = self.client.get_vnc_console(self.server['id'],
- type='novnc')['console']
+ if self.use_get_remote_console:
+ body = self.client.get_remote_console(
+ self.server['id'], console_type='novnc',
+ protocol='vnc')['remote_console']
+ else:
+ body = self.client.get_vnc_console(self.server['id'],
+ type='novnc')['console']
self.assertEqual('novnc', body['type'])
# Do the initial HTTP Request to novncproxy to get the NoVNC JavaScript
self._validate_novnc_html(body['url'])
@@ -184,8 +195,13 @@
@decorators.idempotent_id('f9c79937-addc-4aaa-9e0e-841eef02aeb7')
def test_novnc_bad_token(self):
- body = self.client.get_vnc_console(self.server['id'],
- type='novnc')['console']
+ if self.use_get_remote_console:
+ body = self.client.get_remote_console(
+ self.server['id'], console_type='novnc',
+ protocol='vnc')['remote_console']
+ else:
+ body = self.client.get_vnc_console(self.server['id'],
+ type='novnc')['console']
self.assertEqual('novnc', body['type'])
# Do the WebSockify HTTP Request to novncproxy with a bad token
url = body['url'].replace('token=', 'token=bad')
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 350e8ba..f6494b5 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -59,8 +59,11 @@
self.server_id, validatable=True)
def tearDown(self):
- self.server_check_teardown()
super(ServerActionsTestJSON, self).tearDown()
+ # NOTE(zhufl): Because server_check_teardown will raise Exception
+ # which will prevent other cleanup steps from being executed, so
+ # server_check_teardown should be called after super's tearDown.
+ self.server_check_teardown()
@classmethod
def setup_credentials(cls):
@@ -197,7 +200,7 @@
self.assertEqual(self.server_id, rebuilt_server['id'])
rebuilt_image_id = rebuilt_server['image']['id']
self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
- self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref, rebuilt_server['flavor'])
# Verify the server properties after the rebuild completes
waiters.wait_for_server_status(self.client,
@@ -251,7 +254,7 @@
self.assertEqual(self.server_id, rebuilt_server['id'])
rebuilt_image_id = rebuilt_server['image']['id']
self.assertEqual(new_image, rebuilt_image_id)
- self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref, rebuilt_server['flavor'])
# Verify the server properties after the rebuild completes
waiters.wait_for_server_status(self.client,
@@ -303,7 +306,7 @@
expected_status)
server = self.client.show_server(server_id)['server']
- self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref_alt, server['flavor'])
if stop:
# NOTE(mriedem): tearDown requires the server to be started.
@@ -367,7 +370,7 @@
waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
server = self.client.show_server(self.server_id)['server']
- self.assertEqual(self.flavor_ref, server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref, server['flavor'])
@decorators.idempotent_id('fbbf075f-a812-4022-bc5c-ccb8047eef12')
@decorators.related_bug('1737599')
@@ -527,10 +530,10 @@
def _get_output(self):
output = self.client.get_console_output(
- self.server_id, length=10)['output']
+ self.server_id, length=3)['output']
self.assertTrue(output, "Console output was empty.")
lines = len(output.split('\n'))
- self.assertEqual(lines, 10)
+ self.assertEqual(lines, 3)
@decorators.idempotent_id('4b8867e6-fffa-4d54-b1d1-6fdda57be2f3')
@testtools.skipUnless(CONF.compute_feature_enabled.console_output,
@@ -561,8 +564,8 @@
# NOTE: This test tries to get full length console log, and the
# length should be bigger than the one of test_get_console_output.
- self.assertGreater(lines, 10, "Cannot get enough console log "
- "length. (lines: %s)" % lines)
+ self.assertGreater(lines, 3, "Cannot get enough console log "
+ "length. (lines: %s)" % lines)
self.wait_for(_check_full_length_console_log)
@@ -690,8 +693,13 @@
# Get the VNC console of type 'novnc' and 'xvpvnc'
console_types = ['novnc', 'xvpvnc']
for console_type in console_types:
- body = self.client.get_vnc_console(self.server_id,
- type=console_type)['console']
+ if self.is_requested_microversion_compatible('2.5'):
+ body = self.client.get_vnc_console(
+ self.server_id, type=console_type)['console']
+ else:
+ body = self.client.get_remote_console(
+ self.server_id, console_type=console_type,
+ protocol='vnc')['remote_console']
self.assertEqual(console_type, body['type'])
self.assertNotEqual('', body['url'])
self._validate_url(body['url'])
diff --git a/tempest/api/compute/servers/test_server_group.py b/tempest/api/compute/servers/test_server_group.py
index 5286c8f..1b7cb96 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -47,8 +47,16 @@
super(ServerGroupTestJSON, cls).resource_setup()
cls.policy = ['affinity']
- cls.created_server_group = cls.create_test_server_group(
- policy=cls.policy)
+ def setUp(self):
+ super(ServerGroupTestJSON, self).setUp()
+ # TODO(zhufl): After microversion 2.13 project_id and user_id are
+ # added to the body of server_group, and microversion is not used
+ # in resource_setup for now, so we should create server group in setUp
+ # in order to use the same microversion as in testcases till
+ # microversion support in resource_setup is fulfilled.
+ if not hasattr(self, 'created_server_group'):
+ self.__class__.created_server_group = \
+ self.create_test_server_group(policy=self.policy)
def _create_server_group(self, name, policy):
# create the test server-group with given policy
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index e944c28..0c1c05c 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -40,8 +40,11 @@
self.__class__.server_id = self.recreate_server(self.server_id)
def tearDown(self):
- self.server_check_teardown()
super(ServersNegativeTestJSON, self).tearDown()
+ # NOTE(zhufl): Because server_check_teardown will raise Exception
+ # which will prevent other cleanup steps from being executed, so
+ # server_check_teardown should be called after super's tearDown.
+ self.server_check_teardown()
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 5fb1711..f810ec5 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -28,6 +28,7 @@
# TODO(mriedem): Remove this test class once the nova queens branch goes into
# extended maintenance mode.
class VirtualInterfacesTestJSON(base.BaseV2ComputeTest):
+ max_microversion = '2.43'
depends_on_nova_network = True
diff --git a/tempest/api/compute/servers/test_virtual_interfaces_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index ec4d7a8..f6e8bc9 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -23,6 +23,7 @@
# TODO(mriedem): Remove this test class once the nova queens branch goes into
# extended maintenance mode.
class VirtualInterfacesNegativeTestJSON(base.BaseV2ComputeTest):
+ max_microversion = '2.43'
depends_on_nova_network = True
diff --git a/tempest/api/compute/test_networks.py b/tempest/api/compute/test_networks.py
index b8c79d7..76131e2 100644
--- a/tempest/api/compute/test_networks.py
+++ b/tempest/api/compute/test_networks.py
@@ -20,6 +20,8 @@
class ComputeNetworksTest(base.BaseV2ComputeTest):
+ max_microversion = '2.35'
+
@classmethod
def skip_checks(cls):
super(ComputeNetworksTest, cls).skip_checks()
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 874aaa4..2cd8906 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -20,6 +20,10 @@
class EndPointsTestJSON(base.BaseIdentityV3AdminTest):
+ # NOTE: force_tenant_isolation is true in the base class by default but
+ # overridden to false here to allow test execution for clouds using the
+ # pre-provisioned credentials provider.
+ force_tenant_isolation = False
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index d54e222..4c3eb1c 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -1,4 +1,3 @@
-
# Copyright 2013 IBM Corp.
# All Rights Reserved.
#
@@ -21,6 +20,10 @@
class EndpointsNegativeTestJSON(base.BaseIdentityV3AdminTest):
+ # NOTE: force_tenant_isolation is true in the base class by default but
+ # overridden to false here to allow test execution for clouds using the
+ # pre-provisioned credentials provider.
+ force_tenant_isolation = False
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 82664e8..148b368 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -18,7 +18,19 @@
from tempest.lib import decorators
-class ListProjectsTestJSON(base.BaseIdentityV3AdminTest):
+class BaseListProjectsTestJSON(base.BaseIdentityV3AdminTest):
+
+ def _list_projects_with_params(self, included, excluded, params, key):
+ # Validate that projects in ``included`` belongs to the projects
+ # returned that match ``params`` but not projects in ``excluded``
+ body = self.projects_client.list_projects(params)['projects']
+ for p in included:
+ self.assertIn(p[key], map(lambda x: x[key], body))
+ for p in excluded:
+ self.assertNotIn(p[key], map(lambda x: x[key], body))
+
+
+class ListProjectsTestJSON(BaseListProjectsTestJSON):
@classmethod
def resource_setup(cls):
@@ -61,17 +73,20 @@
def test_list_projects_with_domains(self):
# List projects with domain
self._list_projects_with_params(
- {'domain_id': self.domain['id']}, 'domain_id')
+ [self.p1], [self.p2, self.p3], {'domain_id': self.domain['id']},
+ 'domain_id')
@decorators.idempotent_id('0fe7a334-675a-4509-b00e-1c4b95d5dae8')
def test_list_projects_with_enabled(self):
# List the projects with enabled
- self._list_projects_with_params({'enabled': False}, 'enabled')
+ self._list_projects_with_params(
+ [self.p1], [self.p2, self.p3], {'enabled': False}, 'enabled')
@decorators.idempotent_id('fa178524-4e6d-4925-907c-7ab9f42c7e26')
def test_list_projects_with_name(self):
# List projects with name
- self._list_projects_with_params({'name': self.p1_name}, 'name')
+ self._list_projects_with_params(
+ [self.p1], [self.p2, self.p3], {'name': self.p1_name}, 'name')
@decorators.idempotent_id('6edc66f5-2941-4a17-9526-4073311c1fac')
def test_list_projects_with_parent(self):
@@ -82,8 +97,3 @@
self.assertNotEmpty(fetched_projects)
for project in fetched_projects:
self.assertEqual(self.p3['parent_id'], project['parent_id'])
-
- def _list_projects_with_params(self, params, key):
- body = self.projects_client.list_projects(params)['projects']
- self.assertIn(self.p1[key], map(lambda x: x[key], body))
- self.assertNotIn(self.p2[key], map(lambda x: x[key], body))
diff --git a/tempest/api/network/admin/test_external_network_extension.py b/tempest/api/network/admin/test_external_network_extension.py
index 49a9cdb..7e8cc8e 100644
--- a/tempest/api/network/admin/test_external_network_extension.py
+++ b/tempest/api/network/admin/test_external_network_extension.py
@@ -13,6 +13,7 @@
import testtools
from tempest.api.network import base
+from tempest.common import utils
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
@@ -117,8 +118,15 @@
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.admin_floating_ips_client.delete_floatingip,
created_floating_ip['id'])
- floatingip_list = self.admin_floating_ips_client.list_floatingips(
- network=external_network['id'])
+ if utils.is_extension_enabled('filter-validation', 'network'):
+ floatingip_list = self.admin_floating_ips_client.list_floatingips(
+ floating_network_id=external_network['id'])
+ else:
+ # NOTE(hongbin): This is for testing the backward-compatibility
+ # of neutron API although the parameter is a wrong filter
+ # for listing floating IPs.
+ floatingip_list = self.admin_floating_ips_client.list_floatingips(
+ invalid_filter=external_network['id'])
self.assertIn(created_floating_ip['id'],
(f['id'] for f in floatingip_list['floatingips']))
self.admin_networks_client.delete_network(external_network['id'])
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index e546bff..61a6df4 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -38,8 +38,8 @@
@classmethod
def setup_clients(cls):
super(BaseVolumeQuotasAdminTestJSON, cls).setup_clients()
- cls.transfer_client = cls.os_primary.volume_transfers_v2_client
- cls.alt_transfer_client = cls.os_alt.volume_transfers_v2_client
+ cls.transfer_client = cls.os_primary.volume_transfers_client_latest
+ cls.alt_transfer_client = cls.os_alt.volume_transfers_client_latest
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/volume/admin/test_volume_services_negative.py b/tempest/api/volume/admin/test_volume_services_negative.py
index 6f3dbc6..3a863a1 100644
--- a/tempest/api/volume/admin/test_volume_services_negative.py
+++ b/tempest/api/volume/admin/test_volume_services_negative.py
@@ -23,10 +23,9 @@
@classmethod
def resource_setup(cls):
super(VolumeServicesNegativeTest, cls).resource_setup()
- cls.services = cls.admin_volume_services_client.list_services()[
- 'services']
- cls.host = cls.services[0]['host']
- cls.binary = cls.services[0]['binary']
+ services = cls.admin_volume_services_client.list_services()['services']
+ cls.host = services[0]['host']
+ cls.binary = services[0]['binary']
@decorators.attr(type='negative')
@decorators.idempotent_id('3246ce65-ba70-4159-aa3b-082c28e4b484')
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 81fd6e6..64fe29a 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -74,24 +74,19 @@
if CONF.service_available.glance:
cls.images_client = cls.os_primary.image_client_v2
- if cls._api_version == 3:
- cls.backups_client = cls.os_primary.backups_v3_client
- cls.volumes_client = cls.os_primary.volumes_v3_client
- cls.messages_client = cls.os_primary.volume_v3_messages_client
- cls.versions_client = cls.os_primary.volume_v3_versions_client
- cls.groups_client = cls.os_primary.groups_v3_client
- cls.group_snapshots_client = (
- cls.os_primary.group_snapshots_v3_client)
- else:
- cls.backups_client = cls.os_primary.backups_v2_client
- cls.volumes_client = cls.os_primary.volumes_v2_client
-
- cls.snapshots_client = cls.os_primary.snapshots_v2_client
+ cls.backups_client = cls.os_primary.backups_client_latest
+ cls.volumes_client = cls.os_primary.volumes_client_latest
+ cls.messages_client = cls.os_primary.volume_messages_client_latest
+ cls.versions_client = cls.os_primary.volume_versions_client_latest
+ cls.groups_client = cls.os_primary.groups_client_latest
+ cls.group_snapshots_client = (
+ cls.os_primary.group_snapshots_client_latest)
+ cls.snapshots_client = cls.os_primary.snapshots_client_latest
cls.volumes_extension_client =\
- cls.os_primary.volumes_v2_extension_client
+ cls.os_primary.volumes_extension_client_latest
cls.availability_zone_client = (
- cls.os_primary.volume_v2_availability_zone_client)
- cls.volume_limits_client = cls.os_primary.volume_v2_limits_client
+ cls.os_primary.volume_availability_zone_client_latest)
+ cls.volume_limits_client = cls.os_primary.volume_limits_client_latest
def setUp(self):
super(BaseVolumeTest, self).setUp()
@@ -247,34 +242,34 @@
def setup_clients(cls):
super(BaseVolumeAdminTest, cls).setup_clients()
- cls.admin_volume_qos_client = cls.os_admin.volume_qos_v2_client
+ cls.admin_volume_qos_client = cls.os_admin.volume_qos_client_latest
cls.admin_volume_services_client = \
- cls.os_admin.volume_services_v2_client
- cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
- cls.admin_volume_manage_client = cls.os_admin.volume_manage_v2_client
- cls.admin_volume_client = cls.os_admin.volumes_v2_client
- if cls._api_version == 3:
- cls.admin_volume_client = cls.os_admin.volumes_v3_client
- cls.admin_groups_client = cls.os_admin.groups_v3_client
- cls.admin_messages_client = cls.os_admin.volume_v3_messages_client
- cls.admin_group_snapshots_client = \
- cls.os_admin.group_snapshots_v3_client
- cls.admin_group_types_client = cls.os_admin.group_types_v3_client
- cls.admin_hosts_client = cls.os_admin.volume_hosts_v2_client
+ cls.os_admin.volume_services_client_latest
+ cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
+ cls.admin_volume_manage_client = (
+ cls.os_admin.volume_manage_client_latest)
+ cls.admin_volume_client = cls.os_admin.volumes_client_latest
+ cls.admin_groups_client = cls.os_admin.groups_client_latest
+ cls.admin_messages_client = cls.os_admin.volume_messages_client_latest
+ cls.admin_group_snapshots_client = \
+ cls.os_admin.group_snapshots_client_latest
+ cls.admin_group_types_client = cls.os_admin.group_types_client_latest
+ cls.admin_hosts_client = cls.os_admin.volume_hosts_client_latest
cls.admin_snapshot_manage_client = \
- cls.os_admin.snapshot_manage_v2_client
- cls.admin_snapshots_client = cls.os_admin.snapshots_v2_client
- cls.admin_backups_client = cls.os_admin.backups_v2_client
+ cls.os_admin.snapshot_manage_client_latest
+ cls.admin_snapshots_client = cls.os_admin.snapshots_client_latest
+ cls.admin_backups_client = cls.os_admin.backups_client_latest
cls.admin_encryption_types_client = \
- cls.os_admin.encryption_types_v2_client
+ cls.os_admin.encryption_types_client_latest
cls.admin_quota_classes_client = \
- cls.os_admin.volume_quota_classes_v2_client
- cls.admin_quotas_client = cls.os_admin.volume_quotas_v2_client
- cls.admin_volume_limits_client = cls.os_admin.volume_v2_limits_client
+ cls.os_admin.volume_quota_classes_client_latest
+ cls.admin_quotas_client = cls.os_admin.volume_quotas_client_latest
+ cls.admin_volume_limits_client = (
+ cls.os_admin.volume_limits_client_latest)
cls.admin_capabilities_client = \
- cls.os_admin.volume_capabilities_v2_client
+ cls.os_admin.volume_capabilities_client_latest
cls.admin_scheduler_stats_client = \
- cls.os_admin.volume_scheduler_stats_v2_client
+ cls.os_admin.volume_scheduler_stats_client_latest
@classmethod
def create_test_qos_specs(cls, name=None, consumer=None, **kwargs):
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 75e81b7..c85e0bc 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -27,10 +27,10 @@
def setup_clients(cls):
super(VolumesTransfersTest, cls).setup_clients()
- cls.client = cls.os_primary.volume_transfers_v2_client
- cls.alt_client = cls.os_alt.volume_transfers_v2_client
- cls.alt_volumes_client = cls.os_alt.volumes_v2_client
- cls.adm_volumes_client = cls.os_admin.volumes_v2_client
+ cls.client = cls.os_primary.volume_transfers_client_latest
+ cls.alt_client = cls.os_alt.volume_transfers_client_latest
+ cls.alt_volumes_client = cls.os_alt.volumes_client_latest
+ cls.adm_volumes_client = cls.os_admin.volumes_client_latest
@decorators.idempotent_id('4d75b645-a478-48b1-97c8-503f64242f1a')
def test_create_get_list_accept_volume_transfer(self):
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 5d339c4..ac9a9c7 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -33,7 +33,7 @@
def test_volume_extend(self):
# Extend Volume Test.
volume = self.create_volume(image_ref=self.image_ref)
- extend_size = volume['size'] + 1
+ extend_size = volume['size'] * 2
self.volumes_client.extend_volume(volume['id'],
new_size=extend_size)
waiters.wait_for_volume_resource_status(self.volumes_client,
@@ -48,7 +48,7 @@
volume = self.create_volume()
self.create_snapshot(volume['id'])
- extend_size = volume['size'] + 1
+ extend_size = volume['size'] * 2
self.volumes_client.extend_volume(volume['id'], new_size=extend_size)
waiters.wait_for_volume_resource_status(self.volumes_client,
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index f139283..866bd87 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -103,21 +103,24 @@
def test_create_volume_with_nonexistent_volume_type(self):
# Should not be able to create volume with non-existent volume type
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', volume_type=data_utils.rand_uuid())
+ size=CONF.volume.volume_size,
+ volume_type=data_utils.rand_uuid())
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0c36f6ae-4604-4017-b0a9-34fdc63096f9')
def test_create_volume_with_nonexistent_snapshot_id(self):
# Should not be able to create volume with non-existent snapshot
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', snapshot_id=data_utils.rand_uuid())
+ size=CONF.volume.volume_size,
+ snapshot_id=data_utils.rand_uuid())
@decorators.attr(type=['negative'])
@decorators.idempotent_id('47c73e08-4be8-45bb-bfdf-0c4e79b88344')
def test_create_volume_with_nonexistent_source_volid(self):
# Should not be able to create volume with non-existent source volume
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', source_volid=data_utils.rand_uuid())
+ size=CONF.volume.volume_size,
+ source_volid=data_utils.rand_uuid())
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0186422c-999a-480e-a026-6a665744c30c')
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index ea5f036..0453c0a 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -50,7 +50,7 @@
@decorators.idempotent_id('677863d1-34f9-456d-b6ac-9924f667a7f4')
def test_volume_from_snapshot_decreasing_size(self):
# Creates a volume a snapshot passing a size different from the source
- src_size = CONF.volume.volume_size + 1
+ src_size = CONF.volume.volume_size * 2
src_vol = self.create_volume(size=src_size)
src_snap = self.create_snapshot(src_vol['id'])
@@ -58,7 +58,7 @@
# Destination volume smaller than source
self.assertRaises(lib_exc.BadRequest,
self.volumes_client.create_volume,
- size=src_size - 1,
+ size=CONF.volume.volume_size,
snapshot_id=src_snap['id'])
@decorators.attr(type=['negative'])
diff --git a/tempest/clients.py b/tempest/clients.py
index 2a07be9..4f2846e 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -241,6 +241,47 @@
# if only api_v3 is enabled, all these clients should be available
if (CONF.volume_feature_enabled.api_v2 or
CONF.volume_feature_enabled.api_v3):
+ self.backups_client_latest = self.volume_v3.BackupsClient()
+ self.encryption_types_client_latest = \
+ self.volume_v3.EncryptionTypesClient()
+ self.snapshot_manage_client_latest = \
+ self.volume_v3.SnapshotManageClient()
+ self.snapshots_client_latest = self.volume_v3.SnapshotsClient()
+ self.volume_capabilities_client_latest = \
+ self.volume_v3.CapabilitiesClient()
+ self.volume_manage_client_latest = (
+ self.volume_v3.VolumeManageClient())
+ self.volume_qos_client_latest = self.volume_v3.QosSpecsClient()
+ self.volume_services_client_latest = (
+ self.volume_v3.ServicesClient())
+ self.volume_types_client_latest = self.volume_v3.TypesClient()
+ self.volume_hosts_client_latest = self.volume_v3.HostsClient()
+ self.volume_quotas_client_latest = self.volume_v3.QuotasClient()
+ self.volume_quota_classes_client_latest = \
+ self.volume_v3.QuotaClassesClient()
+ self.volume_scheduler_stats_client_latest = \
+ self.volume_v3.SchedulerStatsClient()
+ self.volume_transfers_client_latest = \
+ self.volume_v3.TransfersClient()
+ self.volume_availability_zone_client_latest = \
+ self.volume_v3.AvailabilityZoneClient()
+ self.volume_limits_client_latest = self.volume_v3.LimitsClient()
+ self.volumes_client_latest = self.volume_v3.VolumesClient()
+ self.volumes_extension_client_latest = \
+ self.volume_v3.ExtensionsClient()
+ self.group_types_client_latest = self.volume_v3.GroupTypesClient()
+ self.groups_client_latest = self.volume_v3.GroupsClient()
+ self.group_snapshots_client_latest = \
+ self.volume_v3.GroupSnapshotsClient()
+ self.volume_messages_client_latest = (
+ self.volume_v3.MessagesClient())
+ self.volume_versions_client_latest = (
+ self.volume_v3.VersionsClient())
+
+ # TODO(gmann): Below alias for service clients have been
+ # deprecated and will be removed in future. Start using the alias
+ # defined above with suffix _latest.
+ # ****************Deprecated alias start from here***************
self.backups_v2_client = self.volume_v3.BackupsClient()
self.encryption_types_v2_client = \
self.volume_v3.EncryptionTypesClient()
@@ -268,11 +309,6 @@
self.volumes_v2_extension_client = \
self.volume_v3.ExtensionsClient()
- # Set default client for users that don't need explicit version
- self.volumes_client_latest = self.volumes_v2_client
- self.snapshots_client_latest = self.snapshots_v2_client
- self.backups_client_latest = self.backups_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()
@@ -283,11 +319,7 @@
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
- self.snapshots_client_latest = self.snapshots_v3_client
- self.backups_client_latest = self.backups_v3_client
+ # ****************Deprecated alias end here***********************
def _set_object_storage_clients(self):
self.account_client = self.object_storage.AccountClient()
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 27e1bc1..83cf42c 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -315,7 +315,7 @@
class VolumeQuotaService(BaseService):
def __init__(self, manager, **kwargs):
super(VolumeQuotaService, self).__init__(kwargs)
- self.client = manager.volume_quotas_v2_client
+ self.client = manager.volume_quotas_client_latest
def delete(self):
client = self.client
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 68c4a10..f2730b3 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -165,7 +165,7 @@
if volume_backed:
volume_name = data_utils.rand_name(__name__ + '-volume')
- volumes_client = clients.volumes_v2_client
+ volumes_client = clients.volumes_client_latest
params = {'name': volume_name,
'imageRef': image_id,
'size': CONF.volume.volume_size}
diff --git a/tempest/config.py b/tempest/config.py
index cc0ba34..0f3fa54 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -845,10 +845,29 @@
"removed."),
cfg.BoolOpt('api_v2',
default=True,
- help="Is the v2 volume API enabled"),
+ help="Is the v2 volume API enabled",
+ deprecated_for_removal=True,
+ deprecated_reason="The v2 volume API has been deprecated "
+ "since Pike release. Now Tempest run all "
+ "the volume tests against v2 or v3 API "
+ "based on CONF.volume.catalog_type which "
+ "makes this config option unusable. If "
+ "catalog_type is volumev2, then all the "
+ "volume tests will run against v2 API. "
+ "Use ``CONF.volume.catalog_type`` to run "
+ "the Tempest against volume v2 or v3 API"),
cfg.BoolOpt('api_v3',
default=True,
- help="Is the v3 volume API enabled"),
+ help="Is the v3 volume API enabled",
+ deprecated_for_removal=True,
+ deprecated_reason="Tempest run all the volume tests against "
+ "v2 or v3 API based on "
+ "CONF.volume.catalog_type which makes this "
+ "config option unusable. If catalog_type is "
+ "volumev3 which is default, then all the "
+ "volume tests will run against v3 API. "
+ "Use ``CONF.volume.catalog_type`` to run "
+ "the Tempest against volume v2 or v3 API"),
cfg.BoolOpt('extend_attached_volume',
default=False,
help='Does the cloud support extending the size of a volume '
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 20ba34e..2c40cb1 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -34,6 +34,9 @@
METHOD_DELETE_RESOURCE = re.compile(r"^\s*def delete_.+")
CLASS = re.compile(r"^class .+")
EX_ATTRIBUTE = re.compile(r'(\s+|\()(e|ex|exc|exception).message(\s+|\))')
+NEGATIVE_TEST_DECORATOR = re.compile(
+ r'\s*@decorators\.attr\(type=.*negative.*\)')
+_HAVE_NEGATIVE_DECORATOR = False
def import_no_clients_in_api_and_scenario_tests(physical_line, filename):
@@ -306,6 +309,29 @@
yield(0, msg)
+def negative_test_attribute_always_applied_to_negative_tests(physical_line,
+ filename):
+ """Check ``@decorators.attr(type=['negative'])`` applied to negative tests.
+
+ T117
+ """
+ global _HAVE_NEGATIVE_DECORATOR
+
+ if re.match(r'.\/tempest\/api\/.*_negative.*', filename):
+
+ if NEGATIVE_TEST_DECORATOR.match(physical_line):
+ _HAVE_NEGATIVE_DECORATOR = True
+ return
+
+ if TEST_DEFINITION.match(physical_line):
+ if not _HAVE_NEGATIVE_DECORATOR:
+ return (
+ 0, "T117: Must apply `@decorators.attr(type=['negative'])`"
+ " to all negative API tests"
+ )
+ _HAVE_NEGATIVE_DECORATOR = False
+
+
def factory(register):
register(import_no_clients_in_api_and_scenario_tests)
register(scenario_tests_need_service_tags)
@@ -322,3 +348,4 @@
register(use_rand_uuid_instead_of_uuid4)
register(dont_put_admin_tests_on_nonadmin_path)
register(unsupported_exception_attribute_PY3)
+ register(negative_test_attribute_always_applied_to_negative_tests)
diff --git a/tempest/lib/api_schema/response/compute/v2_1/flavors.py b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
index 43e80cc..bd5e3d6 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/flavors.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
@@ -60,7 +60,7 @@
},
'additionalProperties': False,
# 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and
- # 'OS-FLV-EXT-DATA' are API extensions. So they are not 'required'.
+ # 'OS-FLV-EXT-DATA' are API extensions, so they are not 'required'.
'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id']
}
@@ -74,7 +74,7 @@
'items': common_flavor_info
},
# NOTE(gmann): flavors_links attribute is not necessary
- # to be present always So it is not 'required'.
+ # to be present always so it is not 'required'.
'flavors_links': parameter_types.links
},
'additionalProperties': False,
diff --git a/tempest/lib/api_schema/response/compute/v2_1/server_groups.py b/tempest/lib/api_schema/response/compute/v2_1/server_groups.py
new file mode 100644
index 0000000..01db20b
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/server_groups.py
@@ -0,0 +1,65 @@
+# Copyright 2017 NTT 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.
+
+common_server_group = {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': 'string'},
+ 'name': {'type': 'string'},
+ 'policies': {
+ 'type': 'array',
+ 'items': {'type': 'string'}
+ },
+ # 'members' attribute contains the array of instance's UUID of
+ # instances present in server group
+ 'members': {
+ 'type': 'array',
+ 'items': {'type': 'string'}
+ },
+ 'metadata': {'type': 'object'}
+ },
+ 'additionalProperties': False,
+ 'required': ['id', 'name', 'policies', 'members', 'metadata']
+}
+
+create_show_server_group = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'server_group': common_server_group
+ },
+ 'additionalProperties': False,
+ 'required': ['server_group']
+ }
+}
+
+delete_server_group = {
+ 'status_code': [204]
+}
+
+list_server_groups = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'server_groups': {
+ 'type': 'array',
+ 'items': common_server_group
+ }
+ },
+ 'additionalProperties': False,
+ 'required': ['server_groups']
+ }
+}
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 2954de0..3300298 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -345,58 +345,6 @@
}
}
-common_server_group = {
- 'type': 'object',
- 'properties': {
- 'id': {'type': 'string'},
- 'name': {'type': 'string'},
- 'policies': {
- 'type': 'array',
- 'items': {'type': 'string'}
- },
- # 'members' attribute contains the array of instance's UUID of
- # instances present in server group
- 'members': {
- 'type': 'array',
- 'items': {'type': 'string'}
- },
- 'metadata': {'type': 'object'}
- },
- 'additionalProperties': False,
- 'required': ['id', 'name', 'policies', 'members', 'metadata']
-}
-
-create_show_server_group = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'server_group': common_server_group
- },
- 'additionalProperties': False,
- 'required': ['server_group']
- }
-}
-
-delete_server_group = {
- 'status_code': [204]
-}
-
-list_server_groups = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'server_groups': {
- 'type': 'array',
- 'items': common_server_group
- }
- },
- 'additionalProperties': False,
- 'required': ['server_groups']
- }
-}
-
instance_actions = {
'type': 'object',
'properties': {
@@ -430,8 +378,9 @@
'traceback': {'type': ['string', 'null']}
},
'additionalProperties': False,
- 'required': ['event', 'start_time', 'finish_time', 'result',
- 'traceback']
+ # NOTE(zhufl): events.traceback can only be seen by admin users
+ # with default policy.json, so it shouldn't be a required field.
+ 'required': ['event', 'start_time', 'finish_time', 'result']
}
}
diff --git a/tempest/lib/api_schema/response/compute/v2_13/servers.py b/tempest/lib/api_schema/response/compute/v2_13/server_groups.py
similarity index 69%
rename from tempest/lib/api_schema/response/compute/v2_13/servers.py
rename to tempest/lib/api_schema/response/compute/v2_13/server_groups.py
index a90f3e4..5cb4241 100644
--- a/tempest/lib/api_schema/response/compute/v2_13/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_13/server_groups.py
@@ -14,21 +14,24 @@
import copy
-from tempest.lib.api_schema.response.compute.v2_1 import servers
+from tempest.lib.api_schema.response.compute.v2_1 import server_groups
-
-common_server_group = copy.deepcopy(servers.common_server_group)
+# Compute microversion 2.13:
+# 1. New attributes in 'server_group' dict.
+# 'project_id', 'user_id'
+common_server_group = copy.deepcopy(server_groups.common_server_group)
common_server_group['properties']['project_id'] = {'type': 'string'}
common_server_group['properties']['user_id'] = {'type': 'string'}
common_server_group['required'].append('project_id')
common_server_group['required'].append('user_id')
-create_show_server_group = copy.deepcopy(servers.create_show_server_group)
+create_show_server_group = copy.deepcopy(
+ server_groups.create_show_server_group)
create_show_server_group['response_body']['properties'][
'server_group'] = common_server_group
-delete_server_group = copy.deepcopy(servers.delete_server_group)
+delete_server_group = copy.deepcopy(server_groups.delete_server_group)
-list_server_groups = copy.deepcopy(servers.list_server_groups)
+list_server_groups = copy.deepcopy(server_groups.list_server_groups)
list_server_groups['response_body']['properties']['server_groups'][
'items'] = common_server_group
diff --git a/tempest/lib/api_schema/response/compute/v2_16/servers.py b/tempest/lib/api_schema/response/compute/v2_16/servers.py
index 3eb658f..72b84f5 100644
--- a/tempest/lib/api_schema/response/compute/v2_16/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_16/servers.py
@@ -122,7 +122,7 @@
'^[a-zA-Z0-9-_.]+$']['items']['properties'].update({
'OS-EXT-IPS:type': {'type': 'string'},
'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address})
-# NOTE(gmann)dd: Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
+# NOTE(gmann): Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
# attributes in server address. Those are API extension,
# and some environments return a response without
# these attributes. So they are not 'required'.
@@ -157,4 +157,14 @@
}
}
+# NOTE(gmann): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.9 ******
list_servers = copy.deepcopy(servers.list_servers)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
diff --git a/tempest/lib/api_schema/response/compute/v2_19/servers.py b/tempest/lib/api_schema/response/compute/v2_19/servers.py
index fd9e933..e3e8ad1 100644
--- a/tempest/lib/api_schema/response/compute/v2_19/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -16,10 +16,10 @@
from tempest.lib.api_schema.response.compute.v2_16 import servers \
as serversv216
-from tempest.lib.api_schema.response.compute.v2_9 import servers as serversv29
-list_servers = copy.deepcopy(serversv216.list_servers)
-
+# Compute microversion 2.19:
+# 1. New attributes in 'server' dict.
+# 'description'
get_server = copy.deepcopy(serversv216.get_server)
get_server['response_body']['properties']['server'][
'properties'].update({'description': {'type': ['string', 'null']}})
@@ -32,21 +32,29 @@
list_servers_detail['response_body']['properties']['servers']['items'][
'required'].append('description')
-update_server = copy.deepcopy(serversv29.update_server)
+update_server = copy.deepcopy(serversv216.update_server)
update_server['response_body']['properties']['server'][
'properties'].update({'description': {'type': ['string', 'null']}})
update_server['response_body']['properties']['server'][
'required'].append('description')
-rebuild_server = copy.deepcopy(serversv29.rebuild_server)
+rebuild_server = copy.deepcopy(serversv216.rebuild_server)
rebuild_server['response_body']['properties']['server'][
'properties'].update({'description': {'type': ['string', 'null']}})
rebuild_server['response_body']['properties']['server'][
'required'].append('description')
rebuild_server_with_admin_pass = copy.deepcopy(
- serversv29.rebuild_server_with_admin_pass)
+ serversv216.rebuild_server_with_admin_pass)
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'properties'].update({'description': {'type': ['string', 'null']}})
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('description')
+
+# NOTE(gmann): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.16 ******
+list_servers = copy.deepcopy(serversv216.list_servers)
+show_server_diagnostics = copy.deepcopy(serversv216.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(serversv216.get_remote_consoles)
diff --git a/tempest/lib/api_schema/response/compute/v2_26/servers.py b/tempest/lib/api_schema/response/compute/v2_26/servers.py
index 5c35eab..8e62dc3 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -15,7 +15,6 @@
import copy
-from tempest.lib.api_schema.response.compute.v2_1 import servers as servers21
from tempest.lib.api_schema.response.compute.v2_19 import servers as servers219
# The 2.26 microversion changes the server GET and (detailed) LIST responses to
@@ -62,10 +61,6 @@
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('tags')
-# list response schema wasn't changed for v2.26 so use v2.1
-
-list_servers = copy.deepcopy(servers21.list_servers)
-
list_tags = {
'status_code': [200],
'response_body': {
@@ -98,3 +93,11 @@
}
delete_tag = {'status_code': [204]}
+
+# NOTE(gmann): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.19 ******
+list_servers = copy.deepcopy(servers219.list_servers)
+show_server_diagnostics = copy.deepcopy(servers219.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers219.get_remote_consoles)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_28/__init__.py
similarity index 100%
rename from tempest/tests/lib/services/volume/v2/__init__.py
rename to tempest/lib/api_schema/response/compute/v2_28/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_28/hypervisors.py b/tempest/lib/api_schema/response/compute/v2_28/hypervisors.py
new file mode 100644
index 0000000..8ea9ff8
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_28/hypervisors.py
@@ -0,0 +1,40 @@
+# Copyright 2018 ZTE 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 hypervisors as hypervisorsv21
+
+# hypervisor.cpu_info change from string to JSON object.
+hypervisor_detail = copy.deepcopy(hypervisorsv21.hypervisor_detail)
+hypervisor_detail['properties'].update({'cpu_info': {'type': 'object'}})
+
+list_hypervisors_detail = copy.deepcopy(hypervisorsv21.list_hypervisors_detail)
+list_hypervisors_detail['response_body']['properties']['hypervisors'].update(
+ {'items': hypervisor_detail})
+
+get_hypervisor = copy.deepcopy(hypervisorsv21.get_hypervisor)
+get_hypervisor['response_body']['properties'].update(
+ {'hypervisor': hypervisor_detail})
+
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.1 ***
+get_hypervisor_statistics = \
+ copy.deepcopy(hypervisorsv21.get_hypervisor_statistics)
+list_search_hypervisors = copy.deepcopy(hypervisorsv21.list_search_hypervisors)
+get_hypervisor_uptime = copy.deepcopy(hypervisorsv21.get_hypervisor_uptime)
+get_hypervisors_servers = copy.deepcopy(hypervisorsv21.get_hypervisors_servers)
diff --git a/tempest/lib/api_schema/response/compute/v2_3/servers.py b/tempest/lib/api_schema/response/compute/v2_3/servers.py
index f24103e..18fb352 100644
--- a/tempest/lib/api_schema/response/compute/v2_3/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_3/servers.py
@@ -128,7 +128,7 @@
'^[a-zA-Z0-9-_.]+$']['items']['properties'].update({
'OS-EXT-IPS:type': {'type': 'string'},
'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address})
-# NOTE(gmann)dd: Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
+# NOTE(gmann): Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
# attributes in server address. Those are API extension,
# and some environments return a response without
# these attributes. So they are not 'required'.
@@ -163,4 +163,13 @@
}
}
+# NOTE: Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.1 ***
list_servers = copy.deepcopy(servers.list_servers)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_36/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_36/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_36/limits.py b/tempest/lib/api_schema/response/compute/v2_36/limits.py
new file mode 100644
index 0000000..8e94690
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_36/limits.py
@@ -0,0 +1,35 @@
+# Copyright 2018 ZTE 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 limits as limitv21
+
+# Compute microversion 2.36:
+# remove attributes in get_limit:
+# 'maxSecurityGroupRules',
+# 'maxSecurityGroups',
+# 'maxTotalFloatingIps',
+# 'totalFloatingIpsUsed',
+# 'totalSecurityGroupsUsed'
+
+get_limit = copy.deepcopy(limitv21.get_limit)
+
+for item in ['maxSecurityGroupRules', 'maxSecurityGroups',
+ 'maxTotalFloatingIps', 'totalFloatingIpsUsed',
+ 'totalSecurityGroupsUsed']:
+ get_limit['response_body']['properties']['limits']['properties'][
+ 'absolute']['properties'].pop(item)
+ get_limit['response_body']['properties']['limits']['properties'][
+ 'absolute']['required'].remove(item)
diff --git a/tempest/lib/api_schema/response/compute/v2_36/quotas.py b/tempest/lib/api_schema/response/compute/v2_36/quotas.py
new file mode 100644
index 0000000..f191ed1
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_36/quotas.py
@@ -0,0 +1,54 @@
+# Copyright 2018 ZTE 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 quotas as quotasv21
+
+# Compute microversion 2.36:
+# remove attributes in quota_set:
+# 'fixed_ips',
+# 'floating_ips',
+# 'security_group_rules',
+# 'security_groups'
+
+remove_item_list = ['fixed_ips', 'floating_ips',
+ 'security_group_rules', 'security_groups']
+
+update_quota_set = copy.deepcopy(quotasv21.update_quota_set)
+for item in remove_item_list:
+ update_quota_set['response_body']['properties']['quota_set'][
+ 'properties'].pop(item)
+ update_quota_set['response_body']['properties']['quota_set'][
+ 'required'].remove(item)
+
+get_quota_set = copy.deepcopy(quotasv21.get_quota_set)
+for item in remove_item_list:
+ get_quota_set['response_body']['properties']['quota_set'][
+ 'properties'].pop(item)
+ get_quota_set['response_body']['properties']['quota_set'][
+ 'required'].remove(item)
+
+get_quota_set_details = copy.deepcopy(quotasv21.get_quota_set_details)
+for item in remove_item_list:
+ get_quota_set_details['response_body']['properties']['quota_set'][
+ 'properties'].pop(item)
+ get_quota_set_details['response_body']['properties']['quota_set'][
+ 'required'].remove(item)
+
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_quota = copy.deepcopy(quotasv21.delete_quota)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_39/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_39/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_39/limits.py b/tempest/lib/api_schema/response/compute/v2_39/limits.py
new file mode 100644
index 0000000..3df6616
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_39/limits.py
@@ -0,0 +1,29 @@
+# Copyright 2018 ZTE 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_36 import limits as limitv236
+
+# Compute microversion 2.39:
+# remove attributes in get_limit:
+# 'maxImageMeta'
+
+get_limit = copy.deepcopy(limitv236.get_limit)
+
+get_limit['response_body']['properties']['limits']['properties']['absolute'][
+ 'properties'].pop('maxImageMeta')
+
+get_limit['response_body']['properties']['limits']['properties']['absolute'][
+ 'required'].remove('maxImageMeta')
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_41/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_41/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_41/aggregates.py b/tempest/lib/api_schema/response/compute/v2_41/aggregates.py
new file mode 100644
index 0000000..036bd83
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_41/aggregates.py
@@ -0,0 +1,54 @@
+# Copyright 2018 ZTE 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 aggregates
+
+# 'uuid' of an aggregate is returned in microversion 2.41
+aggregate_for_create = copy.deepcopy(aggregates.aggregate_for_create)
+aggregate_for_create['properties'].update({'uuid': {'type': 'string',
+ 'format': 'uuid'}})
+aggregate_for_create['required'].append('uuid')
+
+common_aggregate_info = copy.deepcopy(aggregates.common_aggregate_info)
+common_aggregate_info['properties'].update({'uuid': {'type': 'string',
+ 'format': 'uuid'}})
+common_aggregate_info['required'].append('uuid')
+
+list_aggregates = copy.deepcopy(aggregates.list_aggregates)
+list_aggregates['response_body']['properties']['aggregates'].update(
+ {'items': common_aggregate_info})
+
+get_aggregate = copy.deepcopy(aggregates.get_aggregate)
+get_aggregate['response_body']['properties'].update(
+ {'aggregate': common_aggregate_info})
+
+aggregate_set_metadata = get_aggregate
+
+update_aggregate = copy.deepcopy(aggregates.update_aggregate)
+update_aggregate['response_body']['properties'].update(
+ {'aggregate': common_aggregate_info})
+
+create_aggregate = copy.deepcopy(aggregates.create_aggregate)
+create_aggregate['response_body']['properties'].update(
+ {'aggregate': aggregate_for_create})
+
+aggregate_add_remove_host = get_aggregate
+
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_aggregate = copy.deepcopy(aggregates.delete_aggregate)
diff --git a/tempest/lib/api_schema/response/compute/v2_47/servers.py b/tempest/lib/api_schema/response/compute/v2_47/servers.py
index 5d6d4c3..0fbacd3 100644
--- a/tempest/lib/api_schema/response/compute/v2_47/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_47/servers.py
@@ -53,3 +53,16 @@
servers226.rebuild_server_with_admin_pass)
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'properties'].update({'flavor': flavor})
+
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+show_server_diagnostics = copy.deepcopy(servers226.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers226.get_remote_consoles)
+list_tags = copy.deepcopy(servers226.list_tags)
+update_all_tags = copy.deepcopy(servers226.update_all_tags)
+delete_all_tags = copy.deepcopy(servers226.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers226.check_tag_existence)
+update_tag = copy.deepcopy(servers226.update_tag)
+delete_tag = copy.deepcopy(servers226.delete_tag)
+list_servers = copy.deepcopy(servers226.list_servers)
diff --git a/tempest/lib/api_schema/response/compute/v2_48/servers.py b/tempest/lib/api_schema/response/compute/v2_48/servers.py
index 5904758..84b5a2a 100644
--- a/tempest/lib/api_schema/response/compute/v2_48/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_48/servers.py
@@ -112,4 +112,20 @@
}
}
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+list_servers = copy.deepcopy(servers247.list_servers)
+get_remote_consoles = copy.deepcopy(servers247.get_remote_consoles)
+list_tags = copy.deepcopy(servers247.list_tags)
+update_all_tags = copy.deepcopy(servers247.update_all_tags)
+delete_all_tags = copy.deepcopy(servers247.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers247.check_tag_existence)
+update_tag = copy.deepcopy(servers247.update_tag)
+delete_tag = copy.deepcopy(servers247.delete_tag)
get_server = copy.deepcopy(servers247.get_server)
+list_servers_detail = copy.deepcopy(servers247.list_servers_detail)
+update_server = copy.deepcopy(servers247.update_server)
+rebuild_server = copy.deepcopy(servers247.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers247.rebuild_server_with_admin_pass)
diff --git a/tempest/lib/api_schema/response/compute/v2_53/services.py b/tempest/lib/api_schema/response/compute/v2_53/services.py
index aa132a9..97b0c72 100644
--- a/tempest/lib/api_schema/response/compute/v2_53/services.py
+++ b/tempest/lib/api_schema/response/compute/v2_53/services.py
@@ -42,7 +42,8 @@
'properties': {
'id': {'type': 'string', 'format': 'uuid'},
'binary': {'type': 'string'},
- 'disabled_reason': {'type': 'string'},
+ # disabled_reason can be null when status is enabled.
+ 'disabled_reason': {'type': ['string', 'null']},
'host': {'type': 'string'},
'state': {'type': 'string'},
'status': {'type': 'string'},
diff --git a/tempest/lib/api_schema/response/compute/v2_54/servers.py b/tempest/lib/api_schema/response/compute/v2_54/servers.py
index c084696..099e1b8 100644
--- a/tempest/lib/api_schema/response/compute/v2_54/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_54/servers.py
@@ -12,7 +12,7 @@
import copy
-from tempest.lib.api_schema.response.compute.v2_47 import servers as servers247
+from tempest.lib.api_schema.response.compute.v2_48 import servers as servers248
# ****** Schemas changed in microversion 2.54 *****************
# Note(gmann): This is schema for microversion 2.54 which includes the
@@ -26,24 +26,32 @@
]
}
-rebuild_server = copy.deepcopy(servers247.rebuild_server)
+rebuild_server = copy.deepcopy(servers248.rebuild_server)
rebuild_server['response_body']['properties']['server'][
'properties'].update({'key_name': key_name})
rebuild_server['response_body']['properties']['server'][
'required'].append('key_name')
rebuild_server_with_admin_pass = copy.deepcopy(
- servers247.rebuild_server_with_admin_pass)
+ servers248.rebuild_server_with_admin_pass)
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'properties'].update({'key_name': key_name})
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('key_name')
-# ****** Schemas unchanged in microversion 2.54 since microversion 2.47 ***
-
# NOTE(gmann): Below are the unchanged schema in this microversion. We need
# to keep this schema in this file to have the generic way to select the
# right schema based on self.schema_versions_info mapping in service client.
-get_server = copy.deepcopy(servers247.get_server)
-list_servers_detail = copy.deepcopy(servers247.list_servers_detail)
-update_server = copy.deepcopy(servers247.update_server)
+# ****** Schemas unchanged in microversion 2.54 since microversion 2.48 ***
+get_server = copy.deepcopy(servers248.get_server)
+list_servers_detail = copy.deepcopy(servers248.list_servers_detail)
+update_server = copy.deepcopy(servers248.update_server)
+list_servers = copy.deepcopy(servers248.list_servers)
+show_server_diagnostics = copy.deepcopy(servers248.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers248.get_remote_consoles)
+list_tags = copy.deepcopy(servers248.list_tags)
+update_all_tags = copy.deepcopy(servers248.update_all_tags)
+delete_all_tags = copy.deepcopy(servers248.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers248.check_tag_existence)
+update_tag = copy.deepcopy(servers248.update_tag)
+delete_tag = copy.deepcopy(servers248.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_55/flavors.py b/tempest/lib/api_schema/response/compute/v2_55/flavors.py
index 823190a..554f43b 100644
--- a/tempest/lib/api_schema/response/compute/v2_55/flavors.py
+++ b/tempest/lib/api_schema/response/compute/v2_55/flavors.py
@@ -11,7 +11,9 @@
# 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 flavors as flavorsv21
from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
# Note(gmann): This is schema for microversion 2.55 which includes the
@@ -110,3 +112,9 @@
'required': ['flavor']
}
}
+
+# Note(zhufl): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_flavor = copy.deepcopy(flavorsv21.delete_flavor)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/limits.py b/tempest/lib/api_schema/response/compute/v2_57/limits.py
new file mode 100644
index 0000000..dcb8b3d
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_57/limits.py
@@ -0,0 +1,30 @@
+# Copyright 2018 ZTE 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_39 import limits as limitv239
+
+# Compute microversion 2.57:
+# remove attributes in get_limit:
+# 'maxPersonality',
+# 'maxPersonalitySize'
+
+get_limit = copy.deepcopy(limitv239.get_limit)
+
+for item in ['maxPersonality', 'maxPersonalitySize']:
+ get_limit['response_body']['properties']['limits']['properties'][
+ 'absolute']['properties'].pop(item)
+ get_limit['response_body']['properties']['limits']['properties'][
+ 'absolute']['required'].remove(item)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/quotas.py b/tempest/lib/api_schema/response/compute/v2_57/quotas.py
new file mode 100644
index 0000000..4664a1a
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_57/quotas.py
@@ -0,0 +1,53 @@
+# Copyright 2018 ZTE 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_36 import quotas as quotasv236
+
+# Compute microversion 2.57:
+# remove attributes in quota_set:
+# 'injected_file_content_bytes',
+# 'injected_file_path_bytes',
+# 'injected_files'
+
+remove_item_list = ['injected_file_content_bytes', 'injected_file_path_bytes',
+ 'injected_files']
+
+update_quota_set = copy.deepcopy(quotasv236.update_quota_set)
+for item in remove_item_list:
+ update_quota_set['response_body']['properties']['quota_set'][
+ 'properties'].pop(item)
+ update_quota_set['response_body']['properties']['quota_set'][
+ 'required'].remove(item)
+
+get_quota_set = copy.deepcopy(quotasv236.get_quota_set)
+for item in remove_item_list:
+ get_quota_set['response_body']['properties']['quota_set'][
+ 'properties'].pop(item)
+ get_quota_set['response_body']['properties']['quota_set'][
+ 'required'].remove(item)
+
+get_quota_set_details = copy.deepcopy(quotasv236.get_quota_set_details)
+for item in remove_item_list:
+ get_quota_set_details['response_body']['properties']['quota_set'][
+ 'properties'].pop(item)
+ get_quota_set_details['response_body']['properties']['quota_set'][
+ 'required'].remove(item)
+
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_quota = copy.deepcopy(quotasv236.delete_quota)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/servers.py b/tempest/lib/api_schema/response/compute/v2_57/servers.py
index ed1ca7d..0099a2b 100644
--- a/tempest/lib/api_schema/response/compute/v2_57/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_57/servers.py
@@ -43,11 +43,19 @@
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('user_data')
-# ****** Schemas unchanged in microversion 2.57 since microversion 2.54 ***
-
# NOTE(gmann): Below are the unchanged schema in this microversion. We need
-# to keeo this schema in this file to have the generic way to select the
+# to keep this schema in this file to have the generic way to select the
# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged in microversion 2.57 since microversion 2.54 ***
get_server = copy.deepcopy(servers254.get_server)
list_servers_detail = copy.deepcopy(servers254.list_servers_detail)
update_server = copy.deepcopy(servers254.update_server)
+list_servers = copy.deepcopy(servers254.list_servers)
+show_server_diagnostics = copy.deepcopy(servers254.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers254.get_remote_consoles)
+list_tags = copy.deepcopy(servers254.list_tags)
+update_all_tags = copy.deepcopy(servers254.update_all_tags)
+delete_all_tags = copy.deepcopy(servers254.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers254.check_tag_existence)
+update_tag = copy.deepcopy(servers254.update_tag)
+delete_tag = copy.deepcopy(servers254.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_6/servers.py b/tempest/lib/api_schema/response/compute/v2_6/servers.py
index 29b3e86..d5774de 100644
--- a/tempest/lib/api_schema/response/compute/v2_6/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_6/servers.py
@@ -16,9 +16,18 @@
from tempest.lib.api_schema.response.compute.v2_3 import servers
+# NOTE: Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.3 ******
list_servers = copy.deepcopy(servers.list_servers)
get_server = copy.deepcopy(servers.get_server)
list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
# NOTE: The consolidated remote console API got introduced with v2.6
# with bp/consolidate-console-api. See Nova commit 578bafeda
@@ -31,7 +40,7 @@
'type': 'object',
'properties': {
'protocol': {'enum': ['vnc', 'rdp', 'serial', 'spice']},
- 'type': {'enum': ['novnc', 'xpvnc', 'rdp-html5',
+ 'type': {'enum': ['novnc', 'xvpvnc', 'rdp-html5',
'spice-html5', 'serial']},
'url': {
'type': 'string',
diff --git a/tempest/lib/api_schema/response/compute/v2_61/flavors.py b/tempest/lib/api_schema/response/compute/v2_61/flavors.py
index 381fb64..5119466 100644
--- a/tempest/lib/api_schema/response/compute/v2_61/flavors.py
+++ b/tempest/lib/api_schema/response/compute/v2_61/flavors.py
@@ -99,4 +99,8 @@
# Note(gmann): Below are the unchanged schema in this microversion. We need
# to keep this schema in this file to have the generic way to select the
# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.55 ***
list_flavors = copy.deepcopy(flavorsv255.list_flavors)
+
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_flavor = copy.deepcopy(flavorsv255.delete_flavor)
diff --git a/tempest/lib/api_schema/response/compute/v2_63/servers.py b/tempest/lib/api_schema/response/compute/v2_63/servers.py
index 5cdaf54..3c3d41c 100644
--- a/tempest/lib/api_schema/response/compute/v2_63/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_63/servers.py
@@ -12,8 +12,6 @@
import copy
-from tempest.lib.api_schema.response.compute.v2_26 import servers as servers226
-from tempest.lib.api_schema.response.compute.v2_54 import servers as servers254
from tempest.lib.api_schema.response.compute.v2_57 import servers as servers257
# Nova microversion 2.63 adds 'trusted_image_certificates' (a list of
@@ -30,10 +28,8 @@
'minLength': 1
}
}
-# list response schema wasn't changed for v2.63 so use v2.26
-list_servers = copy.deepcopy(servers226.list_servers)
-list_servers_detail = copy.deepcopy(servers254.list_servers_detail)
+list_servers_detail = copy.deepcopy(servers257.list_servers_detail)
list_servers_detail['response_body']['properties']['servers']['items'][
'properties'].update({'trusted_image_certificates': trusted_certs})
list_servers_detail['response_body']['properties']['servers']['items'][
@@ -52,14 +48,28 @@
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('trusted_image_certificates')
-update_server = copy.deepcopy(servers254.update_server)
+update_server = copy.deepcopy(servers257.update_server)
update_server['response_body']['properties']['server'][
'properties'].update({'trusted_image_certificates': trusted_certs})
update_server['response_body']['properties']['server'][
'required'].append('trusted_image_certificates')
-get_server = copy.deepcopy(servers254.get_server)
+get_server = copy.deepcopy(servers257.get_server)
get_server['response_body']['properties']['server'][
'properties'].update({'trusted_image_certificates': trusted_certs})
get_server['response_body']['properties']['server'][
'required'].append('trusted_image_certificates')
+
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.57 ***
+list_servers = copy.deepcopy(servers257.list_servers)
+show_server_diagnostics = copy.deepcopy(servers257.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers257.get_remote_consoles)
+list_tags = copy.deepcopy(servers257.list_tags)
+update_all_tags = copy.deepcopy(servers257.update_all_tags)
+delete_all_tags = copy.deepcopy(servers257.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers257.check_tag_existence)
+update_tag = copy.deepcopy(servers257.update_tag)
+delete_tag = copy.deepcopy(servers257.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py
index 7df02d5..f412839 100644
--- a/tempest/lib/api_schema/response/compute/v2_9/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -17,7 +17,13 @@
from tempest.lib.api_schema.response.compute.v2_1 import servers as servers_21
from tempest.lib.api_schema.response.compute.v2_6 import servers
+# NOTE: Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.6 ******
list_servers = copy.deepcopy(servers.list_servers)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
get_server = copy.deepcopy(servers.get_server)
get_server['response_body']['properties']['server'][
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
index 3fb56ec..d8c776b 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -269,7 +269,7 @@
:param merge_stderr: if True the stderr buffer is merged into stdout
:type merge_stderr: boolean
"""
- flags += ' --endpoint-type %s' % endpoint_type
+ flags += ' --os-endpoint-type %s' % endpoint_type
return self.cmd_with_auth(
'cinder', action, flags, params, fail_ok, merge_stderr)
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 22276d4..bc9cfe2 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -416,6 +416,8 @@
resp_body=None, extra=None):
if 'X-Auth-Token' in req_headers:
req_headers['X-Auth-Token'] = '<omitted>'
+ if 'X-Subject-Token' in req_headers:
+ req_headers['X-Subject-Token'] = '<omitted>'
# A shallow copy is sufficient
resp_log = resp.copy()
if 'x-subject-token' in resp_log:
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index e99dd24..b399aa0 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -19,39 +19,83 @@
import six
import testtools
+from tempest.lib import exceptions as lib_exc
+
LOG = logging.getLogger(__name__)
+_SUPPORTED_BUG_TYPES = {
+ 'launchpad': 'https://launchpad.net/bugs/%s',
+ 'storyboard': 'https://storyboard.openstack.org/#!/story/%s',
+}
+
+
+def _validate_bug_and_bug_type(bug, bug_type):
+ """Validates ``bug`` and ``bug_type`` values.
+
+ :param bug: bug number causing the test to skip (launchpad or storyboard)
+ :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+ :raises: InvalidParam if ``bug`` is not a digit or ``bug_type`` is not
+ a valid value
+ """
+ if not bug.isdigit():
+ invalid_param = '%s must be a valid %s number' % (bug, bug_type)
+ raise lib_exc.InvalidParam(invalid_param=invalid_param)
+ if bug_type not in _SUPPORTED_BUG_TYPES:
+ invalid_param = 'bug_type "%s" must be one of: %s' % (
+ bug_type, ', '.join(_SUPPORTED_BUG_TYPES.keys()))
+ raise lib_exc.InvalidParam(invalid_param=invalid_param)
+
+
+def _get_bug_url(bug, bug_type='launchpad'):
+ """Get the bug URL based on the ``bug_type`` and ``bug``
+
+ :param bug: The launchpad/storyboard bug number causing the test
+ :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+ :returns: Bug URL corresponding to ``bug_type`` value
+ """
+ _validate_bug_and_bug_type(bug, bug_type)
+ return _SUPPORTED_BUG_TYPES[bug_type] % bug
+
def skip_because(*args, **kwargs):
"""A decorator useful to skip tests hitting known bugs
- @param bug: bug number causing the test to skip
- @param condition: optional condition to be True for the skip to have place
+ ``bug`` must be a number and ``condition`` must be true for the test to
+ skip.
+
+ :param bug: bug number causing the test to skip (launchpad or storyboard)
+ :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+ :param condition: optional condition to be True for the skip to have place
+ :raises: testtools.TestCase.skipException if ``condition`` is True and
+ ``bug`` is included
"""
def decorator(f):
@functools.wraps(f)
def wrapper(*func_args, **func_kwargs):
skip = False
+ msg = ''
if "condition" in kwargs:
if kwargs["condition"] is True:
skip = True
else:
skip = True
if "bug" in kwargs and skip is True:
- if not kwargs['bug'].isdigit():
- raise ValueError('bug must be a valid bug number')
- msg = "Skipped until Bug: %s is resolved." % kwargs["bug"]
+ bug = kwargs['bug']
+ bug_type = kwargs.get('bug_type', 'launchpad')
+ bug_url = _get_bug_url(bug, bug_type)
+ msg = "Skipped until bug: %s is resolved." % bug_url
raise testtools.TestCase.skipException(msg)
return f(*func_args, **func_kwargs)
return wrapper
return decorator
-def related_bug(bug, status_code=None):
- """A decorator useful to know solutions from launchpad bug reports
+def related_bug(bug, status_code=None, bug_type='launchpad'):
+ """A decorator useful to know solutions from launchpad/storyboard reports
- @param bug: The launchpad bug number causing the test
- @param status_code: The status code related to the bug report
+ :param bug: The launchpad/storyboard bug number causing the test bug
+ :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+ :param status_code: The status code related to the bug report
"""
def decorator(f):
@functools.wraps(f)
@@ -61,9 +105,10 @@
except Exception as exc:
exc_status_code = getattr(exc, 'status_code', None)
if status_code is None or status_code == exc_status_code:
- LOG.error('Hints: This test was made for the bug %s. '
- 'The failure could be related to '
- 'https://launchpad.net/bugs/%s', bug, bug)
+ if bug:
+ LOG.error('Hints: This test was made for the bug_type '
+ '%s. The failure could be related to '
+ '%s', bug, _get_bug_url(bug, bug_type))
raise exc
return wrapper
return decorator
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index 8918a8c..833cfd6 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -331,7 +331,7 @@
self.region = region
# Check if passed or default credentials are valid
if not self.credentials.is_valid():
- raise exceptions.InvalidCredentials()
+ raise exceptions.InvalidCredentials(credentials)
# Get the identity classes matching the provided credentials
# TODO(andreaf) Define a new interface in Credentials to get
# the API version from an instance
@@ -340,7 +340,9 @@
isinstance(self.credentials, auth.IDENTITY_VERSION[k][0])]
# Zero matches or more than one are both not valid.
if len(identity) != 1:
- raise exceptions.InvalidCredentials()
+ msg = "Zero or %d ambiguous auth provider found. identity: %s, " \
+ "credentials: %s" % (len(identity), identity, credentials)
+ raise exceptions.InvalidCredentials(msg)
self.auth_version, auth_provider_class = identity[0]
self.dscv = disable_ssl_certificate_validation
self.ca_certs = ca_certs
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
index 713d7a3..57f5e4e 100644
--- a/tempest/lib/services/compute/aggregates_client.py
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -15,7 +15,10 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.api_schema.response.compute.v2_1 import aggregates as schema
+from tempest.lib.api_schema.response.compute.v2_1 \
+ import aggregates as schema
+from tempest.lib.api_schema.response.compute.v2_41 \
+ import aggregates as schemav241
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
from tempest.lib.services.compute import base_compute_client
@@ -23,10 +26,15 @@
class AggregatesClient(base_compute_client.BaseComputeClient):
+ schema_versions_info = [
+ {'min': None, 'max': '2.40', 'schema': schema},
+ {'min': '2.41', 'max': None, 'schema': schemav241}]
+
def list_aggregates(self):
"""Get aggregate list."""
resp, body = self.get("os-aggregates")
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.list_aggregates, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -34,6 +42,7 @@
"""Get details of the given aggregate."""
resp, body = self.get("os-aggregates/%s" % aggregate_id)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_aggregate, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -48,6 +57,7 @@
resp, body = self.post('os-aggregates', post_body)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.create_aggregate, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -62,12 +72,14 @@
resp, body = self.put('os-aggregates/%s' % aggregate_id, put_body)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.update_aggregate, resp, body)
return rest_client.ResponseBody(resp, body)
def delete_aggregate(self, aggregate_id):
"""Delete the given aggregate."""
resp, body = self.delete("os-aggregates/%s" % aggregate_id)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.delete_aggregate, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -94,6 +106,7 @@
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
post_body)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.aggregate_add_remove_host, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -108,6 +121,7 @@
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
post_body)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.aggregate_add_remove_host, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -122,5 +136,6 @@
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
post_body)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.aggregate_set_metadata, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/hypervisor_client.py b/tempest/lib/services/compute/hypervisor_client.py
index 23c304e..1cbfcc3 100644
--- a/tempest/lib/services/compute/hypervisor_client.py
+++ b/tempest/lib/services/compute/hypervisor_client.py
@@ -15,16 +15,24 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.api_schema.response.compute.v2_1 import hypervisors as schema
+from tempest.lib.api_schema.response.compute.v2_1 \
+ import hypervisors as schemav21
+from tempest.lib.api_schema.response.compute.v2_28 \
+ import hypervisors as schemav228
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
class HypervisorClient(base_compute_client.BaseComputeClient):
+ schema_versions_info = [
+ {'min': None, 'max': '2.27', 'schema': schemav21},
+ {'min': '2.28', 'max': None, 'schema': schemav228}]
+
def list_hypervisors(self, detail=False):
"""List hypervisors information."""
url = 'os-hypervisors'
+ schema = self.get_schema(self.schema_versions_info)
_schema = schema.list_search_hypervisors
if detail:
url += '/detail'
@@ -39,6 +47,7 @@
"""Display the details of the specified hypervisor."""
resp, body = self.get('os-hypervisors/%s' % hypervisor_id)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_hypervisor, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -46,6 +55,7 @@
"""List instances belonging to the specified hypervisor."""
resp, body = self.get('os-hypervisors/%s/servers' % hypervisor_name)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_hypervisors_servers, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -53,6 +63,7 @@
"""Get hypervisor statistics over all compute nodes."""
resp, body = self.get('os-hypervisors/statistics')
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_hypervisor_statistics, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -60,6 +71,7 @@
"""Display the uptime of the specified hypervisor."""
resp, body = self.get('os-hypervisors/%s/uptime' % hypervisor_id)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_hypervisor_uptime, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -69,5 +81,6 @@
"""Search specified hypervisor."""
resp, body = self.get('os-hypervisors/%s/search' % hypervisor_name)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.list_search_hypervisors, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/limits_client.py b/tempest/lib/services/compute/limits_client.py
index efe9889..9af80c4 100644
--- a/tempest/lib/services/compute/limits_client.py
+++ b/tempest/lib/services/compute/limits_client.py
@@ -15,15 +15,25 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.api_schema.response.compute.v2_1 import limits as schema
+from tempest.lib.api_schema.response.compute.v2_1 import limits as schemav21
+from tempest.lib.api_schema.response.compute.v2_36 import limits as schemav236
+from tempest.lib.api_schema.response.compute.v2_39 import limits as schemav239
+from tempest.lib.api_schema.response.compute.v2_57 import limits as schemav257
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
class LimitsClient(base_compute_client.BaseComputeClient):
+ schema_versions_info = [
+ {'min': None, 'max': '2.35', 'schema': schemav21},
+ {'min': '2.36', 'max': '2.38', 'schema': schemav236},
+ {'min': '2.39', 'max': '2.56', 'schema': schemav239},
+ {'min': '2.57', 'max': None, 'schema': schemav257}]
+
def show_limits(self):
resp, body = self.get("limits")
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_limit, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
index 12df895..99c8d0f 100644
--- a/tempest/lib/services/compute/quotas_client.py
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -17,12 +17,19 @@
from six.moves.urllib import parse as urllib
from tempest.lib.api_schema.response.compute.v2_1 import quotas as schema
+from tempest.lib.api_schema.response.compute.v2_36 import quotas as schemav236
+from tempest.lib.api_schema.response.compute.v2_57 import quotas as schemav257
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
class QuotasClient(base_compute_client.BaseComputeClient):
+ schema_versions_info = [
+ {'min': None, 'max': '2.35', 'schema': schema},
+ {'min': '2.36', 'max': '2.56', 'schema': schemav236},
+ {'min': '2.57', 'max': None, 'schema': schemav257}]
+
def show_quota_set(self, tenant_id, user_id=None, detail=False):
"""List the quota set for a tenant.
@@ -42,6 +49,7 @@
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
if detail:
self.validate_response(schema.get_quota_set_details, resp, body)
else:
@@ -57,6 +65,7 @@
url = 'os-quota-sets/%s/defaults' % tenant_id
resp, body = self.get(url)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_quota_set, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -78,6 +87,7 @@
post_body)
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.update_quota_set, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -87,5 +97,6 @@
https://developer.openstack.org/api-ref/compute/#revert-quotas-to-defaults
"""
resp, body = self.delete('os-quota-sets/%s' % tenant_id)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.delete_quota, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
index 03cd645..0d440d5 100644
--- a/tempest/lib/services/compute/server_groups_client.py
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -16,8 +16,10 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
-from tempest.lib.api_schema.response.compute.v2_13 import servers as schemav213
+from tempest.lib.api_schema.response.compute.v2_1 import server_groups \
+ as schema
+from tempest.lib.api_schema.response.compute.v2_13 import server_groups \
+ as schemav213
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
diff --git a/tempest/lib/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index ed6df47..3c38dba 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -32,7 +32,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/index.html#update-an-image
+ https://developer.openstack.org/api-ref/image/v2/#update-image
"""
data = json.dumps(patch)
headers = {"Content-Type": "application/openstack-images-v2.0"
@@ -47,7 +47,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/index.html#create-an-image
+ https://developer.openstack.org/api-ref/image/v2/#create-image
"""
data = json.dumps(kwargs)
resp, body = self.post('images', data)
@@ -84,7 +84,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/#delete-an-image
+ https://developer.openstack.org/api-ref/image/v2/#delete-image
"""
url = 'images/%s' % image_id
resp, _ = self.delete(url)
@@ -96,7 +96,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/#show-images
+ https://developer.openstack.org/api-ref/image/v2/#list-images
"""
url = 'images'
@@ -113,7 +113,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/#show-image-details
+ https://developer.openstack.org/api-ref/image/v2/#show-image
"""
url = 'images/%s' % image_id
resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
index 10538b0..f2d2d21 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -23,7 +23,6 @@
class BackupsClient(base_client.BaseClient):
"""Volume V3 Backups client"""
- api_version = "v3"
def create_backup(self, **kwargs):
"""Creates a backup of volume.
diff --git a/tempest/lib/services/volume/v3/base_client.py b/tempest/lib/services/volume/v3/base_client.py
index e78380b..617da2e 100644
--- a/tempest/lib/services/volume/v3/base_client.py
+++ b/tempest/lib/services/volume/v3/base_client.py
@@ -20,4 +20,3 @@
BaseClient = moves.moved_class(base_client.BaseClient, 'BaseClient', __name__,
version="Pike", removal_version='?')
-BaseClient.api_version = 'v3'
diff --git a/tempest/lib/services/volume/v3/group_snapshots_client.py b/tempest/lib/services/volume/v3/group_snapshots_client.py
index 6e53e3e..16412d3 100644
--- a/tempest/lib/services/volume/v3/group_snapshots_client.py
+++ b/tempest/lib/services/volume/v3/group_snapshots_client.py
@@ -23,7 +23,6 @@
class GroupSnapshotsClient(base_client.BaseClient):
"""Client class to send CRUD Volume Group Snapshot API requests"""
- api_version = 'v3'
def create_group_snapshot(self, **kwargs):
"""Creates a group snapshot.
diff --git a/tempest/lib/services/volume/v3/group_types_client.py b/tempest/lib/services/volume/v3/group_types_client.py
index ecbcba1..1ccb9f8 100644
--- a/tempest/lib/services/volume/v3/group_types_client.py
+++ b/tempest/lib/services/volume/v3/group_types_client.py
@@ -22,7 +22,6 @@
class GroupTypesClient(base_client.BaseClient):
"""Client class to send CRUD Volume V3 Group Types API requests"""
- api_version = 'v3'
@property
def resource_type(self):
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
index e2e477d..3cf1e6a 100644
--- a/tempest/lib/services/volume/v3/groups_client.py
+++ b/tempest/lib/services/volume/v3/groups_client.py
@@ -23,7 +23,6 @@
class GroupsClient(base_client.BaseClient):
"""Client class to send CRUD Volume Group API requests"""
- api_version = 'v3'
def create_group(self, **kwargs):
"""Creates a group.
diff --git a/tempest/lib/services/volume/v3/messages_client.py b/tempest/lib/services/volume/v3/messages_client.py
index 0127271..47538cd 100644
--- a/tempest/lib/services/volume/v3/messages_client.py
+++ b/tempest/lib/services/volume/v3/messages_client.py
@@ -22,7 +22,6 @@
class MessagesClient(base_client.BaseClient):
"""Client class to send user messages API requests."""
- api_version = 'v3'
def show_message(self, message_id):
"""Show details for a single message."""
diff --git a/tempest/lib/services/volume/v3/snapshots_client.py b/tempest/lib/services/volume/v3/snapshots_client.py
index 08e6c94..0cb5e54 100644
--- a/tempest/lib/services/volume/v3/snapshots_client.py
+++ b/tempest/lib/services/volume/v3/snapshots_client.py
@@ -22,7 +22,6 @@
class SnapshotsClient(rest_client.RestClient):
"""Client class to send CRUD Volume Snapshot V3 API requests."""
- api_version = "v3"
create_resp = 202
def list_snapshots(self, detail=False, **params):
diff --git a/tempest/lib/services/volume/v3/versions_client.py b/tempest/lib/services/volume/v3/versions_client.py
index 5702f95..57629bd 100644
--- a/tempest/lib/services/volume/v3/versions_client.py
+++ b/tempest/lib/services/volume/v3/versions_client.py
@@ -22,7 +22,6 @@
class VersionsClient(base_client.BaseClient):
- api_version = 'v3'
def list_versions(self):
"""List API versions
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index a1185c4..11c5767 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -24,7 +24,6 @@
class VolumesClient(base_client.BaseClient):
"""Client class to send CRUD Volume V3 API requests"""
- api_version = "v3"
def _prepare_params(self, params):
"""Prepares params for use in get or _ext_get methods.
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 145dcf1..cdc30b9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -94,6 +94,10 @@
if not client:
client = self.ports_client
name = data_utils.rand_name(self.__class__.__name__)
+ if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
+ kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
+ if CONF.network.port_profile and 'binding:profile' not in kwargs:
+ kwargs['binding:profile'] = CONF.network.port_profile
result = client.create_port(
name=name,
network_id=network_id,
@@ -297,7 +301,7 @@
def create_volume_type(self, client=None, name=None, backend_name=None):
if not client:
- client = self.os_admin.volume_types_v2_client
+ client = self.os_admin.volume_types_client_latest
randomized_name = name or data_utils.rand_name(
'volume-type-' + self.__class__.__name__)
@@ -543,7 +547,7 @@
volume['id'], 'available')
def ping_ip_address(self, ip_address, should_succeed=True,
- ping_timeout=None, mtu=None):
+ ping_timeout=None, mtu=None, server=None):
timeout = ping_timeout or CONF.validation.ping_timeout
cmd = ['ping', '-c1', '-w1']
@@ -577,12 +581,16 @@
'caller': caller, 'ip': ip_address, 'timeout': timeout,
'result': 'expected' if result else 'unexpected'
})
+ if server:
+ self._log_console_output([server])
return result
def check_vm_connectivity(self, ip_address,
username=None,
private_key=None,
should_connect=True,
+ extra_msg="",
+ server=None,
mtu=None):
"""Check server connectivity
@@ -592,43 +600,36 @@
:param should_connect: True/False indicates positive/negative test
positive - attempt ping and ssh
negative - attempt ping and fail if succeed
+ :param extra_msg: Message to help with debugging if ``ping_ip_address``
+ fails
+ :param server: The server whose console to log for debugging
:param mtu: network MTU to use for connectivity validation
:raises: AssertError if the result of the connectivity check does
not match the value of the should_connect param
"""
+ LOG.debug('checking network connections to IP %s with user: %s',
+ ip_address, username)
if should_connect:
msg = "Timed out waiting for %s to become reachable" % ip_address
else:
msg = "ip address %s is reachable" % ip_address
+ if extra_msg:
+ msg = "%s\n%s" % (extra_msg, msg)
self.assertTrue(self.ping_ip_address(ip_address,
should_succeed=should_connect,
- mtu=mtu),
+ mtu=mtu, server=server),
msg=msg)
if should_connect:
# no need to check ssh for negative connectivity
- self.get_remote_client(ip_address, username, private_key)
-
- def check_public_network_connectivity(self, ip_address, username,
- private_key, should_connect=True,
- msg=None, servers=None, mtu=None):
- # The target login is assumed to have been configured for
- # key-based authentication by cloud-init.
- LOG.debug('checking network connections to IP %s with user: %s',
- ip_address, username)
- try:
- self.check_vm_connectivity(ip_address,
- username,
- private_key,
- should_connect=should_connect,
- mtu=mtu)
- except Exception:
- ex_msg = 'Public network connectivity check failed'
- if msg:
- ex_msg += ": " + msg
- LOG.exception(ex_msg)
- self._log_console_output(servers)
- raise
+ try:
+ self.get_remote_client(ip_address, username, private_key,
+ server=server)
+ except Exception:
+ if not extra_msg:
+ extra_msg = 'Failed to ssh to %s' % ip_address
+ LOG.exception(extra_msg)
+ raise
def create_floating_ip(self, thing, pool_name=None):
"""Create a floating IP and associates to a server on Nova"""
@@ -645,9 +646,10 @@
return floating_ip
def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
- private_key=None):
+ private_key=None, server=None):
ssh_client = self.get_remote_client(ip_address,
- private_key=private_key)
+ private_key=private_key,
+ server=server)
if dev_name is not None:
ssh_client.make_fs(dev_name)
ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
@@ -661,9 +663,10 @@
return timestamp
def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
- private_key=None):
+ private_key=None, server=None):
ssh_client = self.get_remote_client(ip_address,
- private_key=private_key)
+ private_key=private_key,
+ server=server)
if dev_name is not None:
ssh_client.mount(dev_name, mount_path)
timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
@@ -809,8 +812,13 @@
return subnet
def _get_server_port_id_and_ip4(self, server, ip_addr=None):
- ports = self.os_admin.ports_client.list_ports(
- device_id=server['id'], fixed_ip=ip_addr)['ports']
+ if ip_addr:
+ ports = self.os_admin.ports_client.list_ports(
+ device_id=server['id'],
+ fixed_ips='ip_address=%s' % ip_addr)['ports']
+ else:
+ ports = self.os_admin.ports_client.list_ports(
+ device_id=server['id'])['ports']
# A port can have more than one IP address in some cases.
# If the network is dual-stack (IPv4 + IPv6), this port is associated
# with 2 subnets
@@ -1200,9 +1208,9 @@
@classmethod
def setup_clients(cls):
super(EncryptionScenarioTest, cls).setup_clients()
- cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
+ cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
cls.admin_encryption_types_client =\
- cls.os_admin.encryption_types_v2_client
+ cls.os_admin.encryption_types_client_latest
def create_encryption_type(self, client=None, type_id=None, provider=None,
key_size=None, cipher=None,
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 87ce951..7452ee6 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -90,9 +90,10 @@
floating_ip_addr = floating_ip['floating_ip_address']
# Check FloatingIP status before checking the connectivity
self.check_floating_ip_status(floating_ip, 'ACTIVE')
- self.check_public_network_connectivity(floating_ip_addr, username,
- private_key, should_connect,
- servers=[server])
+ self.check_vm_connectivity(floating_ip_addr, username,
+ private_key, should_connect,
+ 'Public network connectivity check failed',
+ server)
def _wait_server_status_and_check_network_connectivity(self, server,
keypair,
@@ -196,7 +197,14 @@
'VERIFY_RESIZE')
self.servers_client.confirm_resize_server(server['id'])
server = self.servers_client.show_server(server['id'])['server']
- self.assertEqual(resize_flavor, server['flavor']['id'])
+ # Nova API > 2.46 no longer includes flavor.id, and schema check
+ # will cover whether 'id' should be in flavor
+ if server['flavor'].get('id'):
+ self.assertEqual(resize_flavor, server['flavor']['id'])
+ else:
+ flavor = self.flavors_client.show_flavor(resize_flavor)['flavor']
+ for key in ['original_name', 'ram', 'vcpus', 'disk']:
+ self.assertEqual(flavor[key], server['flavor'][key])
self._wait_server_status_and_check_network_connectivity(
server, keypair, floating_ip)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 8212e75..c1132cf 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -175,7 +175,7 @@
def _get_server_key(self, server):
return self.keypairs[server['key_name']]['private_key']
- def check_public_network_connectivity(
+ def _check_public_network_connectivity(
self, should_connect=True, msg=None,
should_check_floating_ip_status=True, mtu=None):
"""Verifies connectivty to a VM via public network and floating IP
@@ -199,13 +199,18 @@
if should_connect:
private_key = self._get_server_key(server)
floatingip_status = 'ACTIVE'
+
# Check FloatingIP Status before initiating a connection
if should_check_floating_ip_status:
self.check_floating_ip_status(floating_ip, floatingip_status)
- # call the common method in the parent class
- super(TestNetworkBasicOps, self).check_public_network_connectivity(
- ip_address, ssh_login, private_key, should_connect, msg,
- self.servers, mtu=mtu)
+
+ message = 'Public network connectivity check failed'
+ if msg:
+ message += '. Reason: %s' % msg
+
+ self.check_vm_connectivity(
+ ip_address, ssh_login, private_key, should_connect,
+ message, server, mtu=mtu)
def _disassociate_floating_ips(self):
floating_ip, _ = self.floating_ip_tuple
@@ -404,17 +409,17 @@
"""
self._setup_network_and_servers()
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
self._check_network_internal_connectivity(network=self.network)
self._check_network_external_connectivity()
self._disassociate_floating_ips()
- self.check_public_network_connectivity(should_connect=False,
- msg="after disassociate "
- "floating ip")
+ self._check_public_network_connectivity(should_connect=False,
+ msg="after disassociate "
+ "floating ip")
self._reassociate_floating_ips()
- self.check_public_network_connectivity(should_connect=True,
- msg="after re-associate "
- "floating ip")
+ self._check_public_network_connectivity(should_connect=True,
+ msg="after re-associate "
+ "floating ip")
@decorators.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
@testtools.skipUnless(utils.is_extension_enabled('net-mtu', 'network'),
@@ -425,10 +430,10 @@
"""Validate that network MTU sized frames fit through."""
self._setup_network_and_servers()
# first check that connectivity works in general for the instance
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
# now that we checked general connectivity, test that full size frames
# can also pass between nodes
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, mtu=self.network['mtu'])
@decorators.idempotent_id('1546850e-fbaa-42f5-8b5f-03d8a6a95f15')
@@ -467,7 +472,7 @@
"""
self._setup_network_and_servers()
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
self._check_network_internal_connectivity(network=self.network)
self._check_network_external_connectivity()
self._create_new_network(create_gateway=True)
@@ -502,7 +507,7 @@
"""
self._setup_network_and_servers()
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
self._create_new_network()
self._hotplug_server()
self._check_network_internal_connectivity(network=self.new_net)
@@ -524,19 +529,19 @@
admin_state_up attribute of router to True
"""
self._setup_network_and_servers()
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, msg="before updating "
"admin_state_up of router to False")
self._update_router_admin_state(self.router, False)
# TODO(alokmaurya): Remove should_check_floating_ip_status=False check
# once bug 1396310 is fixed
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=False, msg="after updating "
"admin_state_up of router to False",
should_check_floating_ip_status=False)
self._update_router_admin_state(self.router, True)
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, msg="after updating "
"admin_state_up of router to True")
@@ -581,7 +586,7 @@
renew_timeout = CONF.network.build_timeout
self._setup_network_and_servers(dns_nameservers=[initial_dns_server])
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
floating_ip, server = self.floating_ip_tuple
ip_address = floating_ip['floating_ip_address']
@@ -656,20 +661,20 @@
private_key=private_key,
server=server2)
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, msg="before updating "
"admin_state_up of instance port to False")
self.check_remote_connectivity(ssh_client, dest=server_pip,
should_succeed=True)
self.ports_client.update_port(port_id, admin_state_up=False)
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=False, msg="after updating "
"admin_state_up of instance port to False",
should_check_floating_ip_status=False)
self.check_remote_connectivity(ssh_client, dest=server_pip,
should_succeed=False)
self.ports_client.update_port(port_id, admin_state_up=True)
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, msg="after updating "
"admin_state_up of instance port to True")
self.check_remote_connectivity(ssh_client, dest=server_pip,
@@ -766,7 +771,7 @@
msg = "Rescheduling test does not apply to distributed routers."
raise self.skipException(msg)
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
# remove resource from agents
hosting_agents = set(a["id"] for a in
@@ -783,7 +788,7 @@
'unscheduling router failed')
# verify resource is un-functional
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=False,
msg='after router unscheduling',
)
@@ -800,7 +805,7 @@
"target agent")
# verify resource is functional
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True,
msg='After router rescheduling')
@@ -834,7 +839,7 @@
# Create server
self._setup_network_and_servers()
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
self._create_new_network()
self._hotplug_server()
fip, server = self.floating_ip_tuple
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index e39afe0..f5805ef 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -369,7 +369,8 @@
self.floating_ips[tenant.access_point['id']]['floating_ip_address']
private_key = tenant.keypair['private_key']
access_point_ssh = self.get_remote_client(
- access_point_ssh, private_key=private_key)
+ access_point_ssh, private_key=private_key,
+ server=tenant.access_point)
return access_point_ssh
def _test_in_tenant_block(self, tenant):
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 68f18d1..d6b6d14 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -63,7 +63,8 @@
instance_ip = self.get_server_ip(server)
timestamp = self.create_timestamp(instance_ip,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server)
# Prevent bug #1257594 from coming back
# Unshelve used to boot the instance with the original image, not
@@ -71,7 +72,8 @@
self._shelve_then_unshelve_server(server)
timestamp2 = self.get_timestamp(instance_ip,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server)
self.assertEqual(timestamp, timestamp2)
@decorators.attr(type='slow')
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index b51a781..a33d4d4 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -57,7 +57,8 @@
instance_ip = self.get_server_ip(server)
timestamp = self.create_timestamp(instance_ip,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server)
# snapshot the instance
snapshot_image = self.create_server_snapshot(server=server)
@@ -71,5 +72,6 @@
# check the existence of the timestamp file in the second instance
server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
timestamp2 = self.get_timestamp(server_from_snapshot_ip,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server_from_snapshot)
self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index ef369d6..2782119 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -96,7 +96,8 @@
keypair['private_key'])
timestamp = self.create_timestamp(ip_for_server,
CONF.compute.volume_device_name,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server)
self.nova_volume_detach(server, volume)
# snapshot the volume
@@ -126,5 +127,6 @@
# check the existence of the timestamp file in the volume2
timestamp2 = self.get_timestamp(ip_for_snapshot,
CONF.compute.volume_device_name,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server_from_snapshot)
self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 1564f25..79c2d14 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -108,7 +108,8 @@
LOG.info("Setting timestamp in instance %s", instance_1st)
ip_instance_1st = self.get_server_ip(instance_1st)
timestamp = self.create_timestamp(ip_instance_1st,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=instance_1st)
# delete instance
LOG.info("Deleting first instance: %s", instance_1st)
@@ -126,7 +127,8 @@
LOG.info("Getting timestamp in instance %s", instance_2nd)
ip_instance_2nd = self.get_server_ip(instance_2nd)
timestamp2 = self.get_timestamp(ip_instance_2nd,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=instance_2nd)
self.assertEqual(timestamp, timestamp2)
# snapshot a volume
@@ -150,7 +152,8 @@
server_from_snapshot)
server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
timestamp3 = self.get_timestamp(server_from_snapshot_ip,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server_from_snapshot)
self.assertEqual(timestamp, timestamp3)
@decorators.idempotent_id('05795fb2-b2a7-4c9f-8fac-ff25aedb1489')
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index ff7996a..c54bb38 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -40,7 +40,7 @@
@classmethod
def setup_clients(cls):
super(TestVolumeMigrateRetypeAttached, cls).setup_clients()
- cls.admin_volumes_client = cls.os_admin.volumes_v2_client
+ cls.admin_volumes_client = cls.os_admin.volumes_client_latest
@classmethod
def skip_checks(cls):
@@ -114,7 +114,8 @@
LOG.info("Setting timestamp in instance %s", instance['id'])
ip_instance = self.get_server_ip(instance)
timestamp = self.create_timestamp(ip_instance,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=instance)
# retype volume with migration from backend #1 to backend #2
LOG.info("Retyping Volume %s to new type %s", volume_origin['id'],
@@ -125,5 +126,6 @@
LOG.info("Getting timestamp in postmigrated instance %s",
instance['id'])
timestamp2 = self.get_timestamp(ip_instance,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=instance)
self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index 6cc356e..f55df30 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -40,24 +40,18 @@
args = mock.Mock(spec=argparse.Namespace)
setattr(args, 'smoke', False)
setattr(args, 'regex', '')
- setattr(args, 'whitelist_file', None)
- setattr(args, 'blacklist_file', None)
self.assertIsNone(None, self.run_cmd._build_regex(args))
def test__build_regex_smoke(self):
args = mock.Mock(spec=argparse.Namespace)
setattr(args, "smoke", True)
setattr(args, 'regex', '')
- setattr(args, 'whitelist_file', None)
- setattr(args, 'blacklist_file', None)
self.assertEqual(['smoke'], self.run_cmd._build_regex(args))
def test__build_regex_regex(self):
args = mock.Mock(spec=argparse.Namespace)
setattr(args, 'smoke', False)
setattr(args, "regex", 'i_am_a_fun_little_regex')
- setattr(args, 'whitelist_file', None)
- setattr(args, 'blacklist_file', None)
self.assertEqual(['i_am_a_fun_little_regex'],
self.run_cmd._build_regex(args))
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 8641b63..32d6224 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -225,7 +225,7 @@
# This test verifies that wrong config api_v2 = True is detected
class FakeClient(object):
def get_versions(self):
- return (None, ['v1.0'])
+ return (None, ['v1.1'])
fake_os = mock.MagicMock()
fake_module = mock.MagicMock()
diff --git a/tempest/tests/lib/services/volume/v2/test_backups_client.py b/tempest/tests/lib/services/volume/v2/test_backups_client.py
deleted file mode 100644
index 14e5fb0..0000000
--- a/tempest/tests/lib/services/volume/v2/test_backups_client.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# 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 backups_client
-from tempest.tests.lib import fake_auth_provider
-from tempest.tests.lib.services import base
-
-
-class TestBackupsClient(base.BaseServiceTest):
-
- FAKE_BACKUP_LIST = {
- "backups": [
- {
- "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
- "links": [
- {
- "href": "fake-url-1",
- "rel": "self"
- },
- {
- "href": "fake-url-2",
- "rel": "bookmark"
- }
- ],
- "name": "backup001"
- }
- ]
- }
-
- FAKE_BACKUP_LIST_WITH_DETAIL = {
- "backups": [
- {
- "availability_zone": "az1",
- "container": "volumebackups",
- "created_at": "2013-04-02T10:35:27.000000",
- "description": None,
- "fail_reason": None,
- "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
- "links": [
- {
- "href": "fake-url-1",
- "rel": "self"
- },
- {
- "href": "fake-url-2",
- "rel": "bookmark"
- }
- ],
- "name": "backup001",
- "object_count": 22,
- "size": 1,
- "status": "available",
- "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
- "is_incremental": True,
- "has_dependent_backups": False
- }
- ]
- }
-
- def setUp(self):
- super(TestBackupsClient, self).setUp()
- fake_auth = fake_auth_provider.FakeAuthProvider()
- self.client = backups_client.BackupsClient(fake_auth,
- 'volume',
- 'regionOne')
-
- def _test_list_backups(self, detail=False, mock_args='backups',
- bytes_body=False, **params):
- if detail:
- resp_body = self.FAKE_BACKUP_LIST_WITH_DETAIL
- else:
- resp_body = self.FAKE_BACKUP_LIST
- self.check_service_client_function(
- self.client.list_backups,
- 'tempest.lib.common.rest_client.RestClient.get',
- resp_body,
- to_utf=bytes_body,
- mock_args=[mock_args],
- detail=detail,
- **params)
-
- def test_list_backups_with_str_body(self):
- self._test_list_backups()
-
- def test_list_backups_with_bytes_body(self):
- self._test_list_backups(bytes_body=True)
-
- def test_list_backups_with_detail_with_str_body(self):
- mock_args = "backups/detail"
- self._test_list_backups(detail=True, mock_args=mock_args)
-
- def test_list_backups_with_detail_with_bytes_body(self):
- mock_args = "backups/detail"
- self._test_list_backups(detail=True, mock_args=mock_args,
- bytes_body=True)
-
- def test_list_backups_with_params(self):
- # Run the test separately for each param, to avoid assertion error
- # resulting from randomized params order.
- mock_args = 'backups?sort_key=name'
- self._test_list_backups(mock_args=mock_args, sort_key='name')
-
- mock_args = 'backups/detail?limit=10'
- self._test_list_backups(detail=True, mock_args=mock_args,
- bytes_body=True, limit=10)
diff --git a/tempest/tests/lib/services/volume/v2/test_volumes_client.py b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
deleted file mode 100644
index d7b042e..0000000
--- a/tempest/tests/lib/services/volume/v2/test_volumes_client.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# 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 oslo_serialization import jsonutils as json
-
-from tempest.lib.services.volume.v2 import volumes_client
-from tempest.tests.lib import fake_auth_provider
-from tempest.tests.lib.services import base
-
-
-class TestVolumesClient(base.BaseServiceTest):
-
- FAKE_VOLUME_METADATA_ITEM = {
- "meta": {
- "key1": "value1"
- }
- }
-
- FAKE_VOLUME_IMAGE_METADATA = {
- "metadata": {
- "container_format": "bare",
- "min_ram": "0",
- "disk_format": "raw",
- "image_name": "xly-ubuntu16-server",
- "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
- "checksum": "008f5d22fe3cb825d714da79607a90f9",
- "min_disk": "0",
- "size": "8589934592"
- }
- }
-
- def setUp(self):
- super(TestVolumesClient, self).setUp()
- fake_auth = fake_auth_provider.FakeAuthProvider()
- self.client = volumes_client.VolumesClient(fake_auth,
- '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',
- 'connector': {
- 'initiator': 'iqn.2017-04.org.fake:01'
- }
- }
-
- self.check_service_client_function(
- self.client.force_detach_volume,
- 'tempest.lib.common.rest_client.RestClient.post',
- {},
- to_utf=bytes_body,
- status=202,
- volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
- **kwargs
- )
-
- def _test_show_volume_metadata_item(self, bytes_body=False):
- self.check_service_client_function(
- self.client.show_volume_metadata_item,
- 'tempest.lib.common.rest_client.RestClient.get',
- self.FAKE_VOLUME_METADATA_ITEM,
- to_utf=bytes_body,
- volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
- id="key1")
-
- def _test_show_volume_image_metadata(self, bytes_body=False):
- fake_volume_id = "a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8"
- self.check_service_client_function(
- self.client.show_volume_image_metadata,
- 'tempest.lib.common.rest_client.RestClient.post',
- self.FAKE_VOLUME_IMAGE_METADATA,
- to_utf=bytes_body,
- mock_args=['volumes/%s/action' % fake_volume_id,
- json.dumps({"os-show_image_metadata": {}})],
- volume_id=fake_volume_id)
-
- def test_force_detach_volume_with_str_body(self):
- self._test_force_detach_volume()
-
- def test_force_detach_volume_with_bytes_body(self):
- self._test_force_detach_volume(bytes_body=True)
-
- def test_show_volume_metadata_item_with_str_body(self):
- self._test_show_volume_metadata_item()
-
- def test_show_volume_metadata_item_with_bytes_body(self):
- self._test_show_volume_metadata_item(bytes_body=True)
-
- def test_show_volume_image_metadata_with_str_body(self):
- self._test_show_volume_image_metadata()
-
- def test_show_volume_image_metadata_with_bytes_body(self):
- self._test_show_volume_image_metadata(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/v2/test_availability_zone_client.py b/tempest/tests/lib/services/volume/v3/test_availability_zone_client.py
similarity index 96%
rename from tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
rename to tempest/tests/lib/services/volume/v3/test_availability_zone_client.py
index 770565c..4827326 100644
--- a/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_availability_zone_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import availability_zone_client
+from tempest.lib.services.volume.v3 import availability_zone_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v3/test_backups_client.py b/tempest/tests/lib/services/volume/v3/test_backups_client.py
index f1ce987..5412064 100644
--- a/tempest/tests/lib/services/volume/v3/test_backups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_backups_client.py
@@ -20,6 +20,55 @@
class TestBackupsClient(base.BaseServiceTest):
+ FAKE_BACKUP_LIST = {
+ "backups": [
+ {
+ "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
+ "links": [
+ {
+ "href": "fake-url-1",
+ "rel": "self"
+ },
+ {
+ "href": "fake-url-2",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "backup001"
+ }
+ ]
+ }
+
+ FAKE_BACKUP_LIST_WITH_DETAIL = {
+ "backups": [
+ {
+ "availability_zone": "az1",
+ "container": "volumebackups",
+ "created_at": "2013-04-02T10:35:27.000000",
+ "description": None,
+ "fail_reason": None,
+ "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
+ "links": [
+ {
+ "href": "fake-url-1",
+ "rel": "self"
+ },
+ {
+ "href": "fake-url-2",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "backup001",
+ "object_count": 22,
+ "size": 1,
+ "status": "available",
+ "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
+ "is_incremental": True,
+ "has_dependent_backups": False
+ }
+ ]
+ }
+
FAKE_BACKUP_UPDATE = {
"backup": {
"id": "4c65c15f-a5c5-464b-b92a-90e4c04636a7",
@@ -35,6 +84,46 @@
'volume',
'regionOne')
+ def _test_list_backups(self, detail=False, mock_args='backups',
+ bytes_body=False, **params):
+ if detail:
+ resp_body = self.FAKE_BACKUP_LIST_WITH_DETAIL
+ else:
+ resp_body = self.FAKE_BACKUP_LIST
+ self.check_service_client_function(
+ self.client.list_backups,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ resp_body,
+ to_utf=bytes_body,
+ mock_args=[mock_args],
+ detail=detail,
+ **params)
+
+ def test_list_backups_with_str_body(self):
+ self._test_list_backups()
+
+ def test_list_backups_with_bytes_body(self):
+ self._test_list_backups(bytes_body=True)
+
+ def test_list_backups_with_detail_with_str_body(self):
+ mock_args = "backups/detail"
+ self._test_list_backups(detail=True, mock_args=mock_args)
+
+ def test_list_backups_with_detail_with_bytes_body(self):
+ mock_args = "backups/detail"
+ self._test_list_backups(detail=True, mock_args=mock_args,
+ bytes_body=True)
+
+ def test_list_backups_with_params(self):
+ # Run the test separately for each param, to avoid assertion error
+ # resulting from randomized params order.
+ mock_args = 'backups?sort_key=name'
+ self._test_list_backups(mock_args=mock_args, sort_key='name')
+
+ mock_args = 'backups/detail?limit=10'
+ self._test_list_backups(detail=True, mock_args=mock_args,
+ bytes_body=True, limit=10)
+
def _test_update_backup(self, bytes_body=False):
self.check_service_client_function(
self.client.update_backup,
diff --git a/tempest/tests/lib/services/volume/v2/test_capabilities_client.py b/tempest/tests/lib/services/volume/v3/test_capabilities_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_capabilities_client.py
rename to tempest/tests/lib/services/volume/v3/test_capabilities_client.py
index 3d3f1e1..7efe1ff 100644
--- a/tempest/tests/lib/services/volume/v2/test_capabilities_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_capabilities_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import capabilities_client
+from tempest.lib.services.volume.v3 import capabilities_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_encryption_types_client.py b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
similarity index 98%
rename from tempest/tests/lib/services/volume/v2/test_encryption_types_client.py
rename to tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
index 8de9fb4..c788181 100644
--- a/tempest/tests/lib/services/volume/v2/test_encryption_types_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import encryption_types_client
+from tempest.lib.services.volume.v3 import encryption_types_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_extensions_client.py b/tempest/tests/lib/services/volume/v3/test_extensions_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_extensions_client.py
rename to tempest/tests/lib/services/volume/v3/test_extensions_client.py
index c0ee421..a8bbffd 100644
--- a/tempest/tests/lib/services/volume/v2/test_extensions_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_extensions_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import extensions_client
+from tempest.lib.services.volume.v3 import extensions_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_hosts_client.py b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
similarity index 98%
rename from tempest/tests/lib/services/volume/v2/test_hosts_client.py
rename to tempest/tests/lib/services/volume/v3/test_hosts_client.py
index e107910..67ae4fd 100644
--- a/tempest/tests/lib/services/volume/v2/test_hosts_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import hosts_client
+from tempest.lib.services.volume.v3 import hosts_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_limits_client.py b/tempest/tests/lib/services/volume/v3/test_limits_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_limits_client.py
rename to tempest/tests/lib/services/volume/v3/test_limits_client.py
index 202054c..f94fbe1 100644
--- a/tempest/tests/lib/services/volume/v2/test_limits_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_limits_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import limits_client
+from tempest.lib.services.volume.v3 import limits_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py b/tempest/tests/lib/services/volume/v3/test_quota_classes_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
rename to tempest/tests/lib/services/volume/v3/test_quota_classes_client.py
index e715fcc..6190733 100644
--- a/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_quota_classes_client.py
@@ -15,7 +15,7 @@
import copy
-from tempest.lib.services.volume.v2 import quota_classes_client
+from tempest.lib.services.volume.v3 import quota_classes_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_quotas_client.py b/tempest/tests/lib/services/volume/v3/test_quotas_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_quotas_client.py
rename to tempest/tests/lib/services/volume/v3/test_quotas_client.py
index 6384350..aa5d251 100644
--- a/tempest/tests/lib/services/volume/v2/test_quotas_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_quotas_client.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import quotas_client
+from tempest.lib.services.volume.v3 import quotas_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
rename to tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
index 8a5f25f..e0f5566 100644
--- a/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import scheduler_stats_client
+from tempest.lib.services.volume.v3 import scheduler_stats_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py b/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
similarity index 94%
rename from tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
rename to tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
index e03a8eb..1b88020 100644
--- a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
@@ -17,9 +17,7 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.services.volume.v2 import snapshot_manage_client
-from tempest.lib.services.volume.v3 import snapshot_manage_client \
- as snapshot_manage_clientv3
+from tempest.lib.services.volume.v3 import snapshot_manage_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -65,7 +63,7 @@
# 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_clientv3.json,
+ with mock.patch.object(snapshot_manage_client.json,
'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py b/tempest/tests/lib/services/volume/v3/test_snapshots_client.py
similarity index 99%
rename from tempest/tests/lib/services/volume/v2/test_snapshots_client.py
rename to tempest/tests/lib/services/volume/v3/test_snapshots_client.py
index c9f57a0..2efd2e6 100644
--- a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_snapshots_client.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import snapshots_client
+from tempest.lib.services.volume.v3 import snapshots_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_transfers_client.py b/tempest/tests/lib/services/volume/v3/test_transfers_client.py
similarity index 95%
rename from tempest/tests/lib/services/volume/v2/test_transfers_client.py
rename to tempest/tests/lib/services/volume/v3/test_transfers_client.py
index 8e7c6f4..d631fe7 100644
--- a/tempest/tests/lib/services/volume/v2/test_transfers_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_transfers_client.py
@@ -18,9 +18,7 @@
import mock
from oslo_serialization import jsonutils as json
-from tempest.lib.services.volume.v2 import transfers_client
-from tempest.lib.services.volume.v3 import transfers_client \
- as transfers_clientv3
+from tempest.lib.services.volume.v3 import transfers_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -65,7 +63,7 @@
# 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(transfers_clientv3.json, 'dumps') as mock_dumps:
+ with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
self.check_service_client_function(
@@ -86,7 +84,7 @@
# 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(transfers_clientv3.json, 'dumps') as mock_dumps:
+ with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
self.check_service_client_function(
diff --git a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
similarity index 94%
rename from tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
rename to tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
index 0fb66bb..902f027 100644
--- a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
@@ -17,9 +17,7 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.services.volume.v2 import volume_manage_client
-from tempest.lib.services.volume.v3 import volume_manage_client \
- as volume_manage_clientv3
+from tempest.lib.services.volume.v3 import volume_manage_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -93,7 +91,7 @@
# 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_clientv3.json,
+ with mock.patch.object(volume_manage_client.json,
'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_volumes_client.py b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
index a515fd3..1250536 100644
--- a/tempest/tests/lib/services/volume/v3/test_volumes_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo_serialization import jsonutils as json
+
from tempest.lib.services.volume.v3 import volumes_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -27,6 +29,25 @@
}
}
+ FAKE_VOLUME_METADATA_ITEM = {
+ "meta": {
+ "key1": "value1"
+ }
+ }
+
+ FAKE_VOLUME_IMAGE_METADATA = {
+ "metadata": {
+ "container_format": "bare",
+ "min_ram": "0",
+ "disk_format": "raw",
+ "image_name": "xly-ubuntu16-server",
+ "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
+ "checksum": "008f5d22fe3cb825d714da79607a90f9",
+ "min_disk": "0",
+ "size": "8589934592"
+ }
+ }
+
def setUp(self):
super(TestVolumesClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -34,6 +55,60 @@
'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',
+ 'connector': {
+ 'initiator': 'iqn.2017-04.org.fake:01'
+ }
+ }
+
+ self.check_service_client_function(
+ self.client.force_detach_volume,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {},
+ to_utf=bytes_body,
+ status=202,
+ volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+ **kwargs
+ )
+
+ def _test_show_volume_metadata_item(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_volume_metadata_item,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_VOLUME_METADATA_ITEM,
+ to_utf=bytes_body,
+ volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+ id="key1")
+
+ def _test_show_volume_image_metadata(self, bytes_body=False):
+ fake_volume_id = "a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8"
+ self.check_service_client_function(
+ self.client.show_volume_image_metadata,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_VOLUME_IMAGE_METADATA,
+ to_utf=bytes_body,
+ mock_args=['volumes/%s/action' % fake_volume_id,
+ json.dumps({"os-show_image_metadata": {}})],
+ volume_id=fake_volume_id)
+
def _test_show_volume_summary(self, bytes_body=False):
self.check_service_client_function(
self.client.show_volume_summary,
@@ -41,6 +116,30 @@
self.FAKE_VOLUME_SUMMARY,
bytes_body)
+ def test_force_detach_volume_with_str_body(self):
+ self._test_force_detach_volume()
+
+ def test_force_detach_volume_with_bytes_body(self):
+ self._test_force_detach_volume(bytes_body=True)
+
+ def test_show_volume_metadata_item_with_str_body(self):
+ self._test_show_volume_metadata_item()
+
+ def test_show_volume_metadata_item_with_bytes_body(self):
+ self._test_show_volume_metadata_item(bytes_body=True)
+
+ def test_show_volume_image_metadata_with_str_body(self):
+ self._test_show_volume_image_metadata()
+
+ def test_show_volume_image_metadata_with_bytes_body(self):
+ self._test_show_volume_image_metadata(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)
+
def test_show_volume_summary_with_str_body(self):
self._test_show_volume_summary()
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index ed0eea3..0b1a599 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -19,6 +19,7 @@
from tempest.lib import base as test
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
from tempest.tests import base
@@ -62,21 +63,40 @@
t = TestFoo('test_bar')
if expected_to_skip:
- self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ e = self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ bug = decorator_args['bug']
+ bug_type = decorator_args.get('bug_type', 'launchpad')
+ self.assertRegex(
+ str(e),
+ r'Skipped until bug\: %s.*' % decorators._get_bug_url(
+ bug, bug_type)
+ )
else:
# assert that test_bar returned 0
self.assertEqual(TestFoo('test_bar').test_bar(), 0)
- def test_skip_because_bug(self):
+ def test_skip_because_launchpad_bug(self):
self._test_skip_because_helper(bug='12345')
- def test_skip_because_bug_and_condition_true(self):
+ def test_skip_because_launchpad_bug_and_condition_true(self):
self._test_skip_because_helper(bug='12348', condition=True)
- def test_skip_because_bug_and_condition_false(self):
+ def test_skip_because_launchpad_bug_and_condition_false(self):
self._test_skip_because_helper(expected_to_skip=False,
bug='12349', condition=False)
+ def test_skip_because_storyboard_bug(self):
+ self._test_skip_because_helper(bug='1992', bug_type='storyboard')
+
+ def test_skip_because_storyboard_bug_and_condition_true(self):
+ self._test_skip_because_helper(bug='1992', bug_type='storyboard',
+ condition=True)
+
+ def test_skip_because_storyboard_bug_and_condition_false(self):
+ self._test_skip_because_helper(expected_to_skip=False,
+ bug='1992', bug_type='storyboard',
+ condition=False)
+
def test_skip_because_bug_without_bug_never_skips(self):
"""Never skip without a bug parameter."""
self._test_skip_because_helper(expected_to_skip=False,
@@ -84,8 +104,8 @@
self._test_skip_because_helper(expected_to_skip=False)
def test_skip_because_invalid_bug_number(self):
- """Raise ValueError if with an invalid bug number"""
- self.assertRaises(ValueError, self._test_skip_because_helper,
+ """Raise InvalidParam if with an invalid bug number"""
+ self.assertRaises(lib_exc.InvalidParam, self._test_skip_because_helper,
bug='critical_bug')
@@ -126,6 +146,13 @@
class TestRelatedBugDecorator(base.TestCase):
+
+ def _get_my_exception(self):
+ class MyException(Exception):
+ def __init__(self, status_code):
+ self.status_code = status_code
+ return MyException
+
def test_relatedbug_when_no_exception(self):
f = mock.Mock()
sentinel = object()
@@ -137,10 +164,9 @@
test_foo(sentinel)
f.assert_called_once_with(sentinel)
- def test_relatedbug_when_exception(self):
- class MyException(Exception):
- def __init__(self, status_code):
- self.status_code = status_code
+ def test_relatedbug_when_exception_with_launchpad_bug_type(self):
+ """Validate related_bug decorator with bug_type == 'launchpad'"""
+ MyException = self._get_my_exception()
def f(self):
raise MyException(status_code=500)
@@ -152,4 +178,53 @@
with mock.patch.object(decorators.LOG, 'error') as m_error:
self.assertRaises(MyException, test_foo, object())
- m_error.assert_called_once_with(mock.ANY, '1234', '1234')
+ m_error.assert_called_once_with(
+ mock.ANY, '1234', 'https://launchpad.net/bugs/1234')
+
+ def test_relatedbug_when_exception_with_storyboard_bug_type(self):
+ """Validate related_bug decorator with bug_type == 'storyboard'"""
+ MyException = self._get_my_exception()
+
+ def f(self):
+ raise MyException(status_code=500)
+
+ @decorators.related_bug(bug="1234", status_code=500,
+ bug_type='storyboard')
+ def test_foo(self):
+ f(self)
+
+ with mock.patch.object(decorators.LOG, 'error') as m_error:
+ self.assertRaises(MyException, test_foo, object())
+
+ m_error.assert_called_once_with(
+ mock.ANY, '1234', 'https://storyboard.openstack.org/#!/story/1234')
+
+ def test_relatedbug_when_exception_invalid_bug_type(self):
+ """Check related_bug decorator raises exc when bug_type is not valid"""
+ MyException = self._get_my_exception()
+
+ def f(self):
+ raise MyException(status_code=500)
+
+ @decorators.related_bug(bug="1234", status_code=500,
+ bug_type=mock.sentinel.invalid)
+ def test_foo(self):
+ f(self)
+
+ with mock.patch.object(decorators.LOG, 'error'):
+ self.assertRaises(lib_exc.InvalidParam, test_foo, object())
+
+ def test_relatedbug_when_exception_invalid_bug_number(self):
+ """Check related_bug decorator raises exc when bug_number != digit"""
+ MyException = self._get_my_exception()
+
+ def f(self):
+ raise MyException(status_code=500)
+
+ @decorators.related_bug(bug="not a digit", status_code=500,
+ bug_type='launchpad')
+ def test_foo(self):
+ f(self)
+
+ with mock.patch.object(decorators.LOG, 'error'):
+ self.assertRaises(lib_exc.InvalidParam, test_foo, object())
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index bc3a753..9534ce8 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -193,3 +193,60 @@
"raise TestCase.failureException(exception.message)"))), 1)
self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
"raise TestCase.failureException(ee.message)"))), 0)
+
+ def _test_no_negatve_test_attribute_applied_to_negative_test(
+ self, filename, with_other_decorators=False,
+ with_negative_decorator=True, expected_success=True):
+ check = checks.negative_test_attribute_always_applied_to_negative_tests
+ other_decorators = [
+ "@decorators.idempotent_id(123)",
+ "@utils.requires_ext(extension='ext', service='svc')"
+ ]
+
+ if with_other_decorators:
+ # Include multiple decorators to verify that this check works with
+ # arbitrarily many decorators. These insert decorators above the
+ # @decorators.attr(type=['negative']) decorator.
+ for decorator in other_decorators:
+ self.assertIsNone(check(" %s" % decorator, filename))
+ if with_negative_decorator:
+ self.assertIsNone(
+ check("@decorators.attr(type=['negative'])", filename))
+ if with_other_decorators:
+ # Include multiple decorators to verify that this check works with
+ # arbitrarily many decorators. These insert decorators between
+ # the test and the @decorators.attr(type=['negative']) decorator.
+ for decorator in other_decorators:
+ self.assertIsNone(check(" %s" % decorator, filename))
+ final_result = check(" def test_some_negative_case", filename)
+ if expected_success:
+ self.assertIsNone(final_result)
+ else:
+ self.assertIsInstance(final_result, tuple)
+ self.assertFalse(final_result[0])
+
+ def test_no_negatve_test_attribute_applied_to_negative_test(self):
+ # Check negative filename, negative decorator passes
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something_negative.py")
+ # Check negative filename, negative decorator, other decorators passes
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something_negative.py",
+ with_other_decorators=True)
+
+ # Check non-negative filename skips check, causing pass
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something.py")
+
+ # Check negative filename, no negative decorator fails
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something_negative.py",
+ with_negative_decorator=False,
+ expected_success=False)
+ # Check negative filename, no negative decorator, other decorators
+ # fails
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something_negative.py",
+ with_other_decorators=True,
+ with_negative_decorator=False,
+ expected_success=False)
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
index 20c99b2..b27b23a 100755
--- a/tools/generate-tempest-plugins-list.sh
+++ b/tools/generate-tempest-plugins-list.sh
@@ -49,13 +49,37 @@
sorted_plugins=$(python tools/generate-tempest-plugins-list.py)
-for k in ${sorted_plugins}; do
- project=${k:0:28}
- giturl="git://git.openstack.org/openstack/${k:0:26}"
- printf "|%-28s|%-73s|\n" "${project}" "${giturl}"
- printf "+----------------------------+-------------------------------------------------------------------------+\n"
+name_col_len=$(echo "${sorted_plugins}" | wc -L)
+name_col_len=$(( name_col_len + 20 ))
+
+# Print the title underline for a RST table.
+function title_underline {
+ printf "== "
+ local len=$1
+ while [[ $len -gt 0 ]]; do
+ printf "="
+ len=$(( len - 1))
+ done
+ printf " ===\n"
+}
+
+printf "\n\n"
+title_underline ${name_col_len}
+printf "%-3s %-${name_col_len}s %s\n" "SR" "Plugin Name" "URL"
+title_underline ${name_col_len}
+
+i=0
+for plugin in ${sorted_plugins}; do
+ i=$((i+1))
+ giturl="git://git.openstack.org/openstack/${plugin}"
+ gitlink="https://git.openstack.org/cgit/openstack/${plugin}"
+ printf "%-3s %-${name_col_len}s %s\n" "$i" "${plugin}" "\`${giturl} <${gitlink}>\`__"
done
+title_underline ${name_col_len}
+
+printf "\n\n"
+
if [[ -r doc/source/data/tempest-plugins-registry.footer ]]; then
cat doc/source/data/tempest-plugins-registry.footer
fi
diff --git a/tox.ini b/tox.ini
index ab72548..d61a7fe 100644
--- a/tox.ini
+++ b/tox.ini
@@ -135,6 +135,16 @@
find . -type f -name "*.pyc" -delete
tempest run --serial --regex '\[.*\bsmoke\b.*\]' {posargs}
+[testenv:slow-serial]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+# The regex below is used to select the slow tagged tests to run serially:
+commands =
+ find . -type f -name "*.pyc" -delete
+ tempest run --serial --regex '\[.*\bslow\b.*\]' {posargs}
+
[testenv:venv]
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}