Merge "Modify the list group date"
diff --git a/.zuul.yaml b/.zuul.yaml
index 4ca14ad..d77a528 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -446,86 +446,7 @@
     nodeset: ubuntu-bionic
     vars:
       tox_envlist: plugin-sanity-check
-    voting: false
     timeout: 5000
-    required-projects:
-      - opendev.org/airship/tempest-plugin
-      - opendev.org/x/almanach
-      - opendev.org/openstack/aodh
-      - opendev.org/openstack/barbican-tempest-plugin
-      - opendev.org/openstack/blazar-tempest-plugin
-      - opendev.org/openstack/ceilometer
-      - opendev.org/openstack/cinder-tempest-plugin
-      - opendev.org/openstack/cloudkitty-tempest-plugin
-      - opendev.org/openstack/congress-tempest-plugin
-      - opendev.org/openstack/cyborg-tempest-plugin
-      - opendev.org/openstack/designate-tempest-plugin
-      - opendev.org/openstack/ec2api-tempest-plugin
-      - opendev.org/openstack/freezer
-      - opendev.org/openstack/freezer-api
-      - opendev.org/openstack/freezer-tempest-plugin
-      - opendev.org/x/gabbi-tempest
-      - opendev.org/x/gce-api
-      - opendev.org/x/glare
-      - opendev.org/openstack/heat-tempest-plugin
-      - opendev.org/x/intel-nfv-ci-tests
-      - opendev.org/openstack/ironic-tempest-plugin
-      - opendev.org/openstack/ironic-inspector
-      - opendev.org/openstack/keystone-tempest-plugin
-      - opendev.org/x/kingbird
-      - opendev.org/openstack/kuryr-tempest-plugin
-      - opendev.org/openstack/magnum
-      - opendev.org/openstack/magnum-tempest-plugin
-      - opendev.org/openstack/manila
-      - opendev.org/openstack/manila-tempest-plugin
-      - opendev.org/openstack/mistral-tempest-plugin
-      - opendev.org/x/mogan
-      - opendev.org/openstack/monasca-api
-      - opendev.org/openstack/monasca-log-api
-      - opendev.org/openstack/monasca-tempest-plugin
-      - opendev.org/openstack/murano-tempest-plugin
-      - opendev.org/openstack/networking-bgpvpn
-      - opendev.org/x/networking-cisco
-      - opendev.org/x/networking-fortinet
-      - opendev.org/openstack/networking-generic-switch
-      - opendev.org/openstack/networking-l2gw-tempest-plugin
-      - opendev.org/openstack/networking-midonet
-      - opendev.org/openstack/networking-sfc
-      - opendev.org/x/networking-spp
-      - opendev.org/openstack/neutron
-      - opendev.org/openstack/neutron-dynamic-routing
-      - opendev.org/openstack/neutron-fwaas
-      - opendev.org/openstack/neutron-lbaas
-      - opendev.org/openstack/neutron-tempest-plugin
-      - opendev.org/openstack/neutron-vpnaas
-      - opendev.org/x/nova-lxd
-      - opendev.org/x/novajoin-tempest-plugin
-      - opendev.org/openstack/octavia-tempest-plugin
-      - opendev.org/openstack/oswin-tempest-plugin
-      - opendev.org/openstack/panko
-      - opendev.org/openstack/patrole
-      - opendev.org/openstack/python-watcherclient
-      - opendev.org/openstack/qinling
-      - opendev.org/openstack/requirements
-      - opendev.org/openstack/sahara-tests
-      - opendev.org/openstack/senlin
-      - opendev.org/openstack/senlin-tempest-plugin
-      - opendev.org/openstack/solum-tempest-plugin
-      - opendev.org/x/tap-as-a-service
-      - opendev.org/x/tap-as-a-service-tempest-plugin
-      - opendev.org/openstack/telemetry-tempest-plugin
-      - opendev.org/openstack/tempest-horizon
-      - opendev.org/x/tobiko
-      - opendev.org/x/trio2o
-      - opendev.org/openstack/tripleo-common-tempest-plugin
-      - opendev.org/openstack/trove-tempest-plugin
-      - opendev.org/x/valet
-      - opendev.org/openstack/vitrage-tempest-plugin
-      - opendev.org/x/vmware-nsx-tempest-plugin
-      - opendev.org/openstack/watcher-tempest-plugin
-      - opendev.org/x/whitebox-tempest-plugin
-      - opendev.org/openstack/zaqar-tempest-plugin
-      - opendev.org/openstack/zun-tempest-plugin
 
 - job:
     name: tempest-cinder-v2-api
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
new file mode 100644
index 0000000..a89ad94
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,17 @@
+If you would like to contribute to the development of OpenStack, you must
+follow the steps in this page:
+
+   https://docs.openstack.org/infra/manual/developers.html
+
+If you already have a good understanding of how the system works and your
+OpenStack accounts are set up, you can skip to the development workflow
+section of this documentation to learn how changes to OpenStack should be
+submitted for review via the Gerrit tool:
+
+   https://docs.openstack.org/infra/manual/developers.html#development-workflow
+
+Pull requests submitted through GitHub will be ignored.
+
+Bugs should be filed on Launchpad, not GitHub:
+
+   https://bugs.launchpad.net/tempest
diff --git a/README.rst b/README.rst
index 841fae6..3cde2bf 100644
--- a/README.rst
+++ b/README.rst
@@ -10,274 +10,15 @@
 Tempest - The OpenStack Integration Test Suite
 ==============================================
 
-The documentation for Tempest is officially hosted at:
-https://docs.openstack.org/tempest/latest/
-
 This is a set of integration tests to be run against a live OpenStack
 cluster. Tempest has batteries of tests for OpenStack API validation,
 scenarios, and other specific tests useful in validating an OpenStack
 deployment.
 
-Design Principles
------------------
-Tempest Design Principles that we strive to live by.
+  * Documentation: https://docs.openstack.org/tempest/latest/
+  * Features: https://specs.openstack.org/openstack/qa-specs/#tempest
+  * Bugs: https://bugs.launchpad.net/tempest/
+  * Release Notes: https://docs.openstack.org/releasenotes/tempest
 
