Merge "Fix test_snapshot_list_param_marker when using predefined user"
diff --git a/.zuul.yaml b/.zuul.yaml
index 67d1f5e..e4e95f2 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -45,6 +45,19 @@
         ENABLE_FILE_INJECTION: true
 
 - job:
+    name: tempest-full-parallel
+    parent: tempest-full
+    voting: false
+    branches:
+      - master
+    description: |
+      Base integration test with Neutron networking and py27.
+      It includes all scenarios as it was in the past.
+      This job runs all scenario tests in parallel!
+    vars:
+      tox_envlist: full-parallel
+
+- job:
     name: tempest-full-py3
     parent: devstack-tempest
     branches:
@@ -93,6 +106,13 @@
       tox_envlist: plugin-sanity-check
     voting: false
     timeout: 5000
+    irrelevant-files:
+      - ^.*\.rst$
+      - ^doc/.*$
+      - ^etc/.*$
+      - ^releasenotes/.*$
+      - ^tempest/hacking/.*$
+      - ^tempest/tests/.*$
     required-projects:
       - openstack/almanach
       - openstack/aodh
@@ -168,6 +188,16 @@
               - ^roles/
               - ^.zuul.yaml$
         - nova-multiattach
+        - tempest-full-parallel:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
         - tempest-full-queens:
             irrelevant-files:
               - ^(test-|)requirements.txt$
@@ -202,6 +232,18 @@
     gate:
       jobs:
         - nova-multiattach
+    experimental:
+      jobs:
+        - nova-cells-v1:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
     periodic-stable:
       jobs:
         - tempest-full-queens
diff --git a/HACKING.rst b/HACKING.rst
index f961884..bb55ac5 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -363,13 +363,24 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 When adding tests for new features that were not in previous releases of the
-projects the new test has to be properly skipped with a feature flag. Whether
-this is just as simple as using the @utils.requires_ext() decorator to
-check if the required extension (or discoverable optional API) is enabled or
+projects the new test has to be properly skipped with a feature flag. This can
+be just as simple as using the ``@utils.requires_ext()`` or
+``testtools.skipUnless`` decorators to check if the required extension (or
+discoverable optional API) or feature is enabled or can be as difficult as
 adding a new config option to the appropriate section. If there isn't a method
 of selecting the new **feature** from the config file then there won't be a
-mechanism to disable the test with older stable releases and the new test won't
-be able to merge.
+mechanism to disable the test with older stable releases and the new test
+won't be able to merge.
+
+Introduction of a new feature flag requires specifying a default value for
+the corresponding config option that is appropriate in the latest OpenStack
+release. Because Tempest is branchless, the feature flag's default value will
+need to be overridden to a value that is appropriate in earlier releases
+in which the feature isn't available. In DevStack, this can be accomplished
+by modifying Tempest's `lib installation script`_ for previous branches
+(because DevStack is branched).
+
+.. _lib installation script: http://git.openstack.org/cgit/openstack-dev/devstack/tree/lib/tempest
 
 2. Bug fix on core project needing Tempest changes
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/README.rst b/README.rst
index 242f4d5..f8059ae 100644
--- a/README.rst
+++ b/README.rst
@@ -2,7 +2,7 @@
 Team and repository tags
 ========================
 
-.. image:: https://governance.openstack.org/badges/tempest.svg
+.. 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
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 597b54e..d959d44 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -3,4 +3,4 @@
 # process, which may cause wedges in the gate later.
 openstackdocstheme>=1.18.1 # Apache-2.0
 reno>=2.5.0 # Apache-2.0
-sphinx!=1.6.6,>=1.6.2 # BSD
+sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
diff --git a/playbooks/post-tempest.yaml b/playbooks/post-tempest.yaml
index ab7a1bb..4dde2c9 100644
--- a/playbooks/post-tempest.yaml
+++ b/playbooks/post-tempest.yaml
@@ -1,7 +1,6 @@
 - hosts: all
   become: true
   roles:
-    - role: process-test-results
-      test_results_dir: '{{ devstack_base_dir }}/tempest'
-      tox_envdir: tempest
+    - role: fetch-subunit-output
+      zuul_work_dir: '{{ devstack_base_dir }}/tempest'
     - role: process-stackviz
diff --git a/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml b/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
index 3e43f9a..985acb0 100644
--- a/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
+++ b/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
@@ -3,4 +3,4 @@
   - A new helper method `service_client_config` has been added
     to the stable module config.py that returns extracts from
     configuration into a dictionary the configuration settings
-    relevant for the initisialisation of a service client.
+    relevant for the initialization of a service client.
diff --git a/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml b/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
index 0075a36..fc061bc 100644
--- a/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
+++ b/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
@@ -2,7 +2,7 @@
 upgrade:
   - The ``JSON_ENC`` and ``TXT_ENC`` option in the ``_error_checker``
     section have been added with additional content-type which are
-    defined in RFC7231 but missing in the currnt rest_client.py file.
+    defined in RFC7231 but missing in the current rest_client.py file.
     The lack of these additional content-type will cause defcore test
     to fail for OpenStack public cloud which uses tomcat module in the
     api gateway. The additions are ``application/json;charset=utf-8``,
diff --git a/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml b/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
index 9927971..18ad5b9 100644
--- a/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
+++ b/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
@@ -2,7 +2,7 @@
 features:
   - |
     Add server tags APIs to the servers_client library.
-    This feature enables the possibility of upating, deleting
+    This feature enables the possibility of updating, deleting
     and checking existence of a tag on a server, as well
     as updating and deleting all tags on a server.
 
diff --git a/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml b/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
index f679208..0b45d0d 100644
--- a/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
+++ b/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
@@ -6,5 +6,5 @@
 deprecations:
   - The resources_prefix is marked as deprecated because it is
     enough to set 'tempest' as the prefix on rand_name() to
-    ideintify resources which are created by Tempest and no
+    identify resources which are created by Tempest and no
     projects set this option on OpenStack dev community.
diff --git a/releasenotes/notes/add-additional-methods-to-policy-client-library-b8279c18335588c9.yaml b/releasenotes/notes/add-additional-methods-to-policy-client-library-b8279c18335588c9.yaml
new file mode 100644
index 0000000..cd5284d
--- /dev/null
+++ b/releasenotes/notes/add-additional-methods-to-policy-client-library-b8279c18335588c9.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add ``v3-ext/OS-ENDPOINT-POLICY`` API calls to support creation, deletion and
+    retrieval of associations between service endpoints and policies. Such associations
+    enable an endpoint to request its policy.
diff --git a/releasenotes/notes/add-support-args-kwargs-in-call-until-true-a91k592h5a64exf7.yaml b/releasenotes/notes/add-support-args-kwargs-in-call-until-true-a91k592h5a64exf7.yaml
index e23abe3..9c30a0c 100644
--- a/releasenotes/notes/add-support-args-kwargs-in-call-until-true-a91k592h5a64exf7.yaml
+++ b/releasenotes/notes/add-support-args-kwargs-in-call-until-true-a91k592h5a64exf7.yaml
@@ -2,4 +2,4 @@
 features:
   - Add support of args and kwargs when calling func in call_until_true,
     also to log the cost time when call_until_true returns True or False
-    for debuggin.
+    for debugging.
diff --git a/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml b/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
index 305e756..cc6c51b 100644
--- a/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
+++ b/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
@@ -3,7 +3,7 @@
   - |
     Add a new function called ``compare_version_header_to_response`` to
     ``tempest.lib.common.api_version_utils``, which compares the API