-- Tempest should be able to run against any OpenStack cloud, be it a
-  one node DevStack install, a 20 node LXC cloud, or a 1000 node KVM
-  cloud.
-- Tempest should be explicit in testing features. It is easy to auto
-  discover features of a cloud incorrectly, and give people an
-  incorrect assessment of their cloud. Explicit is always better.
-- Tempest uses OpenStack public interfaces. Tests in Tempest should
-  only touch public OpenStack APIs.
-- Tempest should not touch private or implementation specific
-  interfaces. This means not directly going to the database, not
-  directly hitting the hypervisors, not testing extensions not
-  included in the OpenStack base. If there are some features of
-  OpenStack that are not verifiable through standard interfaces, this
-  should be considered a possible enhancement.
-- Tempest strives for complete coverage of the OpenStack API and
-  common scenarios that demonstrate a working cloud.
-- Tempest drives load in an OpenStack cloud. By including a broad
-  array of API and scenario tests Tempest can be reused in whole or in
-  parts as load generation for an OpenStack cloud.
-- Tempest should attempt to clean up after itself, whenever possible
-  we should tear down resources when done.
-- Tempest should be self-testing.
-
-Quickstart
-----------
-
-To run Tempest, you first need to create a configuration file that will tell
-Tempest where to find the various OpenStack services and other testing behavior
-switches. Where the configuration file lives and how you interact with it
-depends on how you'll be running Tempest. There are 2 methods of using Tempest.
-The first, which is a newer and recommended workflow treats Tempest as a system
-installed program. The second older method is to run Tempest assuming your
-working dir is the actually Tempest source repo, and there are a number of
-assumptions related to that. For this section we'll only cover the newer method
-as it is simpler, and quicker to work with.
-
-#. You first need to install Tempest. This is done with pip after you check out
-   the Tempest repo::
-
-    $ git clone https://opendev.org/openstack/tempest
-    $ pip install tempest/
-
-   This can be done within a venv, but the assumption for this guide is that
-   the Tempest CLI entry point will be in your shell's PATH.
-
-#. Installing Tempest may create a ``/etc/tempest dir``, however if one isn't
-   created you can create one or use ``~/.tempest/etc`` or ``~/.config/tempest`` in
-   place of ``/etc/tempest``. If none of these dirs are created Tempest will create
-   ``~/.tempest/etc`` when it's needed. The contents of this dir will always
-   automatically be copied to all ``etc/`` dirs in local workspaces as an initial
-   setup step. So if there is any common configuration you'd like to be shared
-   between local Tempest workspaces it's recommended that you pre-populate it
-   before running ``tempest init``.
-
-#. Setup a local Tempest workspace. This is done by using the tempest init
-   command::
-
-    $ tempest init cloud-01
-
-   which also works the same as::
-
-    $ mkdir cloud-01 && cd cloud-01 && tempest init
-
-   This will create a new directory for running a single Tempest configuration.
-   If you'd like to run Tempest against multiple OpenStack deployments the idea
-   is that you'll create a new working directory for each to maintain separate
-   configuration files and local artifact storage for each.
-
-#. Then ``cd`` into the newly created working dir and also modify the local
-   config files located in the ``etc/`` subdir created by the ``tempest init``
-   command. Tempest is expecting a ``tempest.conf`` file in etc/ so if only a
-   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
-   `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 `Tempest Run <https://docs.openstack.org/tempest/latest/run.html#tempest-run>`_
-   command. This can be done by either
-   running::
-
-    $ tempest run
-
-   from the Tempest workspace directory. Or you can use the ``--workspace``
-   argument to run in the workspace you created regardless of your current
-   working directory. For example::
-
-    $ tempest run --workspace cloud-01
-
-   There is also the option to use `stestr`_ directly. For example, from
-   the workspace dir run::
-
-    $ stestr run --black-regex '\[.*\bslow\b.*\]' '^tempest\.(api|scenario)'
-
-   will run the same set of tests as the default gate jobs. Or you can
-   use `unittest`_ compatible test runners such as `testr`_, `pytest`_ etc.
-
-   Tox also contains several existing job configurations. For example::
-
-    $ tox -e full
-
-   which will run the same set of tests as the OpenStack gate. (it's exactly how
-   the gate invokes Tempest) Or::
-
-    $ tox -e smoke
-
-   to run the tests tagged as smoke.
-
-.. _unittest: https://docs.python.org/3/library/unittest.html
-.. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
-.. _stestr: https://stestr.readthedocs.org/en/latest/MANUAL.html
-.. _pytest: https://docs.pytest.org/en/latest/
-
-Library
--------
-Tempest exposes a library interface. This interface is a stable interface and
-should be backwards compatible (including backwards compatibility with the
-old tempest-lib package, with the exception of the import). If you plan to
-directly consume Tempest in your project you should only import code from the
-Tempest library interface, other pieces of Tempest do not have the same
-stable interface and there are no guarantees on the Python API unless otherwise
-stated.
-
-For more details refer to the `library documentation
-<https://docs.openstack.org/tempest/latest/library.html#library>`_
-
-Release Versioning
-------------------
-`Tempest Release Notes <https://docs.openstack.org/releasenotes/tempest>`_
-shows what changes have been released on each version.
-
-Tempest's released versions are broken into 2 sets of information. Depending on
-how you intend to consume Tempest you might need
-
-The version is a set of 3 numbers:
-
-X.Y.Z
-
-While this is almost `semver`_ like, the way versioning is handled is slightly
-different:
-
-X is used to represent the supported OpenStack releases for Tempest tests
-in-tree, and to signify major feature changes to Tempest. It's a monotonically
-increasing integer where each version either indicates a new supported OpenStack
-release, the drop of support for an OpenStack release (which will coincide with
-the upstream stable branch going EOL), or a major feature lands (or is removed)
-from Tempest.
-
-Y.Z is used to represent library interface changes. This is treated the same
-way as minor and patch versions from `semver`_ but only for the library
-interface. When Y is incremented we've added functionality to the library
-interface and when Z is incremented it's a bug fix release for the library.
-Also note that both Y and Z are reset to 0 at each increment of X.
-
-.. _semver: https://semver.org/
-
-Configuration
--------------
-
-Detailed configuration of Tempest is beyond the scope of this
-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
-command from the top level of the Tempest directory::
-
-    $ tox -e genconfig
-
-The most important pieces that are needed are the user ids, OpenStack
-endpoints, and basic flavors and images needed to run tests.
-
-Unit Tests
-----------
-
-Tempest also has a set of unit tests which test the Tempest code itself. These
-tests can be run by specifying the test discovery path::
-
-    $ stestr --test-path ./tempest/tests run
-
-By setting ``--test-path`` option to ./tempest/tests it specifies that test discover
-should only be run on the unit test directory. The default value of ``test_path``
-is ``test_path=./tempest/test_discover`` which will only run test discover on the
-Tempest suite.
-
-Alternatively, there are the py27 and py36 tox jobs which will run the unit
-tests with the corresponding version of python.
-
-One common activity is to just run a single test, you can do this with tox
-simply by specifying to just run py27 or py36 tests against a single test::
-
-    $ tox -e py36 -- -n tempest.tests.test_microversions.TestMicroversionsTestsClass.test_config_version_none_23
-
-Or all tests in the test_microversions.py file::
-
-    $ tox -e py36 -- -n tempest.tests.test_microversions
-
-You may also use regular expressions to run any matching tests::
-
-    $ tox -e py36 -- test_microversions
-
-Additionally, when running a single test, or test-file, the ``-n/--no-discover``
-argument is no longer required, however it may perform faster if included.
-
-For more information on these options and details about stestr, please see the
-`stestr documentation <https://stestr.readthedocs.io/en/latest/MANUAL.html>`_.
-
-Python 3.x
-----------
-
-Starting during the Pike cycle Tempest has a gating CI job that runs Tempest
-with Python 3. Any Tempest release after 15.0.0 should fully support running
-under Python 3 as well as Python 2.7.
-
-Legacy run method
------------------
-
-The legacy method of running Tempest is to just treat the Tempest source code
-as a python unittest repository and run directly from the source repo. When
-running in this way you still start with a Tempest config file and the steps
-are basically the same except that it expects you know where the Tempest code
-lives on your system and requires a bit more manual interaction to get Tempest
-running. For example, when running Tempest this way things like a lock file
-directory do not get generated automatically and the burden is on the user to
-create and configure that.
-
-To start you need to create a configuration file. The easiest way to create a
-configuration file is to generate a sample in the ``etc/`` directory ::
-
-    $ cd $TEMPEST_ROOT_DIR
-    $ oslo-config-generator --config-file \
-        tempest/cmd/config-generator.tempest.conf \
-        --output-file etc/tempest.conf
-
-After that, open up the ``etc/tempest.conf`` file and edit the
-configuration variables to match valid data in your environment.
-This includes your Keystone endpoint, a valid user and credentials,
-and reference data to be used in testing.
-
-.. note::
-
-    If you have a running DevStack environment, Tempest will be
-    automatically configured and placed in ``/opt/stack/tempest``. It
-    will have a configuration file already set up to work with your
-    DevStack installation.
-
-Tempest is not tied to any single test runner, but `testr`_ is the most commonly
-used tool. Also, the nosetests test runner is **not** recommended to run Tempest.
-
-After setting up your configuration file, you can execute the set of Tempest
-tests by using ``testr`` ::
-
-    $ testr run --parallel
-
-To run one single test serially ::
-
-    $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
+Get in touch via `email <mailto:openstack-discuss@lists.openstack.org>`_. Use
+[tempest] in your subject.
diff --git a/doc/requirements.txt b/doc/requirements.txt
index d959d44..2194dc4 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -1,6 +1,8 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-openstackdocstheme>=1.18.1 # Apache-2.0
+openstackdocstheme>=1.20.0 # Apache-2.0
 reno>=2.5.0 # Apache-2.0
-sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
+sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
+sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2;python_version>='3.4' # BSD
+sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD
diff --git a/doc/source/conf.py b/doc/source/conf.py
index c2ea628..e6fb8fd 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -52,6 +52,7 @@
 extensions = ['sphinx.ext.autodoc',
               'sphinx.ext.todo',
               'sphinx.ext.viewcode',
+              'sphinxcontrib.rsvgconverter',
               'openstackdocstheme',
               'oslo_config.sphinxconfiggen',
              ]
@@ -196,3 +197,16 @@
 
 # A list of warning types to suppress arbitrary warning messages.
 suppress_warnings = ['image.nonlocal_uri']
+
+# -- Options for LaTeX output -------------------------------------------------
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass
+# [howto/manual]).
+latex_documents = [
+    ('index', 'doc-tempest.tex', u'Tempest Testing Project',
+     u'OpenStack Foundation', 'manual'),
+]
+
+# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664
+latex_use_xindy = False
diff --git a/doc/source/index.rst b/doc/source/index.rst
index fecf98a..7acfd62 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -88,7 +88,12 @@
 
    stable_branch_support_policy
 
-Indices and tables
-==================
+Search
+======
 
-* :ref:`search`
+.. only:: html
+
+  * :ref:`Tempest document search <search>`: Search the contents of this document.
+
+* `OpenStack wide search <https://docs.openstack.org>`_: Search the wider
+  set of OpenStack documentation, including forums.
\ No newline at end of file
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
deleted file mode 120000
index c768ff7..0000000
--- a/doc/source/overview.rst
+++ /dev/null
@@ -1 +0,0 @@
-../../README.rst
\ No newline at end of file
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
new file mode 100644
index 0000000..423214d
--- /dev/null
+++ b/doc/source/overview.rst
@@ -0,0 +1,282 @@
+Tempest - The OpenStack Integration Test Suite
+==============================================
+
+The documentation for Tempest is officially hosted at:
+https://docs.openstack.org/tempest/latest/
+
+This is a set of integration tests to be run against a live OpenStack
+cluster. Tempest has batteries of tests for OpenStack API validation,
+scenarios, and other specific tests useful in validating an OpenStack
+deployment.
+
+Team and repository tags
+------------------------
+
+.. image:: https://governance.openstack.org/tc/badges/tempest.svg
+    :target: https://governance.openstack.org/tc/reference/tags/index.html
+
+.. Change things from this point on
+
+Design Principles
+-----------------
+Tempest Design Principles that we strive to live by.
+
+- Tempest should be able to run against any OpenStack cloud, be it a
+  one node DevStack install, a 20 node LXC cloud, or a 1000 node KVM
+  cloud.
+- Tempest should be explicit in testing features. It is easy to auto
+  discover features of a cloud incorrectly, and give people an
+  incorrect assessment of their cloud. Explicit is always better.
+- Tempest uses OpenStack public interfaces. Tests in Tempest should
+  only touch public OpenStack APIs.
+- Tempest should not touch private or implementation specific
+  interfaces. This means not directly going to the database, not
+  directly hitting the hypervisors, not testing extensions not
+  included in the OpenStack base. If there are some features of
+  OpenStack that are not verifiable through standard interfaces, this
+  should be considered a possible enhancement.
+- Tempest strives for complete coverage of the OpenStack API and
+  common scenarios that demonstrate a working cloud.
+- Tempest drives load in an OpenStack cloud. By including a broad
+  array of API and scenario tests Tempest can be reused in whole or in
+  parts as load generation for an OpenStack cloud.
+- Tempest should attempt to clean up after itself, whenever possible
+  we should tear down resources when done.
+- Tempest should be self-testing.
+
+Quickstart
+----------
+
+To run Tempest, you first need to create a configuration file that will tell
+Tempest where to find the various OpenStack services and other testing behavior
+switches. Where the configuration file lives and how you interact with it
+depends on how you'll be running Tempest. There are 2 methods of using Tempest.
+The first, which is a newer and recommended workflow treats Tempest as a system
+installed program. The second older method is to run Tempest assuming your
+working dir is the actually Tempest source repo, and there are a number of
+assumptions related to that. For this section we'll only cover the newer method
+as it is simpler, and quicker to work with.
+
+#. You first need to install Tempest. This is done with pip after you check out
+   the Tempest repo::
+
+    $ git clone https://opendev.org/openstack/tempest
+    $ pip install tempest/
+
+   This can be done within a venv, but the assumption for this guide is that
+   the Tempest CLI entry point will be in your shell's PATH.
+
+#. Installing Tempest may create a ``/etc/tempest dir``, however if one isn't
+   created you can create one or use ``~/.tempest/etc`` or ``~/.config/tempest`` in
+   place of ``/etc/tempest``. If none of these dirs are created Tempest will create
+   ``~/.tempest/etc`` when it's needed. The contents of this dir will always
+   automatically be copied to all ``etc/`` dirs in local workspaces as an initial
+   setup step. So if there is any common configuration you'd like to be shared
+   between local Tempest workspaces it's recommended that you pre-populate it
+   before running ``tempest init``.
+
+#. Setup a local Tempest workspace. This is done by using the tempest init
+   command::
+
+    $ tempest init cloud-01
+
+   which also works the same as::
+
+    $ mkdir cloud-01 && cd cloud-01 && tempest init
+
+   This will create a new directory for running a single Tempest configuration.
+   If you'd like to run Tempest against multiple OpenStack deployments the idea
+   is that you'll create a new working directory for each to maintain separate
+   configuration files and local artifact storage for each.
+
+#. Then ``cd`` into the newly created working dir and also modify the local
+   config files located in the ``etc/`` subdir created by the ``tempest init``
+   command. Tempest is expecting a ``tempest.conf`` file in etc/ so if only a
+   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
+   `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 `Tempest Run <https://docs.openstack.org/tempest/latest/run.html#tempest-run>`_
+   command. This can be done by either
+   running::
+
+    $ tempest run
+
+   from the Tempest workspace directory. Or you can use the ``--workspace``
+   argument to run in the workspace you created regardless of your current
+   working directory. For example::
+
+    $ tempest run --workspace cloud-01
+
+   There is also the option to use `stestr`_ directly. For example, from
+   the workspace dir run::
+
+    $ stestr run --black-regex '\[.*\bslow\b.*\]' '^tempest\.(api|scenario)'
+
+   will run the same set of tests as the default gate jobs. Or you can
+   use `unittest`_ compatible test runners such as `testr`_, `pytest`_ etc.
+
+   Tox also contains several existing job configurations. For example::
+
+    $ tox -e full
+
+   which will run the same set of tests as the OpenStack gate. (it's exactly how
+   the gate invokes Tempest) Or::
+
+    $ tox -e smoke
+
+   to run the tests tagged as smoke.
+
+.. _unittest: https://docs.python.org/3/library/unittest.html
+.. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
+.. _stestr: https://stestr.readthedocs.org/en/latest/MANUAL.html
+.. _pytest: https://docs.pytest.org/en/latest/
+
+Library
+-------
+Tempest exposes a library interface. This interface is a stable interface and
+should be backwards compatible (including backwards compatibility with the
+old tempest-lib package, with the exception of the import). If you plan to
+directly consume Tempest in your project you should only import code from the
+Tempest library interface, other pieces of Tempest do not have the same
+stable interface and there are no guarantees on the Python API unless otherwise
+stated.
+
+For more details refer to the `library documentation
+<https://docs.openstack.org/tempest/latest/library.html#library>`_
+
+Release Versioning
+------------------
+`Tempest Release Notes <https://docs.openstack.org/releasenotes/tempest>`_
+shows what changes have been released on each version.
+
+Tempest's released versions are broken into 2 sets of information. Depending on
+how you intend to consume Tempest you might need
+
+The version is a set of 3 numbers:
+
+X.Y.Z
+
+While this is almost `semver`_ like, the way versioning is handled is slightly
+different:
+
+X is used to represent the supported OpenStack releases for Tempest tests
+in-tree, and to signify major feature changes to Tempest. It's a monotonically
+increasing integer where each version either indicates a new supported OpenStack
+release, the drop of support for an OpenStack release (which will coincide with
+the upstream stable branch going EOL), or a major feature lands (or is removed)
+from Tempest.
+
+Y.Z is used to represent library interface changes. This is treated the same
+way as minor and patch versions from `semver`_ but only for the library
+interface. When Y is incremented we've added functionality to the library
+interface and when Z is incremented it's a bug fix release for the library.
+Also note that both Y and Z are reset to 0 at each increment of X.
+
+.. _semver: https://semver.org/
+
+Configuration
+-------------
+
+Detailed configuration of Tempest is beyond the scope of this
+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
+command from the top level of the Tempest directory::
+
+    $ tox -e genconfig
+
+The most important pieces that are needed are the user ids, OpenStack
+endpoints, and basic flavors and images needed to run tests.
+
+Unit Tests
+----------
+
+Tempest also has a set of unit tests which test the Tempest code itself. These
+tests can be run by specifying the test discovery path::
+
+    $ stestr --test-path ./tempest/tests run
+
+By setting ``--test-path`` option to ./tempest/tests it specifies that test discover
+should only be run on the unit test directory. The default value of ``test_path``
+is ``test_path=./tempest/test_discover`` which will only run test discover on the
+Tempest suite.
+
+Alternatively, there are the py27 and py36 tox jobs which will run the unit
+tests with the corresponding version of python.
+
+One common activity is to just run a single test, you can do this with tox
+simply by specifying to just run py27 or py36 tests against a single test::
+
+    $ tox -e py36 -- -n tempest.tests.test_microversions.TestMicroversionsTestsClass.test_config_version_none_23
+
+Or all tests in the test_microversions.py file::
+
+    $ tox -e py36 -- -n tempest.tests.test_microversions
+
+You may also use regular expressions to run any matching tests::
+
+    $ tox -e py36 -- test_microversions
+
+Additionally, when running a single test, or test-file, the ``-n/--no-discover``
+argument is no longer required, however it may perform faster if included.
+
+For more information on these options and details about stestr, please see the
+`stestr documentation <https://stestr.readthedocs.io/en/latest/MANUAL.html>`_.
+
+Python 3.x
+----------
+
+Starting during the Pike cycle Tempest has a gating CI job that runs Tempest
+with Python 3. Any Tempest release after 15.0.0 should fully support running
+under Python 3 as well as Python 2.7.
+
+Legacy run method
+-----------------
+
+The legacy method of running Tempest is to just treat the Tempest source code
+as a python unittest repository and run directly from the source repo. When
+running in this way you still start with a Tempest config file and the steps
+are basically the same except that it expects you know where the Tempest code
+lives on your system and requires a bit more manual interaction to get Tempest
+running. For example, when running Tempest this way things like a lock file
+directory do not get generated automatically and the burden is on the user to
+create and configure that.
+
+To start you need to create a configuration file. The easiest way to create a
+configuration file is to generate a sample in the ``etc/`` directory ::
+
+    $ cd $TEMPEST_ROOT_DIR
+    $ oslo-config-generator --config-file \
+        tempest/cmd/config-generator.tempest.conf \
+        --output-file etc/tempest.conf
+
+After that, open up the ``etc/tempest.conf`` file and edit the
+configuration variables to match valid data in your environment.
+This includes your Keystone endpoint, a valid user and credentials,
+and reference data to be used in testing.
+
+.. note::
+
+    If you have a running DevStack environment, Tempest will be
+    automatically configured and placed in ``/opt/stack/tempest``. It
+    will have a configuration file already set up to work with your
+    DevStack installation.
+
+Tempest is not tied to any single test runner, but `testr`_ is the most commonly
+used tool. Also, the nosetests test runner is **not** recommended to run Tempest.
+
+After setting up your configuration file, you can execute the set of Tempest
+tests by using ``testr`` ::
+
+    $ testr run --parallel
+
+To run one single test serially ::
+
+    $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index e1787b6..d4b253a 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -67,3 +67,8 @@
        ::
            vars:
              tox_extra_args: --sitepackages