-    micoversion in the response header to another microversion using the
+    microversion in the response header to another microversion using the
     comparators defined in
     ``tempest.lib.common.api_version_request.APIVersionRequest``.
 
diff --git a/releasenotes/notes/fix-show-image-file-expected-code-92d97342d0f6d60e.yaml b/releasenotes/notes/fix-show-image-file-expected-code-92d97342d0f6d60e.yaml
new file mode 100644
index 0000000..a4050a5
--- /dev/null
+++ b/releasenotes/notes/fix-show-image-file-expected-code-92d97342d0f6d60e.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    Fix show_image_file interface in v2 ImagesClient: Bug#1756264.
+    The expected success code of show_image_file is changed from
+    ``200`` to ``[200, 204, 206]``.
diff --git a/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
new file mode 100644
index 0000000..0da2ddc
--- /dev/null
+++ b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
@@ -0,0 +1,10 @@
+---
+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'.
+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 df1de46..2518703 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -2,19 +2,22 @@
  Tempest Release Notes
 ===========================
 
- .. toctree::
-    :maxdepth: 1
+.. toctree::
+   :maxdepth: 1
 
-    unreleased
-    v17.0.0
-    v16.1.0
-    v16.0.0
-    v15.0.0
-    v14.0.0
-    v13.0.0
-    v12.0.0
-    v11.0.0
-    v10.0.0
+   unreleased
+   v18.0.0
+   v17.2.0
+   v17.1.0
+   v17.0.0
+   v16.1.0
+   v16.0.0
+   v15.0.0
+   v14.0.0
+   v13.0.0
+   v12.0.0
+   v11.0.0
+   v10.0.0
 
 Indices and tables
 ==================
diff --git a/releasenotes/source/v17.1.0.rst b/releasenotes/source/v17.1.0.rst
new file mode 100644
index 0000000..b8fd570
--- /dev/null
+++ b/releasenotes/source/v17.1.0.rst
@@ -0,0 +1,6 @@
+=====================
+v17.1.0 Release Notes
+=====================
+
+.. release-notes:: 17.1.0 Release Notes
+   :version: 17.1.0
diff --git a/releasenotes/source/v17.2.0.rst b/releasenotes/source/v17.2.0.rst
new file mode 100644
index 0000000..8566ae4
--- /dev/null
+++ b/releasenotes/source/v17.2.0.rst
@@ -0,0 +1,6 @@
+=====================
+v17.2.0 Release Notes
+=====================
+
+.. release-notes:: 17.2.0 Release Notes
+   :version: 17.2.0
diff --git a/releasenotes/source/v18.0.0.rst b/releasenotes/source/v18.0.0.rst
new file mode 100644
index 0000000..aa05db3
--- /dev/null
+++ b/releasenotes/source/v18.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v18.0.0 Release Notes
+=====================
+
+.. release-notes:: 18.0.0 Release Notes
+   :version: 18.0.0
diff --git a/requirements.txt b/requirements.txt
index 76db574..7520d42 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,15 +7,15 @@
 testtools>=2.2.0 # MIT
 paramiko>=2.0.0 # LGPLv2.1+
 netaddr>=0.7.18 # BSD
-oslo.concurrency>=3.25.0 # Apache-2.0
-oslo.config>=5.1.0 # Apache-2.0
+oslo.concurrency>=3.26.0 # Apache-2.0
+oslo.config>=5.2.0 # Apache-2.0
 oslo.log>=3.36.0 # Apache-2.0
 stestr>=1.0.0 # Apache-2.0
 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
 oslo.utils>=3.33.0 # Apache-2.0
 six>=1.10.0 # MIT
 fixtures>=3.0.0 # Apache-2.0/BSD
-PyYAML>=3.10 # MIT
+PyYAML>=3.12 # MIT
 python-subunit>=1.0.0 # Apache-2.0/BSD
 stevedore>=1.20.0 # Apache-2.0
 PrettyTable<0.8,>=0.7.1 # BSD
diff --git a/roles/process-stackviz/README.rst b/roles/process-stackviz/README.rst
index 54c217b..a8447d2 100644
--- a/roles/process-stackviz/README.rst
+++ b/roles/process-stackviz/README.rst
@@ -16,7 +16,7 @@
    The stage directory where the input data can be found and
    the output will be produced.
 
-.. zuul:rolevar:: test_results_stage_name
-   :default: test_results
+.. zuul:rolevar:: zuul_work_dir
+   :default: {{ devstack_base_dir }}/tempest
 
-   The name of the subunit file to be used as input.
+   Directory to work in. It has to be a fully qualified path.
diff --git a/roles/process-stackviz/defaults/main.yaml b/roles/process-stackviz/defaults/main.yaml
index c6a64d1..f3bc32b 100644
--- a/roles/process-stackviz/defaults/main.yaml
+++ b/roles/process-stackviz/defaults/main.yaml
@@ -1,3 +1,3 @@
 devstack_base_dir: /opt/stack
 stage_dir: "{{ ansible_user_dir }}"
-test_results_stage_name: test_results
+zuul_work_dir: "{{ devstack_base_dir }}/tempest"
diff --git a/roles/process-stackviz/tasks/main.yaml b/roles/process-stackviz/tasks/main.yaml
index 09de606..3724e0e 100644
--- a/roles/process-stackviz/tasks/main.yaml
+++ b/roles/process-stackviz/tasks/main.yaml
@@ -9,11 +9,11 @@
 
 - name: Check if subunit data exists
   stat:
-    path: "{{ stage_dir }}/{{ test_results_stage_name }}.subunit"
+    path: "{{ zuul_work_dir }}/testrepository.subunit"
   register: subunit_input
 
 - debug:
-    msg: "Subunit file could not be found at {{ stage_dir }}/{{ test_results_stage_name }}.subunit"
+    msg: "Subunit file could not be found at {{ zuul_work_dir }}/testrepository.subunit"
   when: not subunit_input.stat.exists
 
 - name: Install stackviz
@@ -50,6 +50,7 @@
     - stackviz_archive.stat.exists
     - subunit_input.stat.exists
     - dstat_input.stat.exists
+  failed_when: False
 
 - name: Run stackviz without dstat
   shell: |
@@ -61,3 +62,4 @@
     - stackviz_archive.stat.exists
     - subunit_input.stat.exists
     - not dstat_input.stat.exists
+  failed_when: False
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index 33dcce9..384ca38 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -29,6 +29,12 @@
                # Line with only a comment.
                (tempest\.(api|scenario|thirdparty)).*$    # Run only api scenario and third party
 
+.. zuul:rolevar:: tempest_test_blacklist
+
+   Specifies a blacklist file to skip tests that are not needed.
+
+   Pass a full path to the file.
+
 .. zuul:rolevar:: tox_envlist
    :default: smoke
 
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 87898db..b68507a 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -20,8 +20,22 @@
     default_concurrency: "{{ num_cores|int // 2 }}"
   when: num_cores|int > 3
 
+- when:
+    - tempest_test_blacklist is defined
+  block:
+    - name: Check for test blacklist file
+      stat:
+        path: "{{ tempest_test_blacklist }}"
+      register:
+        blacklist_stat
+
+    - name: Build blacklist option
+      set_fact:
+        blacklist_option: "--blacklist-file={{ tempest_test_blacklist|quote }}"
+      when: blacklist_stat.stat.exists
+
 - name: Run Tempest
-  command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} --concurrency={{tempest_concurrency|default(default_concurrency)}}
+  command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} {{blacklist_option|default('')}} --concurrency={{tempest_concurrency|default(default_concurrency)}}
   args:
     chdir: "{{devstack_base_dir}}/tempest"
   become: true
diff --git a/setup.cfg b/setup.cfg
index 04bb29f..c981370 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -49,7 +49,8 @@
 
 [build_sphinx]
 all-files = 1
-warning-is-error = 1
+# warning can be generated by using GENERATE_TEMPEST_PLUGIN_LIST='False'
+warning-is-error = 0
 build-dir = doc/build
 source-dir = doc/source
 
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index 36ff09e..a6e0efa 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -27,16 +27,17 @@
     def setup_clients(cls):
         super(AggregatesAdminNegativeTestJSON, cls).setup_clients()
         cls.client = cls.os_admin.aggregates_client
+        cls.services_client = cls.os_admin.services_client
 
     @classmethod
     def resource_setup(cls):
         super(AggregatesAdminNegativeTestJSON, cls).resource_setup()
         cls.aggregate_name_prefix = 'test_aggregate'
 
-        hosts_all = cls.os_admin.hosts_client.list_hosts()['hosts']
-        hosts = ([host['host_name']
-                 for host in hosts_all if host['service'] == 'compute'])
-        cls.host = hosts[0]
+        svc_list = cls.services_client.list_services(
+            binary='nova-compute')['services']
+        cls.hosts = [v['host'] for v in svc_list
+                     if v['status'] == 'enabled' and v['state'] == 'up']
 
     def _create_test_aggregate(self):
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
@@ -123,11 +124,9 @@
     @decorators.idempotent_id('0ef07828-12b4-45ba-87cc-41425faf5711')
     def test_aggregate_add_non_exist_host(self):
         # Adding a non-exist host to an aggregate should raise exceptions.
-        hosts_all = self.os_admin.hosts_client.list_hosts()['hosts']
-        hosts = map(lambda x: x['host_name'], hosts_all)
         while True:
             non_exist_host = data_utils.rand_name('nonexist_host')
-            if non_exist_host not in hosts:
+            if non_exist_host not in self.hosts:
                 break
         aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.NotFound, self.client.add_host,
@@ -140,7 +139,7 @@
         aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.Forbidden,
                           self.aggregates_client.add_host,
-                          aggregate['id'], host=self.host)
+                          aggregate['id'], host=self.hosts[0])
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('19dd44e1-c435-4ee1-a402-88c4f90b5950')
@@ -148,12 +147,12 @@
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate = self._create_test_aggregate()
 
-        self.client.add_host(aggregate['id'], host=self.host)
+        self.client.add_host(aggregate['id'], host=self.hosts[0])
         self.addCleanup(self.client.remove_host, aggregate['id'],
-                        host=self.host)
+                        host=self.hosts[0])
 
         self.assertRaises(lib_exc.Conflict, self.client.add_host,
-                          aggregate['id'], host=self.host)
+                          aggregate['id'], host=self.hosts[0])
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7a53af20-137a-4e44-a4ae-e19260e626d9')
@@ -162,13 +161,13 @@
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate = self._create_test_aggregate()
 
-        self.client.add_host(aggregate['id'], host=self.host)
+        self.client.add_host(aggregate['id'], host=self.hosts[0])
         self.addCleanup(self.client.remove_host, aggregate['id'],
-                        host=self.host)
+                        host=self.hosts[0])
 
         self.assertRaises(lib_exc.Forbidden,
                           self.aggregates_client.remove_host,
-                          aggregate['id'], host=self.host)
+                          aggregate['id'], host=self.hosts[0])
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('95d6a6fa-8da9-4426-84d0-eec0329f2e4d')
diff --git a/tempest/api/compute/admin/test_auto_allocate_network.py b/tempest/api/compute/admin/test_auto_allocate_network.py
index c4d5768..e8011a6 100644
--- a/tempest/api/compute/admin/test_auto_allocate_network.py
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -148,9 +148,7 @@
     def test_server_create_no_allocate(self):
         """Tests that no networking is allocated for the server."""
         # create the server with no networking
-        server, _ = compute.create_test_server(
-            self.os_primary, networks='none', wait_until='ACTIVE')
-        self.addCleanup(self.delete_server, server['id'])
+        server = self.create_test_server(networks='none', wait_until='ACTIVE')
         # get the server ips
         addresses = self.servers_client.list_addresses(
             server['id'])['addresses']
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index ba19937..72d09ed 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -31,6 +31,7 @@
     API documentation - http://docs.openstack.org/api/openstack-compute/2/
     content/ext-os-floating-ips-bulk.html
     """
+    max_microversion = '2.35'
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index 00f3256..c246685 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -20,6 +20,8 @@
 class HostsAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests hosts API using admin privileges."""
 
+    max_microversion = '2.42'
+
     @classmethod
     def setup_clients(cls):
         super(HostsAdminTestJSON, cls).setup_clients()
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index 5bd8104..8a91ae2 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -20,6 +20,8 @@
 class HostsAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
     """Tests hosts API using admin privileges."""
 
+    max_microversion = '2.42'
+
     @classmethod
     def setup_clients(cls):
         super(HostsAdminNegativeTestJSON, cls).setup_clients()
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 99bad8f..ed8cf20 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -126,13 +126,13 @@
         self.assertIn(volume1['id'], vol_attachments[0]['volumeId'])
 
 
-class AttachVolumeMultiAttachTest(TestVolumeSwapBase):
+class TestMultiAttachVolumeSwap(TestVolumeSwapBase):
     min_microversion = '2.60'
     max_microversion = 'latest'
 
     @classmethod
     def skip_checks(cls):
-        super(AttachVolumeMultiAttachTest, cls).skip_checks()
+        super(TestMultiAttachVolumeSwap, cls).skip_checks()
         if not CONF.compute_feature_enabled.volume_multiattach:
             raise cls.skipException('Volume multi-attach is not available.')
 
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 9759be7..760e356 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -532,10 +532,10 @@
     def get_host_other_than(self, server_id):
         source_host = self.get_host_for_server(server_id)
 
-        hypers = self.os_admin.hypervisor_client.list_hypervisors(
-            )['hypervisors']
-        hosts = [hyper['hypervisor_hostname'] for hyper in hypers
-                 if hyper['state'] == 'up' and hyper['status'] == 'enabled']
+        svcs = self.os_admin.services_client.list_services(
+            binary='nova-compute')['services']
+        hosts = [svc['host'] for svc in svcs
+                 if svc['state'] == 'up' and svc['status'] == 'enabled']
 
         for target_host in hosts:
             if source_host != target_host:
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index b497626..1f3af5f 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -28,6 +28,7 @@
 
 
 class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
+    max_microversion = '2.38'
 
     @classmethod
     def skip_checks(cls):
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 03d0789..407fb08 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -20,6 +20,7 @@
 
 
 class ImagesMetadataNegativeTestJSON(base.BaseV2ComputeTest):
+    max_microversion = '2.38'
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index c660821..122c4f5 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -135,8 +135,13 @@
             servers_client=self.client)
         hostname = linux_client.exec_command("hostname").rstrip()
         msg = ('Failed while verifying servername equals hostname. Expected '
-               'hostname "%s" but got "%s".' % (self.name, hostname))
-        self.assertEqual(self.name.lower(), hostname, msg)
+               'hostname "%s" but got "%s".' %
+               (self.name, hostname.split(".")[0]))
+        # NOTE(zhufl): Some images will add postfix for the hostname, e.g.,
+        # if hostname is "aaa", postfix ".novalocal" may be added to it, and
+        # the hostname will be "aaa.novalocal" then, so we should ignore the
+        # postfix when checking whether hostname equals self.name.
+        self.assertEqual(self.name.lower(), hostname.split(".")[0], msg)
 
 
 class ServersTestManualDisk(ServersTestJSON):
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 43046ca..b0d527c 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -47,8 +47,8 @@
             raise cls.skipException('Neutron is required')
         if not CONF.validation.run_validation:
             raise cls.skipException('Validation must be enabled')