+
+.. zuul:rolevar:: tempest_test_timeout
+   :default: ''
+
+   The timeout (in seconds) for each test.
diff --git a/roles/run-tempest/defaults/main.yaml b/roles/run-tempest/defaults/main.yaml
index 06918b5..79df3e1 100644
--- a/roles/run-tempest/defaults/main.yaml
+++ b/roles/run-tempest/defaults/main.yaml
@@ -3,3 +3,4 @@
 tox_envlist: smoke
 tempest_black_regex: ''
 tox_extra_args: ''
+tempest_test_timeout: ''
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 16086aa..24bd4db 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -42,3 +42,4 @@
     chdir: "{{devstack_base_dir}}/tempest"
   become: true
   become_user: tempest
+  environment: '{{ {"OS_TEST_TIMEOUT": tempest_test_timeout} if tempest_test_timeout else {} }}'
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 3c152c9..b811421 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -15,7 +15,6 @@
 
 from tempest.api.compute import base
 from tempest import config
-from tempest.lib.common import api_version_utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
@@ -101,11 +100,5 @@
         # will return 400(Bad Request) if we attempt to send a name which has
         # 4 byte utf-8 character.
         utf8_name = data_utils.rand_name(b'\xe2\x82\xa1'.decode('utf-8'))
-        body = self.compute_images_client.create_image(
-            self.server_id, name=utf8_name)
-        if api_version_utils.compare_version_header_to_response(
-            "OpenStack-API-Version", "compute 2.45", body.response, "lt"):
-            image_id = body['image_id']
-        else:
-            image_id = data_utils.parse_image_id(body.response['location'])
-        self.addCleanup(self.client.delete_image, image_id)
+        self.create_image_from_server(self.server_id, name=utf8_name,
+                                      wait_until='ACTIVE')
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 6a2af71..df8da07 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -79,6 +79,9 @@
             validatable=True,
             validation_resources=validation_resources,
             wait_until='ACTIVE')
+        # NOTE(mgoddard): Get detailed server to ensure addresses are present
+        # in fixed IP case.
+        server = self.servers_client.show_server(server['id'])['server']
         # NOTE(artom) self.create_test_server adds cleanups, but this is
         # apparently not enough? Add cleanup here.
         self.addCleanup(self.delete_server, server['id'])
@@ -319,6 +322,9 @@
             self.addCleanup(self.delete_server, server['id'])
 
         for server in servers:
+            # NOTE(mgoddard): Get detailed server to ensure addresses are
+            # present in fixed IP case.
+            server = self.servers_client.show_server(server['id'])['server']
             self._wait_for_validation(server, validation_resources)
             # attach the port to the server
             iface = self.interfaces_client.create_interface(
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 4ee243e..8aab574 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -361,6 +361,10 @@
             networks=[{'uuid': self.get_tenant_network()['id']}])
         self.addCleanup(self.delete_server, server['id'])
 