-        if (not CONF.compute_feature_enabled.config_drive
-            and not CONF.compute_feature_enabled.metadata_service):
+        if (not CONF.compute_feature_enabled.config_drive and
+                not CONF.compute_feature_enabled.metadata_service):
             raise cls.skipException('One of metadata or config drive must be '
                                     'enabled')
 
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index d9581e3..1dfd0f9 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -151,11 +151,22 @@
         self.assertTrue(
             self._websocket.response.startswith(b'HTTP/1.1 101 Switching '
                                                 b'Protocols\r\n'),
-            'Did not get the expected 101 on the websockify call: '
-            + six.text_type(self._websocket.response))
-        self.assertTrue(
-            self._websocket.response.find(b'Server: WebSockify') > 0,
-            'Did not get the expected WebSocket HTTP Response.')
+            'Did not get the expected 101 on the {} call: {}'.format(
+                CONF.compute_feature_enabled.vnc_server_header,
+                six.text_type(self._websocket.response)
+            )
+        )
+        # Since every other server type returns Headers with different case
+        # (for example 'nginx'), lowercase must be applied to eliminate issues.
+        _desired_header = "server: {0}".format(
+            CONF.compute_feature_enabled.vnc_server_header
+        ).lower()
+        _response = six.text_type(self._websocket.response).lower()
+        self.assertIn(
+            _desired_header,
+            _response,
+            'Did not get the expected WebSocket HTTP Response.'
+        )
 
     @decorators.idempotent_id('c640fdff-8ab4-45a4-a5d8-7e6146cbd0dc')
     def test_novnc(self):
diff --git a/tempest/api/compute/test_tenant_networks.py b/tempest/api/compute/test_tenant_networks.py
index b55e2c0..f4eada0 100644
--- a/tempest/api/compute/test_tenant_networks.py
+++ b/tempest/api/compute/test_tenant_networks.py
@@ -18,6 +18,7 @@
 
 
 class ComputeTenantNetworksTest(base.BaseV2ComputeTest):
+    max_microversion = '2.35'
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 76723f4..2432c8b 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -132,8 +132,8 @@
     @classmethod
     def skip_checks(cls):
         super(ListImagesTest, cls).skip_checks()
-        if (len(CONF.image.container_formats) < 2
-           or len(CONF.image.disk_formats) < 2):
+        if (len(CONF.image.container_formats) < 2 or
+                len(CONF.image.disk_formats) < 2):
             skip_msg = ("%s skipped as multiple container formats "
                         "or disk formats are not available." % cls.__name__)
             raise cls.skipException(skip_msg)
@@ -227,8 +227,8 @@
             self.assertEqual(image['disk_format'], self.disk_format_alt)
         result_set = set(map(lambda x: x['id'], images_list))
         self.assertTrue(self.same_disk_format_set <= result_set)
-        self.assertFalse(self.created_set - self.same_disk_format_set
-                         <= result_set)
+        self.assertFalse(self.created_set - self.same_disk_format_set <=
+                         result_set)
 
     @decorators.idempotent_id('2143655d-96d9-4bec-9188-8674206b4b3b')
     def test_index_container_format(self):
@@ -238,8 +238,8 @@
             self.assertEqual(image['container_format'], self.container_format)
         result_set = set(map(lambda x: x['id'], images_list))
         self.assertTrue(self.same_container_format_set <= result_set)
-        self.assertFalse(self.created_set - self.same_container_format_set
-                         <= result_set)
+        self.assertFalse(self.created_set - self.same_container_format_set <=
+                         result_set)
 
     @decorators.idempotent_id('feb32ac6-22bb-4a16-afd8-9454bb714b14')
     def test_index_max_size(self):
diff --git a/tempest/api/image/v2/admin/test_images.py b/tempest/api/image/v2/admin/test_images.py
index 4ff0268..dbb8c58 100644
--- a/tempest/api/image/v2/admin/test_images.py
+++ b/tempest/api/image/v2/admin/test_images.py
@@ -31,3 +31,21 @@
         self.addCleanup(self.admin_client.delete_image, image['id'])
         image_info = self.admin_client.show_image(image['id'])
         self.assertEqual(random_id, image_info['owner'])
+
+    @decorators.related_bug('1420008')
+    @decorators.idempotent_id('525ba546-10ef-4aad-bba1-1858095ce553')
+    def test_update_image_owner_param(self):
+        random_id_1 = data_utils.rand_uuid_hex()
+        image = self.admin_client.create_image(
+            container_format='bare', disk_format='raw', owner=random_id_1)
+        self.addCleanup(self.admin_client.delete_image, image['id'])
+        created_image_info = self.admin_client.show_image(image['id'])
+
+        random_id_2 = data_utils.rand_uuid_hex()
+        self.admin_client.update_image(
+            image['id'], [dict(replace="/owner", value=random_id_2)])
+        updated_image_info = self.admin_client.show_image(image['id'])
+
+        self.assertEqual(random_id_2, updated_image_info['owner'])
+        self.assertNotEqual(created_image_info['owner'],
+                            updated_image_info['owner'])
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index ce0cbd2..7e53ce8 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -46,8 +46,8 @@
         # show host API should fail (return code: 404). The cinder-volume host
         # is presented in format: <host-name>@driver-name.
         c_vol_hosts = [host['host_name'] for host in hosts
-                       if (host['service'] == 'cinder-volume'
-                           and host['service-state'] == 'enabled')]
+                       if (host['service'] == 'cinder-volume' and
+                           host['service-state'] == 'enabled')]
         self.assertNotEmpty(c_vol_hosts,
                             "No available cinder-volume host is found, "
                             "all hosts that found are: %s" % hosts)
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 54052ae..362f4cc 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -32,7 +32,7 @@
     @decorators.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
     def test_volume_extend(self):
         # Extend Volume Test.
-        volume = self.create_volume()
+        volume = self.create_volume(image_ref=self.image_ref)
         extend_size = volume['size'] + 1
         self.volumes_client.extend_volume(volume['id'],
                                           new_size=extend_size)
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index dcd3518..52114bc 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -41,16 +41,19 @@
     def test_snapshot_create_delete_with_volume_in_use(self):
         # Create a test instance
         server = self.create_server()
-        self.attach_volume(server['id'], self.volume_origin['id'])
+        # NOTE(zhufl) Here we create volume from self.image_ref for adding
+        # coverage for "creating snapshot from non-blank volume".
+        volume = self.create_volume(image_ref=self.image_ref)
+        self.attach_volume(server['id'], volume['id'])
 
         # Snapshot a volume which attached to an instance with force=False
         self.assertRaises(lib_exc.BadRequest, self.create_snapshot,
-                          self.volume_origin['id'], force=False)
+                          volume['id'], force=False)
 
         # Snapshot a volume attached to an instance
-        snapshot1 = self.create_snapshot(self.volume_origin['id'], force=True)
-        snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
-        snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+        snapshot1 = self.create_snapshot(volume['id'], force=True)
+        snapshot2 = self.create_snapshot(volume['id'], force=True)
+        snapshot3 = self.create_snapshot(volume['id'], force=True)
 
         # Delete the snapshots. Some snapshot implementations can take
         # different paths according to order they are deleted.
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 025959a..27e1bc1 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -104,11 +104,11 @@
             self.tenant_filter['tenant_id'] = self.tenant_id
 
     def _filter_by_tenant_id(self, item_list):