+        # NOTE(mgoddard): Get detailed server to ensure addresses are present
+        # in fixed IP case.
+        server = self.servers_client.show_server(server['id'])['server']
+
         # Attach tagged nic and volume
         interface = self.interfaces_client.create_interface(
             server['id'], net_id=net['id'],
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index d47ff51..0e469c7 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -345,6 +345,9 @@
         # from setUp is not volume-backed.
         server = self.create_test_server(
             volume_backed=True, wait_until='ACTIVE')
+        # NOTE(mgoddard): Get detailed server to ensure addresses are present
+        # in fixed IP case.
+        server = self.servers_client.show_server(server['id'])['server']
         self._test_resize_server_confirm(server['id'])
         if CONF.compute_feature_enabled.console_output:
             # Now do something interactive with the guest like get its console
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 9022b2d..cb8ea11 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -44,29 +44,24 @@
     @classmethod
     def resource_setup(cls):
         super(ListProjectsTestJSON, cls).resource_setup()
-        cls.project_ids = list()
-        cls.domain_id = cls.os_admin.credentials.domain_id
+        domain_id = cls.os_admin.credentials.domain_id
         # Create project with domain
-        cls.p1_name = data_utils.rand_name('project')
+        p1_name = data_utils.rand_name(cls.__name__)
         cls.p1 = cls.projects_client.create_project(
-            cls.p1_name, enabled=False,
-            domain_id=cls.domain_id)['project']
+            p1_name, enabled=False, domain_id=domain_id)['project']
         cls.addClassResourceCleanup(cls.projects_client.delete_project,
                                     cls.p1['id'])
-        cls.project_ids.append(cls.p1['id'])
         # Create default project
-        p2_name = data_utils.rand_name('project')
+        p2_name = data_utils.rand_name(cls.__name__)
         cls.p2 = cls.projects_client.create_project(p2_name)['project']
         cls.addClassResourceCleanup(cls.projects_client.delete_project,
                                     cls.p2['id'])
-        cls.project_ids.append(cls.p2['id'])
         # Create a new project (p3) using p2 as parent project
-        p3_name = data_utils.rand_name('project')
+        p3_name = data_utils.rand_name(cls.__name__)
         cls.p3 = cls.projects_client.create_project(
             p3_name, parent_id=cls.p2['id'])['project']
         cls.addClassResourceCleanup(cls.projects_client.delete_project,
                                     cls.p3['id'])
-        cls.project_ids.append(cls.p3['id'])
 
     @decorators.idempotent_id('0fe7a334-675a-4509-b00e-1c4b95d5dae8')
     def test_list_projects_with_enabled(self):
@@ -98,7 +93,7 @@
         cls.p1 = cls.projects_client.show_project(
             cls.os_primary.credentials.project_id)['project']
         # Create a test project
-        p2_name = data_utils.rand_name('project')
+        p2_name = data_utils.rand_name(cls.__name__)
         p2_domain_id = CONF.identity.default_domain_id
         cls.p2 = cls.projects_client.create_project(
             p2_name, domain_id=p2_domain_id)['project']
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 0b85b19..e46145d 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -230,8 +230,14 @@
         _projects = self.projects_client.list_projects()['projects']
         project_list = next(x for x in _projects if x['id'] == project['id'])
 
-        # Assert the list of fields is correct (one is enough to check here)
-        self.assertSetEqual(set(fields), set(project_get.keys()))
+        # Assert the expected fields exist. More fields than expected may
+        # be in this list. This is for future proofind as keystone does not
+        # and has no plans to support microservices. Any fields in the future
+        # that are added to the response of the API should eventually be added
+        # to the expected fields. The expected fields must be a subset of
+        # the project_get fields (all keys in fields must exist in project_get,
+        # but project_get.keys() may have additional fields)
+        self.assertTrue(set(fields).issubset(project_get.keys()))
 
         # Ensure the set of tags is identical and match the expected one
         get_tags = set(project_get.pop("tags"))
diff --git a/tempest/api/identity/v3/test_api_discovery.py b/tempest/api/identity/v3/test_api_discovery.py
index c04c21b..e87d1cd 100644
--- a/tempest/api/identity/v3/test_api_discovery.py
+++ b/tempest/api/identity/v3/test_api_discovery.py
@@ -14,12 +14,24 @@
 #    under the License.
 
 from tempest.api.identity import base
+from tempest import config
 from tempest.lib import decorators
 
 
+CONF = config.CONF
+
+
 class TestApiDiscovery(base.BaseIdentityV3Test):
     """Tests for API discovery features."""
 
+    @decorators.idempotent_id('79aec9ae-710f-4c54-a4fc-3aa25b4feac3')
+    def test_identity_v3_existence(self):
+        versions = self.non_admin_versions_client.list_versions()
+        found = any(
+            "v3" in version.get('id')
+            for version in versions['versions']['values'])
+        self.assertEqual(CONF.identity_feature_enabled.api_v3, found)
+
     @decorators.idempotent_id('721f480f-35b6-46c7-846e-047e6acea0dc')
     @decorators.attr(type='smoke')
     def test_list_api_versions(self):
diff --git a/tempest/api/volume/admin/test_backends_capabilities.py b/tempest/api/volume/admin/test_backends_capabilities.py
index affed6b..1351704 100644
--- a/tempest/api/volume/admin/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/test_backends_capabilities.py
@@ -21,17 +21,6 @@
 
 class BackendsCapabilitiesAdminTestsJSON(base.BaseVolumeAdminTest):
 
-    CAPABILITIES = ('namespace',
-                    'vendor_name',
-                    'volume_backend_name',
-                    'pool_name',
-                    'driver_version',
-                    'storage_protocol',
-                    'display_name',
-                    'description',
-                    'visibility',
-                    'properties')
-
     @classmethod
     def resource_setup(cls):
         super(BackendsCapabilitiesAdminTestsJSON, cls).resource_setup()
@@ -44,12 +33,8 @@
     @decorators.idempotent_id('3750af44-5ea2-4cd4-bc3e-56e7e6caf854')
     def test_get_capabilities_backend(self):
         # Test backend properties
-        backend = self.admin_capabilities_client.show_backend_capabilities(
-            self.hosts[0])
-
-        # Verify getting capabilities parameters from a backend
-        for key in self.CAPABILITIES:
-            self.assertIn(key, backend)
+        # Check response schema
+        self.admin_capabilities_client.show_backend_capabilities(self.hosts[0])
 
     @decorators.idempotent_id('a9035743-d46a-47c5-9cb7-3c80ea16dea0')
     def test_compare_volume_stats_values(self):
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index 7e53ce8..83c27e1 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -26,13 +26,6 @@
                                 "The count of volume hosts is < 2, "
                                 "response of list hosts is: %s" % hosts)
 
-        # Check elements in volume hosts list
-        host_list_keys = ['service', 'host_name', 'last-update',
-                          'zone', 'service-status', 'service-state']
-        for host in hosts:
-            for key in host_list_keys:
-                self.assertIn(key, host)
-
     @decorators.idempotent_id('21168d57-b373-4b71-a3ac-f2c88f0c5d31')
     def test_show_host(self):
         hosts = self.admin_hosts_client.list_hosts()['hosts']
@@ -53,12 +46,6 @@
                             "all hosts that found are: %s" % hosts)
 
         # Check each cinder-volume host.
-        host_detail_keys = ['project', 'volume_count', 'snapshot_count',
-                            'host', 'total_volume_gb', 'total_snapshot_gb']
         for host in c_vol_hosts:
             host_details = self.admin_hosts_client.show_host(host)['host']
             self.assertNotEmpty(host_details)
-            for detail in host_details:
-                self.assertIn('resource', detail)
-                for key in host_detail_keys:
-                    self.assertIn(key, detail['resource'])
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 053a7d9..b073604 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -19,7 +19,6 @@
 
 QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups',
               'backup_gigabytes', 'per_volume_gigabytes']