-        if (item_list is None
-                or not item_list
-                or not hasattr(self, 'tenant_id')
-                or self.tenant_id is None
-                or 'tenant_id' not in item_list[0]):
+        if (item_list is None or
+                not item_list or
+                not hasattr(self, 'tenant_id') or
+                self.tenant_id is None or
+                'tenant_id' not in item_list[0]):
             return item_list
 
         return [item for item in item_list
@@ -816,8 +816,8 @@
             if not self.is_save_state:
                 roles = [role for role in roles if
                          (role['id'] not in
-                          self.saved_state_json['roles'].keys()
-                          and role['name'] != CONF.identity.admin_role)]
+                          self.saved_state_json['roles'].keys() and
+                          role['name'] != CONF.identity.admin_role)]
                 LOG.debug("List count, %s Roles after reconcile", len(roles))
             return roles
         except Exception:
@@ -852,13 +852,16 @@
     def list(self):
         projects = self.client.list_projects()['projects']
         if not self.is_save_state:
-            projects = [project for project in projects if (project['id']
-                        not in self.saved_state_json['projects'].keys()
-                        and project['name'] != CONF.auth.admin_project_name)]
+            project_ids = self.saved_state_json['projects']
+            projects = [project
+                        for project in projects
+                        if (project['id'] not in project_ids and
+                            project['name'] != CONF.auth.admin_project_name)]
 
         if self.is_preserve:
-            projects = [project for project in projects if project['name']
-                        not in CONF_PROJECTS]
+            projects = [project
+                        for project in projects
+                        if project['name'] not in CONF_PROJECTS]
 
         LOG.debug("List count, %s Projects after reconcile", len(projects))
         return projects
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
index f0ade7e..a4402fe 100644
--- a/tempest/cmd/subunit_describe_calls.py
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -29,6 +29,9 @@
   written to. This contains more information than is present in stdout.
 * ``--ports, -p``: (Optional) The path to a JSON file describing the ports
   being used by different services
+* ``--verbose, -v``: (Optional) Print Request and Response Headers and Body
+  data to stdout
+
 
 Usage
 -----
@@ -262,6 +265,10 @@
             "-p", "--ports", metavar="<ports file>", default=None,
             help="A JSON file describing the ports for each service.")
 
+        self.add_argument(
+            "-v", "--verbose", action='store_true', default=False,
+            help="Add Request and Response header and body data to stdout.")
+
 
 def parse(stream, non_subunit_name, ports):
     if ports is not None and os.path.exists(ports):
@@ -286,7 +293,7 @@
     return url_parser
 
 
-def output(url_parser, output_file):
+def output(url_parser, output_file, verbose):
     if output_file is not None:
         with open(output_file, "w") as outfile:
             outfile.write(json.dumps(url_parser.test_logs))
@@ -302,13 +309,22 @@
             sys.stdout.write('\t- {0} {1} request for {2} to {3}\n'.format(
                 item.get('status_code'), item.get('verb'),
                 item.get('service'), item.get('url')))
+            if verbose:
+                sys.stdout.write('\t\t- request headers: {0}\n'.format(
+                    item.get('request_headers')))
+                sys.stdout.write('\t\t- request body: {0}\n'.format(
+                    item.get('request_body')))
+                sys.stdout.write('\t\t- response headers: {0}\n'.format(
+                    item.get('response_headers')))
+                sys.stdout.write('\t\t- response body: {0}\n'.format(
+                    item.get('response_body')))
         sys.stdout.write('\n')
 
 
 def entry_point():
     cl_args = ArgumentParser().parse_args()
     parser = parse(cl_args.subunit, cl_args.non_subunit_name, cl_args.ports)
-    output(parser, cl_args.output_file)
+    output(parser, cl_args.output_file, cl_args.verbose)
 
 
 if __name__ == "__main__":
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 638ad9b..68c4a10 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -289,7 +289,12 @@
 
 def create_websocket(url):
     url = urlparse.urlparse(url)
-    for res in socket.getaddrinfo(url.hostname, url.port,
+
+    # NOTE(mnaser): It is possible that there is no port specified, so fall
+    #               back to the default port based on the scheme.
+    port = url.port or (443 if url.scheme == 'https' else 80)
+
+    for res in socket.getaddrinfo(url.hostname, port,
                                   socket.AF_UNSPEC, socket.SOCK_STREAM):
         af, socktype, proto, _, sa = res
         client_socket = socket.socket(af, socktype, proto)
@@ -382,7 +387,12 @@
         """Upgrade the HTTP connection to a WebSocket and verify."""
         # The real request goes to the /websockify URI always
         reqdata = 'GET /websockify HTTP/1.1\r\n'
-        reqdata += 'Host: %s:%s\r\n' % (url.hostname, url.port)
+        reqdata += 'Host: %s' % url.hostname
+        # Add port only if we have one specified
+        if url.port:
+            reqdata += ':%s' % url.port
+        # Line-ending for Host header
+        reqdata += '\r\n'
         # Tell the HTTP Server to Upgrade the connection to a WebSocket
         reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
         # The token=xxx is sent as a Cookie not in the URI
diff --git a/tempest/config.py b/tempest/config.py
index 8a6370a..a2ccb84 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -237,7 +237,10 @@
     cfg.BoolOpt('security_compliance',
                 default=False,
                 help='Does the environment have the security compliance '
-                     'settings enabled?')
+                     'settings enabled?'),
+    cfg.BoolOpt('project_tags',
+                default=False,
+                help='Is the project tags identity v3 API available?')
 ]
 
 compute_group = cfg.OptGroup(name='compute',
@@ -404,6 +407,10 @@
                 default=False,
                 help='Enable VNC console. This configuration value should '
                      'be same as [nova.vnc]->vnc_enabled in nova.conf'),
+    cfg.StrOpt('vnc_server_header',
+               default='WebSockify',
+               help='Expected VNC server name (WebSockify, nginx, etc) '
+                    'in response header.'),
     cfg.BoolOpt('spice_console',
                 default=False,
                 help='Enable Spice console. This configuration value should '
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index aae685c..b6e7f8c 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -228,12 +228,12 @@
     if 'tempest/lib/' not in filename:
         return
 
-    if not ('from tempest' in logical_line
-            or 'import tempest' in logical_line):
+    if not ('from tempest' in logical_line or
+            'import tempest' in logical_line):
         return
 
-    if ('from tempest.lib' in logical_line
-        or 'import tempest.lib' in logical_line):
+    if ('from tempest.lib' in logical_line or
+            'import tempest.lib' in logical_line):
         return
 
     msg = ("T112: tempest.lib should not import local tempest code to avoid "
@@ -266,9 +266,9 @@
     if 'tempest/lib/' not in filename:
         return
 
-    if ('tempest.config' in logical_line
-        or 'from tempest import config' in logical_line
-        or 'oslo_config' in logical_line):
+    if ('tempest.config' in logical_line or
+            'from tempest import config' in logical_line or
+            'oslo_config' in logical_line):
         msg = ('T114: tempest.lib can not have any dependency on tempest '
                'config.')
         yield(0, msg)
diff --git a/tempest/lib/base.py b/tempest/lib/base.py
index 33a32ee..3be55c0 100644
--- a/tempest/lib/base.py
+++ b/tempest/lib/base.py
@@ -43,8 +43,7 @@
         super(BaseTestCase, self).setUp()
         if not self.setUpClassCalled:
             raise RuntimeError("setUpClass does not calls the super's "
-                               "setUpClass in the "
-                               + self.__class__.__name__)
+                               "setUpClass in {!r}".format(type(self)))
         test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
         try:
             test_timeout = int(test_timeout)
@@ -62,7 +61,7 @@
             stderr = self.useFixture(fixtures.StringStream('stderr')).stream
             self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
         if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
-            os.environ.get('OS_LOG_CAPTURE') != '0'):
+                os.environ.get('OS_LOG_CAPTURE') != '0'):
             self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
                                                    format=self.log_format,
                                                    level=None))