-QUOTA_USAGE_KEYS = ['reserved', 'limit', 'in_use']
 
 
 class VolumeQuotasAdminTestJSON(base.BaseVolumeAdminTest):
@@ -55,17 +54,13 @@
 
     @decorators.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
     def test_list_quotas(self):
-        quotas = (self.admin_quotas_client.show_quota_set(self.demo_tenant_id)
-                  ['quota_set'])
-        for key in QUOTA_KEYS:
-            self.assertIn(key, quotas)
+        # Check response schema
+        self.admin_quotas_client.show_quota_set(self.demo_tenant_id)
 
     @decorators.idempotent_id('2be020a2-5fdd-423d-8d35-a7ffbc36e9f7')
     def test_list_default_quotas(self):
-        quotas = self.admin_quotas_client.show_default_quota_set(
-            self.demo_tenant_id)['quota_set']
-        for key in QUOTA_KEYS:
-            self.assertIn(key, quotas)
+        # Check response schema
+        self.admin_quotas_client.show_default_quota_set(self.demo_tenant_id)
 
     @decorators.idempotent_id('3d45c99e-cc42-4424-a56e-5cbd212b63a6')
     def test_update_all_quota_resources_for_tenant(self):
@@ -92,13 +87,9 @@
 
     @decorators.idempotent_id('18c51ae9-cb03-48fc-b234-14a19374dbed')
     def test_show_quota_usage(self):
-        quota_usage = self.admin_quotas_client.show_quota_set(
-            self.os_admin.credentials.tenant_id,
-            params={'usage': True})['quota_set']
-        for key in QUOTA_KEYS:
-            self.assertIn(key, quota_usage)
-            for usage_key in QUOTA_USAGE_KEYS:
-                self.assertIn(usage_key, quota_usage[key])
+        # Check response schema
+        self.admin_quotas_client.show_quota_set(
+            self.os_admin.credentials.tenant_id, params={'usage': True})
 
     @decorators.idempotent_id('874b35a9-51f1-4258-bec5-cd561b6690d3')
     def test_delete_quota(self):
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index c85e0bc..4cdf898 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -63,8 +63,6 @@
         # Accept a volume transfer by alt_tenant
         body = self.alt_client.accept_volume_transfer(
             transfer_id, auth_key=auth_key)['transfer']
-        for key in ['id', 'name', 'links', 'volume_id']:
-            self.assertIn(key, body)
         waiters.wait_for_volume_resource_status(self.alt_volumes_client,
                                                 volume['id'], 'available')
         accepted_volume = self.alt_volumes_client.show_volume(
@@ -95,8 +93,6 @@
         # elements, and look for the created transfer.
         transfers = self.client.list_volume_transfers(detail=True)['transfers']
         self.assertNotEmpty(transfers)
-        for transfer in transfers:
-            self.assertIn('created_at', transfer)
         volume_list = [transfer['volume_id'] for transfer in transfers]
         self.assertIn(volume['id'], volume_list,
                       'Transfer not found for volume %s' % volume['id'])
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index d76a323..dad710c 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -148,7 +148,7 @@
         self.exec_command('sudo umount %s' % mount_path)
 
     def make_fs(self, dev_name, fs='ext4'):
-        cmd_mkfs = 'sudo /usr/sbin/mke2fs -t %s /dev/%s' % (fs, dev_name)
+        cmd_mkfs = 'sudo mke2fs -t %s /dev/%s' % (fs, dev_name)
         try:
             self.exec_command(cmd_mkfs)
         except tempest.lib.exceptions.SSHExecCommandFailed:
diff --git a/tempest/config.py b/tempest/config.py
index 5f91a6d..d67d3e0 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -811,7 +811,7 @@
                default="password",
                help="Password used to authenticate to an instance."),
     cfg.StrOpt('ssh_shell_prologue',
-               default="set -eu -o pipefail; PATH=$$PATH:/sbin;",
+               default="set -eu -o pipefail; PATH=$$PATH:/sbin:/usr/sbin;",
                help="Shell fragments to use before executing a command "
                     "when sshing to a guest."),
     cfg.IntOpt('ping_size',
diff --git a/tempest/lib/api_schema/response/volume/capabilities.py b/tempest/lib/api_schema/response/volume/capabilities.py
new file mode 100644
index 0000000..ec60fc3
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/capabilities.py
@@ -0,0 +1,55 @@
+# 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.
+
+property_info = {
+    'type': 'object',
+    'properties': {
+        'type': {'type': 'string'},
+        'description': {'type': 'string'},
+        'title': {'type': 'string'}
+    },
+    'additionalProperties': False,
+    'required': ['type', 'description', 'title']
+}
+
+show_backend_capabilities = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'pool_name': {'type': ['string', 'null']},
+            'description': {'type': ['string', 'null']},
+            'volume_backend_name': {'type': 'string'},
+            'namespace': {'type': 'string',
+                          'pattern': '^OS::Storage::Capabilities::.+$'},
+            'visibility': {'type': ['string', 'null']},
+            'driver_version': {'type': 'string'},
+            'vendor_name': {'type': 'string'},
+            'properties': {
+                'type': 'object',
+                'properties': {
+                    '^.+$': property_info
+                },
+            },
+            'storage_protocol': {'type': 'string'},
+            'replication_targets': {'type': 'array'},
+            'display_name': {'type': ['string', 'null']}
+        },
+        'additionalProperties': False,
+        'required': ['pool_name', 'volume_backend_name', 'namespace',
+                     'visibility', 'driver_version', 'vendor_name',
+                     'properties', 'storage_protocol', 'replication_targets',
+                     'display_name', 'description']
+    }
+}
diff --git a/tempest/lib/api_schema/response/volume/hosts.py b/tempest/lib/api_schema/response/volume/hosts.py
new file mode 100644
index 0000000..ce67e9f
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/hosts.py
@@ -0,0 +1,81 @@
+# 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.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+show_host = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'host': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'resource': {
+                            'type': 'object',
+                            'properties': {
+                                'volume_count': {'type': 'string'},
+                                'total_volume_gb': {'type': 'string'},
+                                'total_snapshot_gb': {'type': 'string'},
+                                'project': {'type': 'string'},
+                                'host': {'type': 'string'},
+                                'snapshot_count': {'type': 'string'},
+                            },
+                            'additionalProperties': False,
+                            'required': ['volume_count', 'total_volume_gb',
+                                         'total_snapshot_gb', 'project',
+                                         'host', 'snapshot_count'],
+                        }
+                    },
+                    'additionalProperties': False,
+                    'required': ['resource']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['host']
+    }
+}
+
+list_hosts = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'hosts': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'service-status': {
+                            'enum': ['available', 'unavailable']},
+                        'service': {'type': 'string'},
+                        'zone': {'type': 'string'},
+                        'service-state': {
+                            'enum': ['enabled', 'disabled']},
+                        'host_name': {'type': 'string'},
+                        'last-update': parameter_types.date_time_or_null
+                    },
+                    'additionalProperties': False,
+                    'required': ['service-status', 'service', 'zone',
+                                 'service-state', 'host_name', 'last-update']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['hosts']
+    }
+}
diff --git a/tempest/lib/api_schema/response/volume/manage_snapshot.py b/tempest/lib/api_schema/response/volume/manage_snapshot.py
new file mode 100644
index 0000000..bbb9ee2
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/manage_snapshot.py
@@ -0,0 +1,49 @@
+# Copyright 2015 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+manage_snapshot = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'snapshot': {
+                'type': 'object',
+                'properties': {
+                    'status': {'type': 'string'},
+                    'size': {'type': 'integer'},
+                    'metadata': {
+                        'type': 'object',
+                        'patternProperties': {
+                            '^.+$': {'type': 'string'}
+                        }
+                    },
+                    'name': {'type': ['string', 'null']},
+                    'volume_id': {'type': 'string', 'format': 'uuid'},
+                    'created_at': parameter_types.date_time,
+                    'description': {'type': ['string', 'null']},
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'updated_at': parameter_types.date_time_or_null
+                },
+                'additionalProperties': False,
+                'required': ['status', 'size', 'volume_id',
+                             'created_at', 'description', 'id', 'updated_at']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['snapshot']
+    }
+}
diff --git a/tempest/lib/api_schema/response/volume/quotas.py b/tempest/lib/api_schema/response/volume/quotas.py
new file mode 100644
index 0000000..4be584c
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/quotas.py
@@ -0,0 +1,92 @@
+# Copyright 2019 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
+
+delete_quota_set = {
+    'status_code': [200],
+}
+
+quota_usage_info = {
+    'type': 'object',
+    'properties': {
+        'reserved': {'type': 'integer'},
+        'allocated': {'type': 'integer'},
+        'limit': {'type': 'integer'},
+        'in_use': {'type': 'integer'}
+    },
+    'additionalProperties': False,
+    # 'allocated' attribute is available only when nested quota is enabled.
+    'required': ['reserved', 'limit', 'in_use'],
+}
+
+show_quota_set = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'quota_set': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'volumes': {'type': 'integer'},
+                    'snapshots': {'type': 'integer'},
+                    'backups': {'type': 'integer'},
+                    'groups': {'type': 'integer'},
+                    'per_volume_gigabytes': {'type': 'integer'},
+                    'gigabytes': {'type': 'integer'},
+                    'backup_gigabytes': {'type': 'integer'},
+                },
+                # for volumes_{volume_type}, etc
+                "additionalProperties": {'type': 'integer'},
+                'required': ['id', 'volumes', 'snapshots', 'backups',
+                             'per_volume_gigabytes', 'gigabytes',
+                             'backup_gigabytes', 'groups'],
+            }
+        },
+        'required': ['quota_set']
+    }
+}
+
+update_quota_set = copy.deepcopy(show_quota_set)
+update_quota_set['response_body']['properties']['quota_set'][
+    'required'].remove('id')
+
+show_quota_set_usage = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'quota_set': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'volumes': quota_usage_info,
+                    'snapshots': quota_usage_info,
+                    'backups': quota_usage_info,
+                    'groups': quota_usage_info,
+                    'per_volume_gigabytes': quota_usage_info,
+                    'gigabytes': quota_usage_info,
+                    'backup_gigabytes': quota_usage_info,
+                },
+                # for volumes_{volume_type}, etc
+                "additionalProperties": quota_usage_info,
+                'required': ['id', 'volumes', 'snapshots', 'backups',
+                             'per_volume_gigabytes', 'gigabytes',
+                             'backup_gigabytes', 'groups'],
+            }
+        },
+        'required': ['quota_set']
+    }
+}
diff --git a/tempest/lib/api_schema/response/volume/transfers.py b/tempest/lib/api_schema/response/volume/transfers.py
new file mode 100644
index 0000000..d1d1b68
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/transfers.py
@@ -0,0 +1,129 @@
+# Copyright 2015 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+create_volume_transfer = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'transfer': {
+                'type': 'object',
+                'properties': {
+                    'auth_key': {'type': 'string'},
+                    'links': parameter_types.links,
+                    'created_at': parameter_types.date_time,
+                    'volume_id': {'type': 'string', 'format': 'uuid'},
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'name': {'type': ['string', 'null']}
+                },
+                'additionalProperties': False,
+                'required': ['auth_key', 'links', 'created_at',
+                             'volume_id', 'id', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['transfer']
+    }
+}
+
+common_show_volume_transfer = {
+    'type': 'object',
+    'properties': {
+        'links': parameter_types.links,
+        'created_at': parameter_types.date_time,
+        'volume_id': {'type': 'string', 'format': 'uuid'},
+        'id': {'type': 'string', 'format': 'uuid'},
+        'name': {'type': ['string', 'null']}
+    },
+    'additionalProperties': False,
+    'required': ['links', 'created_at', 'volume_id', 'id', 'name']
+}
+
+show_volume_transfer = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'transfer': common_show_volume_transfer
+        },
+        'additionalProperties': False,
+        'required': ['transfer']
+    }
+}
+
+list_volume_transfers_no_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'transfers': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'volume_id': {'type': 'string', 'format': 'uuid'},
+                        'id': {'type': 'string', 'format': 'uuid'},
+                        'links': parameter_types.links,
+                        'name': {'type': ['string', 'null']}
+                    },
+                    'additionalProperties': False,
+                    'required': ['volume_id', 'id', 'links', 'name']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['transfers'],
+    }
+}
+
+list_volume_transfers_with_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'transfers': {
+                'type': 'array',
+                'items': common_show_volume_transfer
+            }
+        },
+        'additionalProperties': False,
+        'required': ['transfers'],
+    }
+}
+
+delete_volume_transfer = {'status_code': [202]}
+
+accept_volume_transfer = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'transfer': {
+                'type': 'object',
+                'properties': {
+                    'links': parameter_types.links,
+                    'volume_id': {'type': 'string', 'format': 'uuid'},
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'name': {'type': ['string', 'null']}
+                },
+                'additionalProperties': False,
+                'required': ['links', 'volume_id', 'id', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['transfer']
+    }
+}
diff --git a/tempest/lib/api_schema/response/volume/versions.py b/tempest/lib/api_schema/response/volume/versions.py
old mode 100644
new mode 100755
diff --git a/tempest/lib/services/identity/v3/roles_client.py b/tempest/lib/services/identity/v3/roles_client.py
index 43a9020..f9356be 100644
--- a/tempest/lib/services/identity/v3/roles_client.py
+++ b/tempest/lib/services/identity/v3/roles_client.py
@@ -42,8 +42,12 @@
         return rest_client.ResponseBody(resp, body)
 
     def list_roles(self, **params):
-        """Get the list of Roles."""
+        """Get the list of Roles.
 
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3/index.html#list-roles
+        """
         url = 'roles'
         if params:
             url += '?%s' % urllib.urlencode(params)
diff --git a/tempest/lib/services/volume/v3/capabilities_client.py b/tempest/lib/services/volume/v3/capabilities_client.py
index ac2cd02..dc850a8 100644
--- a/tempest/lib/services/volume/v3/capabilities_client.py
+++ b/tempest/lib/services/volume/v3/capabilities_client.py
@@ -15,6 +15,7 @@
 
 from oslo_serialization import jsonutils as json
 
+from tempest.lib.api_schema.response.volume import capabilities as schema
 from tempest.lib.common import rest_client
 
 
@@ -30,5 +31,5 @@
         url = 'capabilities/%s' % host
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_backend_capabilities, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/hosts_client.py b/tempest/lib/services/volume/v3/hosts_client.py
index c95d2d2..019a852 100644
--- a/tempest/lib/services/volume/v3/hosts_client.py
+++ b/tempest/lib/services/volume/v3/hosts_client.py
@@ -16,6 +16,7 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
+from tempest.lib.api_schema.response.volume import hosts as schema
 from tempest.lib.common import rest_client
 
 
@@ -35,13 +36,13 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.list_hosts, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_host(self, host_name):
         """Show host details."""
         url = 'os-hosts/%s' % host_name
         resp, body = self.get(url)
-        self.expected_success(200, resp.status)
         body = json.loads(body)