diff --git a/tempest/lib/cli/output_parser.py b/tempest/lib/cli/output_parser.py
index 2edd5c1..a7d5e49 100644
--- a/tempest/lib/cli/output_parser.py
+++ b/tempest/lib/cli/output_parser.py
@@ -37,8 +37,8 @@
     items = []
     tables_ = tables(output_lines)
     for table_ in tables_:
-        if ('Property' not in table_['headers']
-            or 'Value' not in table_['headers']):
+        if ('Property' not in table_['headers'] or
+                'Value' not in table_['headers']):
             raise exceptions.InvalidStructure()
         item = {}
         for value in table_['values']:
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 101d692..d1f0888 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -103,7 +103,7 @@
     def _modules_search(self):
         """Recursive search for python modules in base package"""
         modules = []
-        for root, dirs, files in os.walk(self.base_path):
+        for root, _, files in os.walk(self.base_path):
             if not os.path.exists(os.path.join(root, '__init__.py')):
                 continue
             root_package = self._path_to_package(root)
@@ -121,10 +121,10 @@
         idempotent_id = None
         for decorator in test_node.decorator_list:
             if (hasattr(decorator, 'func') and
-                hasattr(decorator.func, 'attr') and
-                decorator.func.attr == DECORATOR_NAME and
-                hasattr(decorator.func, 'value') and
-                decorator.func.value.id == DECORATOR_MODULE):
+                    hasattr(decorator.func, 'attr') and
+                    decorator.func.attr == DECORATOR_NAME and
+                    hasattr(decorator.func, 'value') and
+                    decorator.func.value.id == DECORATOR_MODULE):
                 for arg in decorator.args:
                     idempotent_id = ast.literal_eval(arg)
         return idempotent_id
@@ -165,8 +165,8 @@
 
     @staticmethod
     def _is_test_method(node):
-        return (node.__class__ is ast.FunctionDef
-                and node.name.startswith('test_'))
+        return (node.__class__ is ast.FunctionDef and
+                node.name.startswith('test_'))
 
     @staticmethod
     def _next_node(body, node):
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index 4f1a883..f27e926 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -338,15 +338,15 @@
                 credentials = self._create_creds(roles=credential_type)
             self._creds[str(credential_type)] = credentials
             # Maintained until tests are ported
-            LOG.info("Acquired dynamic creds:\n credentials: %s", credentials)
-            if (self.neutron_available and
-                self.create_networks):
+            LOG.info("Acquired dynamic creds:\n"
+                     " credentials: %s", credentials)
+            if (self.neutron_available and self.create_networks):
                 network, subnet, router = self._create_network_resources(
                     credentials.tenant_id)
                 credentials.set_resources(network=network, subnet=subnet,
                                           router=router)
-                LOG.info("Created isolated network resources for : \n"
-                         + " credentials: %s", credentials)
+                LOG.info("Created isolated network resources for:\n"
+                         " credentials: %s", credentials)
         return credentials
 
     def get_primary_creds(self):
diff --git a/tempest/lib/services/identity/v3/policies_client.py b/tempest/lib/services/identity/v3/policies_client.py
index d4560e2..ca8dbbd 100644
--- a/tempest/lib/services/identity/v3/policies_client.py
+++ b/tempest/lib/services/identity/v3/policies_client.py
@@ -73,3 +73,115 @@
         resp, body = self.delete(url)
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def update_policy_association_for_endpoint(self, policy_id, endpoint_id):
+        """Create policy association with endpoint.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#associate-policy-and-endpoint
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/endpoints/{1}"\
+              .format(policy_id, endpoint_id)
+        resp, body = self.put(url, '{}')
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_policy_association_for_endpoint(self, policy_id, endpoint_id):
+        """Get policy association of endpoint.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#verify-a-policy-and-endpoint-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/endpoints/{1}"\
+              .format(policy_id, endpoint_id)
+        resp, body = self.get(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_policy_association_for_endpoint(self, policy_id, endpoint_id):
+        """Delete policy association with endpoint.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#delete-a-policy-and-endpoint-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/endpoints/{1}"\
+              .format(policy_id, endpoint_id)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_policy_association_for_service(self, policy_id, service_id):
+        """Create policy association with service.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#associate-policy-and-service-type-endpoint
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}"\
+              .format(policy_id, service_id)
+        resp, body = self.put(url, '{}')
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_policy_association_for_service(self, policy_id, service_id):
+        """Get policy association of service.
+
+        API Reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#verify-a-policy-and-service-type-endpoint-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}"\
+              .format(policy_id, service_id)
+        resp, body = self.get(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_policy_association_for_service(self, policy_id, service_id):
+        """Delete policy association with service.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#delete-a-policy-and-service-type-endpoint-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}"\
+              .format(policy_id, service_id)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_policy_association_for_region_and_service(
+            self, policy_id, service_id, region_id):
+        """Create policy association with service and region.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#associate-policy-and-service-type-endpoint-in-a-region
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}/regions/{2}"\
+              .format(policy_id, service_id, region_id)
+        resp, body = self.put(url, '{}')
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_policy_association_for_region_and_service(
+        self, policy_id, service_id, region_id):
+        """Get policy association of service and region.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#verify-a-policy-and-service-type-endpoint-in-a-region-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}/regions/{2}"\
+              .format(policy_id, service_id, region_id)
+        resp, body = self.get(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_policy_association_for_region_and_service(
+        self, policy_id, service_id, region_id):
+        """Delete policy association with service and region.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#delete-a-policy-and-service-type-endpoint-in-a-region-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}/regions/{2}"\
+              .format(policy_id, service_id, region_id)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index bcdae44..ed6df47 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -161,7 +161,7 @@
         """
         url = 'images/%s/file' % image_id
         resp, body = self.get(url)
-        self.expected_success(200, resp.status)
+        self.expected_success([200, 204, 206], resp.status)
         return rest_client.ResponseBodyData(resp, body)
 
     def add_image_tag(self, image_id, tag):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index ef277fb..cf53b67 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -141,13 +141,18 @@
         vnic_type = CONF.network.port_vnic_type
         profile = CONF.network.port_profile
 
-        # If vnic_type is configured create port for
+        # If vnic_type or profile are configured create port for
         # every network
-        if vnic_type:
+        if vnic_type or profile:
             ports = []
+            create_port_body = {}
 
-            create_port_body = {'binding:vnic_type': vnic_type,
-                                'binding:profile': profile}
+            if vnic_type:
+                create_port_body['binding:vnic_type'] = vnic_type
+
+            if profile:
+                create_port_body['binding:profile'] = profile
+
             if kwargs:
                 # Convert security group names to security group ids
                 # to pass to create_port
@@ -254,6 +259,8 @@
         self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
         waiters.wait_for_volume_resource_status(self.snapshots_client,
                                                 snapshot['id'], 'available')
+        snapshot = self.snapshots_client.show_snapshot(
+            snapshot['id'])['snapshot']
         return snapshot
 
     def create_volume_type(self, client=None, name=None, backend_name=None):
@@ -655,8 +662,8 @@
                 addresses = (server['addresses'][network['name']]
                              if network else [])
             for address in addresses:
-                if (address['version'] == CONF.validation.ip_version_for_ssh
-                        and address['OS-EXT-IPS:type'] == 'fixed'):
+                if (address['version'] == CONF.validation.ip_version_for_ssh and  # noqa
+                        address['OS-EXT-IPS:type'] == 'fixed'):
                     return address['addr']
             raise exceptions.ServerUnreachable(server_id=server['id'])
         else:
@@ -785,8 +792,8 @@
         port_map = [(p["id"], fxip["ip_address"])
                     for p in ports
                     for fxip in p["fixed_ips"]
-                    if netutils.is_valid_ipv4(fxip["ip_address"])
-                    and p['status'] in p_status]
+                    if (netutils.is_valid_ipv4(fxip["ip_address"]) and
+                        p['status'] in p_status)]
         inactive = [p for p in ports if p['status'] != 'ACTIVE']
         if inactive:
             LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index 9ff6227..b515639 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -37,7 +37,7 @@
         super(TestAggregatesBasicOps, cls).setup_clients()
         # Use admin client by default
         cls.aggregates_client = cls.os_admin.aggregates_client
-        cls.hosts_client = cls.os_admin.hosts_client
+        cls.services_client = cls.os_admin.services_client
 
     def _create_aggregate(self, **kwargs):
         aggregate = (self.aggregates_client.create_aggregate(**kwargs)
@@ -51,10 +51,10 @@
         return aggregate
 
     def _get_host_name(self):
-        hosts = self.hosts_client.list_hosts()['hosts']
-        self.assertNotEmpty(hosts)
-        computes = [x for x in hosts if x['service'] == 'compute']
-        return computes[0]['host_name']
+        svc_list = self.services_client.list_services(
+            binary='nova-compute')['services']
+        self.assertNotEmpty(svc_list)
+        return svc_list[0]['host']
 
     def _add_host(self, aggregate_id, host):
         aggregate = (self.aggregates_client.add_host(aggregate_id, host=host)
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index e4ab11c..87ce951 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -43,8 +43,8 @@
     @classmethod
     def skip_checks(cls):
         super(TestNetworkAdvancedServerOps, cls).skip_checks()
-        if not (CONF.network.project_networks_reachable
-                or CONF.network.public_network_id):
+        if not (CONF.network.project_networks_reachable or
+                CONF.network.public_network_id):
             msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index ff8837f..bcd4ddb 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -81,8 +81,8 @@
     @classmethod
     def skip_checks(cls):
         super(TestNetworkBasicOps, cls).skip_checks()
-        if not (CONF.network.project_networks_reachable
-                or CONF.network.public_network_id):
+        if not (CONF.network.project_networks_reachable or
+                CONF.network.public_network_id):
             msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
@@ -304,16 +304,19 @@
 
         - ping internal gateway and DHCP port, implying in-tenant connectivity
         pinging both, because L3 and DHCP agents might be on different nodes
+        - ping internal compute port, implying connectivity to other VMs on
+        this network
         """
         floating_ip, server = self.floating_ip_tuple
         # get internal ports' ips:
-        # get all network ports in the new network
+        # get all network and compute ports in the new network
         internal_ips = (
             p['fixed_ips'][0]['ip_address'] for p in
             self.os_admin.ports_client.list_ports(
                 tenant_id=server['tenant_id'],
                 network_id=network['id'])['ports']
-            if p['device_owner'].startswith('network')
+            if p['device_owner'].startswith('network') or
+            p['device_owner'].startswith('compute')
         )
 
         self._check_server_connectivity(floating_ip,
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 9f4e62b..e4e39c3 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -38,11 +38,11 @@
     @classmethod
     def skip_checks(cls):
         super(TestGettingAddress, cls).skip_checks()
-        if not (CONF.network_feature_enabled.ipv6
-                and CONF.network_feature_enabled.ipv6_subnet_attributes):
+        if not (CONF.network_feature_enabled.ipv6 and
+                CONF.network_feature_enabled.ipv6_subnet_attributes):
             raise cls.skipException('IPv6 or its attributes not supported')
-        if not (CONF.network.project_networks_reachable
-                or CONF.network.public_network_id):
+        if not (CONF.network.project_networks_reachable or
+                CONF.network.public_network_id):
             msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index beb039c..1fc57e7 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -30,12 +30,6 @@
     # breathing room to get through deletes in the time allotted.
     TIMEOUT_SCALING_FACTOR = 2
 
-    @classmethod
-    def skip_checks(cls):
-        super(TestVolumeBootPattern, cls).skip_checks()
-        if not CONF.volume_feature_enabled.snapshot:
-            raise cls.skipException("Cinder volume snapshots are disabled")
-
     def _create_volume_from_image(self):
         img_uuid = CONF.compute.image_ref
         vol_name = data_utils.rand_name(
@@ -76,6 +70,8 @@
     @decorators.idempotent_id('557cd2c2-4eb8-4dce-98be-f86765ff311b')
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          'Cinder volume snapshots are disabled')
     @utils.services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
 
@@ -156,6 +152,8 @@
 
     @decorators.idempotent_id('05795fb2-b2a7-4c9f-8fac-ff25aedb1489')
     @decorators.attr(type='slow')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          'Cinder volume snapshots are disabled')
     @utils.services('compute', 'image', 'volume')
     def test_create_server_from_volume_snapshot(self):
         # Create a volume from an image
@@ -192,37 +190,60 @@
                          created_volume_info['attachments'][0]['volume_id'])
 
     @decorators.idempotent_id('36c34c67-7b54-4b59-b188-02a2f458a63b')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          'Cinder volume snapshots are disabled')
     @utils.services('compute', 'volume', 'image')
-    def test_create_ebs_image_and_check_boot(self):
-        # create an instance from volume
+    def test_image_defined_boot_from_volume(self):
+        # create an instance from image-backed volume
         volume_origin = self._create_volume_from_image()
         instance = self._boot_instance_from_resource(
             source_id=volume_origin['id'],
             source_type='volume',
             delete_on_termination=True)
-        # create EBS image
+        # Create a snapshot image from the volume-backed server.
+        # The compute service will have the block service create a snapshot of
+        # the root volume and store its metadata in the image.
         image = self.create_server_snapshot(instance)
 
-        # delete instance
+        # Delete the first server which will also delete the first image-backed
+        # volume.
         self._delete_server(instance)
 
-        # boot instance from EBS image
+        # Create a server from the image snapshot which has an
+        # "image-defined block device mapping (BDM)" in it, i.e. the metadata
+        # about the volume snapshot. The compute service will use this to
+        # create a volume from the volume snapshot and use that as the root
+        # disk for the server.
         instance = self.create_server(image_id=image['id'])
 
-        # Verify the server was created from the image
-        created_volume = instance['os-extended-volumes:volumes_attached']
-        self.assertNotEmpty(created_volume, "No volume attachment found.")
-        created_volume_info = self.volumes_client.show_volume(
-            created_volume[0]['id'])['volume']
+        # Verify the server was created from the image-defined BDM.
+        volume_attachments = instance['os-extended-volumes:volumes_attached']
+        self.assertEqual(1, len(volume_attachments),
+                         "No volume attachment found.")
+        created_volume = self.volumes_client.show_volume(
+            volume_attachments[0]['id'])['volume']
+        # Assert that the volume service also shows the server attachment.
+        self.assertEqual(1, len(created_volume['attachments']),
+                         "No server attachment found for volume: %s" %
+                         created_volume)
         self.assertEqual(instance['id'],
-                         created_volume_info['attachments'][0]['server_id'])
-        self.assertEqual(created_volume[0]['id'],
-                         created_volume_info['attachments'][0]['volume_id'])
+                         created_volume['attachments'][0]['server_id'])
+        self.assertEqual(volume_attachments[0]['id'],
+                         created_volume['attachments'][0]['volume_id'])
         self.assertEqual(
             volume_origin['volume_image_metadata']['image_id'],
-            created_volume_info['volume_image_metadata']['image_id'])
+            created_volume['volume_image_metadata']['image_id'])
 
-        # delete instance
+        # Delete the second server which should also delete the second volume
+        # created from the volume snapshot.
+        # TODO(mriedem): Currently, the compute service fails to delete the
+        # volume it created because the volume still has the snapshot
+        # associated with it, and the cleanups for the volume snapshot which
+        # were added in create_server_snapshot above, don't run until after
+        # this is called. So we need to delete the volume snapshot and wait for
+        # it to be gone here first before deleting the server, and then we can
+        # also assert that the underlying volume is deleted when the server is
+        # deleted.
         self._delete_server(instance)
 
     @decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
diff --git a/tempest/test.py b/tempest/test.py
index 27e0165..f2babbb 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -68,9 +68,9 @@
 def validate_tearDownClass():
     if at_exit_set:
         LOG.error(
-            "tearDownClass does not call the super's "
-            "tearDownClass in these classes: \n"
-            + str(at_exit_set))
+            "tearDownClass does not call the super's tearDownClass in "
+            "these classes:\n"
+            "  %s", at_exit_set)
 
 
 atexit.register(validate_tearDownClass)
@@ -582,8 +582,8 @@
         super(BaseTestCase, self).setUp()
         if not self.__setupclass_called:
             raise RuntimeError("setUpClass does not calls the super's"
-                               "setUpClass in the "
-                               + self.__class__.__name__)
+                               "setUpClass in the " +
+                               self.__class__.__name__)
         at_exit_set.add(self.__class__)
         test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
         try:
@@ -602,7 +602,7 @@
             stderr = self.useFixture(fixtures.StringStream('stderr')).stream
             self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
         if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
-            os.environ.get('OS_LOG_CAPTURE') != '0'):
+                os.environ.get('OS_LOG_CAPTURE') != '0'):
             self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
                                                    format=self.log_format,
                                                    level=None))
diff --git a/tempest/tests/cmd/test_subunit_describe_calls.py b/tempest/tests/cmd/test_subunit_describe_calls.py
index 5f3d770..cb34ba6 100644
--- a/tempest/tests/cmd/test_subunit_describe_calls.py
+++ b/tempest/tests/cmd/test_subunit_describe_calls.py
@@ -33,15 +33,42 @@
         p.communicate()
         self.assertEqual(0, p.returncode)
 
+    def test_verbose(self):
+        subunit_file = os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            'sample_streams/calls.subunit')
+        p = subprocess.Popen([
+            'subunit-describe-calls', '-s', subunit_file,
+            '-v'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+        stdout = p.communicate()
+        self.assertEqual(0, p.returncode)
+        self.assertIn(b'- request headers:', stdout[0])
+        self.assertIn(b'- request body:', stdout[0])
+        self.assertIn(b'- response headers:', stdout[0])
+        self.assertIn(b'- response body:', stdout[0])
+
     def test_return_code_no_output(self):
         subunit_file = os.path.join(
             os.path.dirname(os.path.abspath(__file__)),
             'sample_streams/calls.subunit')
         p = subprocess.Popen([
             'subunit-describe-calls', '-s', subunit_file],
-            stdin=subprocess.PIPE)
-        p.communicate()
+            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+        stdout = p.communicate()
         self.assertEqual(0, p.returncode)
+        self.assertIn(b'foo', stdout[0])
+        self.assertIn(b'- 200 POST request for Nova to v2.1/<id>/',
+                      stdout[0])
+        self.assertIn(b'- 200 DELETE request for Nova to v2.1/<id>/',
+                      stdout[0])
+        self.assertIn(b'- 200 GET request for Nova to v2.1/<id>/',
+                      stdout[0])
+        self.assertIn(b'- 404 DELETE request for Nova to v2.1/<id>/',
+                      stdout[0])
+        self.assertNotIn(b'- request headers:', stdout[0])
+        self.assertNotIn(b'- request body:', stdout[0])
+        self.assertNotIn(b'- response headers:', stdout[0])
+        self.assertNotIn(b'- response body:', stdout[0])
 
     def test_parse(self):
         subunit_file = os.path.join(
diff --git a/tempest/tests/lib/services/identity/v3/test_policies_client.py b/tempest/tests/lib/services/identity/v3/test_policies_client.py
index 66c3d65..0237475 100644
--- a/tempest/tests/lib/services/identity/v3/test_policies_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_policies_client.py
@@ -81,6 +81,10 @@
                 }
             ]
     }
+    FAKE_ENDPOINT_ID = "234789"
+    FAKE_SERVICE_ID = "556782"
+    FAKE_POLICY_ID = "717273"
+    FAKE_REGION_ID = "73"
 
     def setUp(self):
         super(TestPoliciesClient, self).setUp()
@@ -150,3 +154,87 @@
             {},
             policy_id="717273",
             status=204)
+
+    def test_update_policy_association_for_endpoint(self):
+        self.check_service_client_function(
+            self.client.update_policy_association_for_endpoint,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            endpoint_id=self.FAKE_ENDPOINT_ID,
+            status=204)
+
+    def test_show_policy_association_for_endpoint(self):
+        self.check_service_client_function(
+            self.client.show_policy_association_for_endpoint,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            endpoint_id=self.FAKE_ENDPOINT_ID,
+            status=204)
+
+    def test_delete_policy_association_for_endpoint(self):
+        self.check_service_client_function(
+            self.client.delete_policy_association_for_endpoint,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            endpoint_id=self.FAKE_ENDPOINT_ID,
+            status=204)
+
+    def test_update_policy_association_for_service(self):
+        self.check_service_client_function(
+            self.client.update_policy_association_for_service,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            status=204)
+
+    def test_show_policy_association_for_service(self):
+        self.check_service_client_function(
+            self.client.show_policy_association_for_service,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            status=204)
+
+    def test_delete_policy_association_for_service(self):
+        self.check_service_client_function(
+            self.client.delete_policy_association_for_service,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            status=204)
+
+    def test_update_policy_association_for_region_and_service(self):
+        self.check_service_client_function(
+            self.client.update_policy_association_for_region_and_service,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            region_id=self.FAKE_REGION_ID,
+            status=204)
+
+    def test_show_policy_association_for_region_and_service(self):
+        self.check_service_client_function(
+            self.client.show_policy_association_for_region_and_service,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            region_id=self.FAKE_REGION_ID,
+            status=204)
+
+    def test_delete_policy_association_for_region_and_service(self):
+        self.check_service_client_function(
+            self.client.delete_policy_association_for_region_and_service,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            region_id=self.FAKE_REGION_ID,
+            status=204)
diff --git a/tox.ini b/tox.ini
index 5644641..da0233a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -83,6 +83,16 @@
     tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.api)' {posargs}
     tempest run --combine --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.scenario)' {posargs}
 
+[testenv:full-parallel]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+# The regex below is used to select all tempest scenario and including the non slow api tests
+commands =
+    find . -type f -name "*.pyc" -delete
+    tempest run --regex '(^tempest\.scenario.*)|(?!.*\[.*\bslow\b.*\])(^tempest\.api)' {posargs}
+
 [testenv:full-serial]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
@@ -146,7 +156,7 @@
   -r{toxinidir}/doc/requirements.txt
 commands =
   rm -rf doc/build
-  sphinx-build -b html doc/source doc/build/html
+  sphinx-build -W -b html doc/source doc/build/html
 whitelist_externals = rm
 
 [testenv:pep8]