+        self.validate_response(schema.show_host, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/quotas_client.py b/tempest/lib/services/volume/v3/quotas_client.py
index 4d680c1..5b1a52c 100644
--- a/tempest/lib/services/volume/v3/quotas_client.py
+++ b/tempest/lib/services/volume/v3/quotas_client.py
@@ -16,6 +16,7 @@
 from oslo_serialization import jsonutils
 from six.moves.urllib import parse as urllib
 
+from tempest.lib.api_schema.response.volume import quotas as schema
 from tempest.lib.common import rest_client
 
 
@@ -27,8 +28,8 @@
 
         url = 'os-quota-sets/%s/defaults' % tenant_id
         resp, body = self.get(url)
-        self.expected_success(200, resp.status)
         body = jsonutils.loads(body)
+        self.validate_response(schema.show_quota_set, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_quota_set(self, tenant_id, params=None):
@@ -39,8 +40,11 @@
             url += '?%s' % urllib.urlencode(params)
 
         resp, body = self.get(url)
-        self.expected_success(200, resp.status)
         body = jsonutils.loads(body)
+        if params and params.get('usage', False):
+            self.validate_response(schema.show_quota_set_usage, resp, body)
+        else:
+            self.validate_response(schema.show_quota_set, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_quota_set(self, tenant_id, **kwargs):
@@ -52,12 +56,12 @@
         """
         put_body = jsonutils.dumps({'quota_set': kwargs})
         resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
-        self.expected_success(200, resp.status)
         body = jsonutils.loads(body)
+        self.validate_response(schema.update_quota_set, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_quota_set(self, tenant_id):
         """Delete the tenant's quota set."""
         resp, body = self.delete('os-quota-sets/%s' % tenant_id)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.delete_quota_set, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/snapshot_manage_client.py b/tempest/lib/services/volume/v3/snapshot_manage_client.py
index a9e1f74..77920e4 100644
--- a/tempest/lib/services/volume/v3/snapshot_manage_client.py
+++ b/tempest/lib/services/volume/v3/snapshot_manage_client.py
@@ -15,6 +15,7 @@
 
 from oslo_serialization import jsonutils as json
 
+from tempest.lib.api_schema.response.volume import manage_snapshot as schema
 from tempest.lib.common import rest_client
 
 
@@ -31,6 +32,6 @@
         post_body = json.dumps({'snapshot': kwargs})
         url = 'os-snapshot-manage'
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
         body = json.loads(body)
+        self.validate_response(schema.manage_snapshot, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/transfers_client.py b/tempest/lib/services/volume/v3/transfers_client.py
index 39e3475..f572f95 100644
--- a/tempest/lib/services/volume/v3/transfers_client.py
+++ b/tempest/lib/services/volume/v3/transfers_client.py
@@ -16,6 +16,7 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
+from tempest.lib.api_schema.response.volume import transfers as schema
 from tempest.lib.common import rest_client
 
 
@@ -32,7 +33,7 @@
         post_body = json.dumps({'transfer': kwargs})
         resp, body = self.post('os-volume-transfer', post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.create_volume_transfer, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_volume_transfer(self, transfer_id):
@@ -40,7 +41,7 @@
         url = "os-volume-transfer/%s" % transfer_id
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_volume_transfer, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def list_volume_transfers(self, detail=False, **params):
@@ -52,19 +53,21 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-volume-transfers-and-details
         """
         url = 'os-volume-transfer'
+        schema_list_transfers = schema.list_volume_transfers_no_detail
         if detail:
             url += '/detail'
+            schema_list_transfers = schema.list_volume_transfers_with_detail
         if params:
             url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema_list_transfers, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_volume_transfer(self, transfer_id):
         """Delete a volume transfer."""
         resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.delete_volume_transfer, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def accept_volume_transfer(self, transfer_id, **kwargs):
@@ -78,5 +81,5 @@
         post_body = json.dumps({'accept': kwargs})
         resp, body = self.post(url, post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.accept_volume_transfer, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 1f0080f..644a018 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -88,7 +88,7 @@
     # the information using gnu/linux tools.
 
     def _assert_exec_called_with(self, cmd):
-        cmd = "set -eu -o pipefail; PATH=$PATH:/sbin; " + cmd
+        cmd = "set -eu -o pipefail; PATH=$PATH:/sbin:/usr/sbin; " + cmd
         self.ssh_mock.mock.exec_command.assert_called_with(cmd)
 
     def test_get_disks(self):
diff --git a/tempest/tests/lib/services/volume/v3/test_hosts_client.py b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
index 09bc0b1..8033e38 100644
--- a/tempest/tests/lib/services/volume/v3/test_hosts_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
@@ -48,7 +48,7 @@
                     "total_volume_gb": "2",
                     "total_snapshot_gb": "0",
                     "project": "(total)",
-                    "host": "fake-host",
+                    "host": "fake-host@rbd",
                     "snapshot_count": "0"
                 }
             },
@@ -58,7 +58,7 @@
                     "total_volume_gb": "2",
                     "total_snapshot_gb": "0",
                     "project": "f21a9c86d7114bf99c711f4874d80474",
-                    "host": "fake-host",
+                    "host": "fake-host@lvm",
                     "snapshot_count": "0"
                 }
             }
diff --git a/tempest/tests/lib/services/volume/v3/test_quotas_client.py b/tempest/tests/lib/services/volume/v3/test_quotas_client.py
index aa5d251..f09784c 100644
--- a/tempest/tests/lib/services/volume/v3/test_quotas_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_quotas_client.py
@@ -20,15 +20,26 @@
 class TestQuotasClient(base.BaseServiceTest):
     FAKE_QUOTAS = {
         "quota_set": {
+            "id": '730a1cbd-68ca-4d68-8e09-d603f2dfa72b',
             "gigabytes": 5,
             "snapshots": 10,
-            "volumes": 20
+            "volumes": 20,
+            'backups': 10,
+            'groups': 10,
+            'per_volume_gigabytes': 1000,
+            'backup_gigabytes': 2000
         }
     }
 
-    FAKE_UPDATE_QUOTAS_REQUEST = {
+    FAKE_UPDATE_QUOTAS_RESPONSE = {
         "quota_set": {
-            "security_groups": 45
+            "gigabytes": 6,
+            "snapshots": 11,
+            "volumes": 21,
+            'backups': 11,
+            'groups': 11,
+            'per_volume_gigabytes': 1001,
+            'backup_gigabytes': 2001
         }
     }
 
@@ -57,7 +68,7 @@
         self.check_service_client_function(
             self.client.update_quota_set,
             'tempest.lib.common.rest_client.RestClient.put',
-            self.FAKE_UPDATE_QUOTAS_REQUEST,
+            self.FAKE_UPDATE_QUOTAS_RESPONSE,
             bytes_body, tenant_id="fake_tenant")
 
     def test_show_default_quota_set_with_str_body(self):
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index d4ffd01..255487e 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -38,6 +38,7 @@
     'openstack/barbican-tempest-plugin',
     # https://review.opendev.org/#/c/634631/
     'x/gce-api',  # It looks gce-api doesn't support python3 yet.
+    'x/group-based-policy',  # It looks this doesn't support python3 yet.
     'x/intel-nfv-ci-tests',  # https://review.opendev.org/#/c/634640/
     'openstack/networking-generic-switch',
     # https://review.opendev.org/#/c/634846/
diff --git a/tox.ini b/tox.ini
index ca4bb3f..effd400 100644
--- a/tox.ini
+++ b/tox.ini
@@ -268,6 +268,15 @@
   sphinx-build -W -b html doc/source doc/build/html
 whitelist_externals = rm
 
+[testenv:pdf-docs]
+basepython = python3
+deps = {[testenv:docs]deps}
+whitelist_externals =
+   make
+commands =
+   sphinx-build -W -b latex doc/source doc/build/pdf
+   make -C doc/build/pdf
+
 [testenv:pep8]
 deps =
     -r{toxinidir}/test-requirements.txt