Merge "Do not select inactive l3 agent in L3AgentSchedulerTestJSON"
diff --git a/.zuul.yaml b/.zuul.yaml
index 10788d7..b51babb 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -17,14 +17,17 @@
     name: tempest-full
     parent: devstack-tempest
     # This currently only works on the master branch.
-    branches: master
+    # NOTE(andreaf) Only run on master for now.
+    # The negative lookup is redudant but it's a
+    # reminder that we don't want the job running there.
+    branches: ^(?!driverfixes/)master$
     description: |
       Base integration test with Neutron networking and py27.
       Former names for this job where:
         * legacy-tempest-dsvm-neutron-full
         * gate-tempest-dsvm-neutron-full-ubuntu-xenial
     vars:
-      tox_venvlist: full
+      tox_envlist: full
       devstack_localrc:
         ENABLE_FILE_INJECTION: True
 
@@ -113,15 +116,4 @@
               - ^playbooks/
               - ^roles/
               - ^.zuul.yaml$
-        - tempest-full:
-            voting: false
-            irrelevant-files:
-              - ^(test-|)requirements.txt$
-              - ^.*\.rst$
-              - ^doc/.*$
-              - ^etc/.*$
-              - ^releasenotes/.*$
-              - ^setup.cfg$
-              - ^tempest/hacking/.*$
-              - ^tempest/tests/.*$
         - tempest-tox-plugin-sanity-check
diff --git a/doc/requirements.txt b/doc/requirements.txt
new file mode 100644
index 0000000..b666029
--- /dev/null
+++ b/doc/requirements.txt
@@ -0,0 +1,6 @@
+# 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.17.0 # Apache-2.0
+reno>=2.5.0 # Apache-2.0
+sphinx>=1.6.2 # BSD
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index aca1845..9c4ac0b 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -302,6 +302,10 @@
 
   .. _2.2: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id2
 
+  * `2.6`_
+
+  .. _2.6: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id5
+
   * `2.10`_
 
   .. _2.10: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id9
@@ -310,6 +314,10 @@
 
   .. _2.20: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id18
 
+  * `2.21`_
+
+  .. _2.21: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id19
+
   * `2.25`_
 
   .. _2.25: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-mitaka
diff --git a/playbooks/post-tempest.yaml b/playbooks/post-tempest.yaml
index 820e4f6..70dac09 100644
--- a/playbooks/post-tempest.yaml
+++ b/playbooks/post-tempest.yaml
@@ -2,7 +2,6 @@
   become: true
   vars:
     logs_root: "{{ devstack_base_dir|default('/opt/stack') }}"
-    stage_dir: "{{ devstack_base_dir|default('/opt/stack') }}"
     test_results_stage_name: 'test_results'
   roles:
     - role: process-test-results
diff --git a/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml b/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml
new file mode 100644
index 0000000..e3443c8
--- /dev/null
+++ b/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml
@@ -0,0 +1,9 @@
+---
+other:
+  - |
+    The CLIClient class, when it calls a command line client, uses
+    --os-project-name instead of --os-tenant-name for the project, and
+    passes --os-identity-api-version (default empty).
+    All CLI clients still available in supported releases of OpenStack
+    which are wrapped by the cmd_with_auth() method support those
+    switches.
diff --git a/releasenotes/notes/removal-deprecated-config-options-3db535b979fe3509.yaml b/releasenotes/notes/removal-deprecated-config-options-3db535b979fe3509.yaml
index dbb6c46..2b3bb3a 100644
--- a/releasenotes/notes/removal-deprecated-config-options-3db535b979fe3509.yaml
+++ b/releasenotes/notes/removal-deprecated-config-options-3db535b979fe3509.yaml
@@ -6,3 +6,4 @@
     good to handle them.
 
     * ``[identity-feature-enabled].forbid_global_implied_dsr``
+    * ``[image-feature-enabled].deactivate_image``
diff --git a/requirements.txt b/requirements.txt
index cd74449..c02cd05 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,9 +8,9 @@
 paramiko>=2.0.0 # LGPLv2.1+
 netaddr>=0.7.18 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
-oslo.concurrency>=3.20.0 # Apache-2.0
+oslo.concurrency>=3.25.0 # Apache-2.0
 oslo.config>=5.1.0 # Apache-2.0
-oslo.log>=3.30.0 # Apache-2.0
+oslo.log>=3.36.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
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index b5defb7..33dcce9 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -29,7 +29,7 @@
                # Line with only a comment.
                (tempest\.(api|scenario|thirdparty)).*$    # Run only api scenario and third party
 
-.. zuul:rolevar:: tox_venvlist
+.. zuul:rolevar:: tox_envlist
    :default: smoke
 
    The Tempest tox environment to run.
diff --git a/roles/run-tempest/defaults/main.yaml b/roles/run-tempest/defaults/main.yaml
index 3e57511..85e94f2 100644
--- a/roles/run-tempest/defaults/main.yaml
+++ b/roles/run-tempest/defaults/main.yaml
@@ -1,3 +1,3 @@
 devstack_base_dir: /opt/stack
 tempest_test_regex: ''
-tox_venvlist: smoke
+tox_envlist: smoke
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 297cd72..87898db 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -21,7 +21,7 @@
   when: num_cores|int > 3
 
 - name: Run Tempest
-  command: tox -e {{tox_venvlist}} -- {{tempest_test_regex|quote}} --concurrency={{tempest_concurrency|default(default_concurrency)}}
+  command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} --concurrency={{tempest_concurrency|default(default_concurrency)}}
   args:
     chdir: "{{devstack_base_dir}}/tempest"
   become: true
diff --git a/tempest/api/compute/admin/test_auto_allocate_network.py b/tempest/api/compute/admin/test_auto_allocate_network.py
index a9772c4..c4d5768 100644
--- a/tempest/api/compute/admin/test_auto_allocate_network.py
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -84,8 +84,7 @@
         nets = cls.networks_client.list_networks(
             **search_opts).get('networks', [])
         if nets:
-            raise lib_excs.TempestException(
-                'Found shared networks: %s' % nets)
+            raise cls.skipException('Found shared networks: %s' % nets)
 
     @classmethod
     def resource_cleanup(cls):
diff --git a/tempest/api/compute/admin/test_create_server.py b/tempest/api/compute/admin/test_create_server.py
index 08b2d19..711b441 100644
--- a/tempest/api/compute/admin/test_create_server.py
+++ b/tempest/api/compute/admin/test_create_server.py
@@ -56,6 +56,18 @@
             # Create a flavor with ephemeral disk
             flavor = self.create_flavor(name=flavor_name, ram=ram, vcpus=vcpus,
                                         disk=disk, ephemeral=ephem_disk)
+
+            # Set extra specs same as self.flavor_ref for the created flavor,
+            # because the environment may need some special extra specs to
+            # create server which should have been contained in
+            # self.flavor_ref.
+            extra_spec_keys = \
+                self.admin_flavors_client.list_flavor_extra_specs(
+                    self.flavor_ref)['extra_specs']
+            if extra_spec_keys:
+                self.admin_flavors_client.set_flavor_extra_spec(
+                    flavor['id'], **extra_spec_keys)
+
             return flavor['id']
 
         flavor_with_eph_disk_id = create_flavor_with_ephemeral(ephem_disk=1)
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index a626ebb..a6b71b2 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -77,6 +77,16 @@
         )['flavor']
         self.addCleanup(self._flavor_clean_up, flavor['id'])
 
+        # Set extra specs same as self.flavor_ref for the created flavor,
+        # because the environment may need some special extra specs to
+        # create server which should have been contained in
+        # self.flavor_ref.
+        extra_spec_keys = self.admin_flavors_client.list_flavor_extra_specs(
+            self.flavor_ref)['extra_specs']
+        if extra_spec_keys:
+            self.admin_flavors_client.set_flavor_extra_spec(
+                flavor['id'], **extra_spec_keys)
+
         # Now boot a server with the copied flavor.
         server = self.create_test_server(
             wait_until='ACTIVE', flavor=flavor['id'])
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index ac03cdc..a5d5c57 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -99,6 +99,15 @@
         cls.versions_client = cls.os_primary.compute_versions_client
         if CONF.service_available.cinder:
             cls.volumes_client = cls.os_primary.volumes_client_latest
+        if CONF.service_available.glance:
+            if CONF.image_feature_enabled.api_v1:
+                cls.images_client = cls.os_primary.image_client
+            elif CONF.image_feature_enabled.api_v2:
+                cls.images_client = cls.os_primary.image_client_v2
+            else:
+                raise lib_exc.InvalidConfiguration(
+                    'Either api_v1 or api_v2 must be True in '
+                    '[image-feature-enabled].')
 
     @classmethod
     def resource_setup(cls):
@@ -254,7 +263,11 @@
 
     @classmethod
     def create_image_from_server(cls, server_id, **kwargs):
-        """Wrapper utility that returns an image created from the server."""
+        """Wrapper utility that returns an image created from the server.
+
+        If compute microversion >= 2.36, the returned image response will
+        be from the image service API rather than the compute image proxy API.
+        """
         name = kwargs.pop('name',
                           data_utils.rand_name(cls.__name__ + "-image"))
         wait_until = kwargs.pop('wait_until', None)
@@ -267,14 +280,21 @@
             image_id = image['image_id']
         else:
             image_id = data_utils.parse_image_id(image.response['location'])
+
+        # The compute image proxy APIs were deprecated in 2.35 so
+        # use the images client directly if the API microversion being
+        # used is >=2.36.
+        if api_version_utils.compare_version_header_to_response(
+                "OpenStack-API-Version", "compute 2.36", image.response, "lt"):
+            client = cls.images_client
+        else:
+            client = cls.compute_images_client
         cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
-                                    cls.compute_images_client.delete_image,
-                                    image_id)
+                                    client.delete_image, image_id)
 
         if wait_until is not None:
             try:
-                waiters.wait_for_image_status(cls.compute_images_client,
-                                              image_id, wait_until)
+                waiters.wait_for_image_status(client, image_id, wait_until)
             except lib_exc.NotFound:
                 if wait_until.upper() == 'ACTIVE':
                     # If the image is not found after create_image returned
@@ -292,7 +312,11 @@
                             image_id=image_id)
                 else:
                     raise
-            image = cls.compute_images_client.show_image(image_id)['image']
+            image = client.show_image(image_id)
+            # Compute image client returns response wrapped in 'image' element
+            # which is not the case with Glance image client.
+            if 'image' in image:
+                image = image['image']
 
             if wait_until.upper() == 'ACTIVE':
                 if wait_for_server:
@@ -352,6 +376,11 @@
                                        'VERIFY_RESIZE')
         cls.servers_client.confirm_resize_server(server_id)
         waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE')
+        server = cls.servers_client.show_server(server_id)['server']
+        if new_flavor_id != server['flavor']['id']:
+            msg = ('Flavor id of %s is not equal to new_flavor_id.'
+                   % server_id)
+            raise lib_exc.TempestException(msg)
 
     @classmethod
     def delete_volume(cls, volume_id):
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index efd4f0e..3a474e6 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -30,18 +30,6 @@
 
 class FlavorsV2NegativeTest(base.BaseV2ComputeTest):
 
-    @classmethod
-    def setup_clients(cls):
-        super(FlavorsV2NegativeTest, cls).setup_clients()
-        if CONF.image_feature_enabled.api_v1:
-            cls.images_client = cls.os_primary.image_client
-        elif CONF.image_feature_enabled.api_v2:
-            cls.images_client = cls.os_primary.image_client_v2
-        else:
-            raise lib_exc.InvalidConfiguration(
-                'Either api_v1 or api_v2 must be True in '
-                '[image-feature-enabled].')
-
     @decorators.attr(type=['negative'])
     @utils.services('image')
     @decorators.idempotent_id('90f0d93a-91c1-450c-91e6-07d18172cefe')
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index a126fd6..d857fcb 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -139,6 +139,7 @@
 
         server = self.create_test_server(
             validatable=True,
+            wait_until='ACTIVE',
             validation_resources=validation_resources,
             config_drive=config_drive_enabled,
             adminPass=admin_pass,
@@ -205,6 +206,7 @@
 
         self.addCleanup(self.delete_server, server['id'])
 
+        server = self.servers_client.show_server(server['id'])['server']
         self.ssh_client = remote_client.RemoteClient(
             self.get_server_ip(server, validation_resources),
             CONF.validation.image_ssh_user,
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index 6c9b287..393e68f 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -119,8 +119,12 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('74745ad8-b346-45b5-b9b8-509d7447fc1f')
     def test_list_servers_by_changes_since_future_date(self):
-        # Return an empty list when a date in the future is passed
-        changes_since = {'changes-since': '2051-01-01T12:34:00Z'}
+        # Return an empty list when a date in the future is passed.
+        # updated_at field may haven't been set at the point in the boot
+        # process where build_request still exists, so add
+        # {'status': 'ACTIVE'} along with changes-since as filter.
+        changes_since = {'changes-since': '2051-01-01T12:34:00Z',
+                         'status': 'ACTIVE'}
         body = self.client.list_servers(**changes_since)
         self.assertEmpty(body['servers'])
 
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 297e8a8..e6184b7 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -23,12 +23,12 @@
 CONF = config.CONF
 
 
-class AttachVolumeTestJSON(base.BaseV2ComputeTest):
-    max_microversion = '2.19'
+class BaseAttachVolumeTest(base.BaseV2ComputeTest):
+    """Base class for the attach volume tests in this module."""
 
     @classmethod
     def skip_checks(cls):
-        super(AttachVolumeTestJSON, cls).skip_checks()
+        super(BaseAttachVolumeTest, cls).skip_checks()
         if not CONF.service_available.cinder:
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
@@ -36,11 +36,11 @@
     @classmethod
     def setup_credentials(cls):
         cls.prepare_instance_network()
-        super(AttachVolumeTestJSON, cls).setup_credentials()
+        super(BaseAttachVolumeTest, cls).setup_credentials()
 
     @classmethod
     def resource_setup(cls):
-        super(AttachVolumeTestJSON, cls).resource_setup()
+        super(BaseAttachVolumeTest, cls).resource_setup()
         cls.device = CONF.compute.volume_device_name
 
     def _create_server(self):
@@ -58,6 +58,9 @@
             server['id'])['addresses']
         return server, validation_resources
 
+
+class AttachVolumeTestJSON(BaseAttachVolumeTest):
+
     @decorators.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
     def test_attach_detach_volume(self):
         # Stop and Start a server with an attached volume, ensuring that
@@ -149,7 +152,7 @@
                 self.volumes_client, attachment['volumeId'], 'available')
 
 
-class AttachVolumeShelveTestJSON(AttachVolumeTestJSON):
+class AttachVolumeShelveTestJSON(BaseAttachVolumeTest):
     """Testing volume with shelved instance.
 
     This test checks the attaching and detaching volumes from
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index c846f88..ce5bd3e 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -18,8 +18,6 @@
 
 import six
 
-import testtools
-
 from oslo_log import log as logging
 from tempest.api.image import base
 from tempest import config
@@ -128,8 +126,6 @@
         self.assertEqual(image['id'], body['id'])
         self.assertEqual(new_image_name, body['name'])
 
-    @testtools.skipUnless(CONF.image_feature_enabled.deactivate_image,
-                          'deactivate-image is not available.')
     @decorators.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
     def test_deactivate_reactivate_image(self):
         # Create image
diff --git a/tempest/config.py b/tempest/config.py
index fc95df8..4544871 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -541,13 +541,6 @@
                                   'are current one. In future, Tempest will '
                                   'test v2 APIs only so this config option '
                                   'will be removed.'),
-    cfg.BoolOpt('deactivate_image',
-                default=False,
-                help="Is the deactivate-image feature enabled."
-                     " The feature has been integrated since Kilo.",
-                deprecated_for_removal=True,
-                deprecated_reason="All supported versions of OpenStack now "
-                                  "support the 'deactivate_image' feature"),
 ]
 
 network_group = cfg.OptGroup(name='network',
diff --git a/tempest/lib/api_schema/response/compute/v2_45/__init__.py b/tempest/lib/api_schema/response/compute/v2_45/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_45/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_45/images.py b/tempest/lib/api_schema/response/compute/v2_45/images.py
new file mode 100644
index 0000000..8a48f36
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_45/images.py
@@ -0,0 +1,32 @@
+#    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.
+
+# The 2.45 microversion removes the "location" header and adds "image_id"
+# to the response body.
+create_image = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'image_id': {'type': 'string'}
+        },
+        'additionalProperties': False,
+        'required': ['image_id']
+    }
+}
+
+# NOTE(mriedem): The compute proxy APIs for showing/listing and deleting
+# images were deprecated in microversion 2.35, and the compute proxy APIs for
+# working with image metadata were deprecated in microversion 2.39. Therefore,
+# client-side code shouldn't rely on those APIs in the compute images client
+# past those microversions and should instead use the Glance images client
+# directly.
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
index f39ecbc..3a97801 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -101,12 +101,15 @@
     :type project_domain_name: string
     :param project_domain_id: Project's domain ID
     :type project_domain_id: string
+    :param identity_api_version: Version of the Identity API
+    :type identity_api_version: string
     """
 
     def __init__(self, username='', password='', tenant_name='', uri='',
                  cli_dir='', insecure=False, prefix='', user_domain_name=None,
                  user_domain_id=None, project_domain_name=None,
-                 project_domain_id=None, *args, **kwargs):
+                 project_domain_id=None, identity_api_version=None, *args,
+                 **kwargs):
         """Initialize a new CLIClient object."""
         super(CLIClient, self).__init__()
         self.cli_dir = cli_dir if cli_dir else '/usr/bin'
@@ -120,6 +123,7 @@
         self.user_domain_id = user_domain_id
         self.project_domain_name = project_domain_name
         self.project_domain_id = project_domain_id
+        self.identity_api_version = identity_api_version
 
     def nova(self, action, flags='', params='', fail_ok=False,
              endpoint_type='publicURL', merge_stderr=False):
@@ -374,12 +378,15 @@
         :param merge_stderr:  if True the stderr buffer is merged into stdout
         :type merge_stderr: boolean
         """
-        creds = ('--os-username %s --os-tenant-name %s --os-password %s '
+        creds = ('--os-username %s --os-project-name %s --os-password %s '
                  '--os-auth-url %s' %
                  (self.username,
                   self.tenant_name,
                   self.password,
                   self.uri))
+        if self.identity_api_version:
+            creds += ' --os-identity-api-version %s' % (
+                self.identity_api_version)
         if self.user_domain_name is not None:
             creds += ' --os-user-domain-name %s' % self.user_domain_name
         if self.user_domain_id is not None:
diff --git a/tempest/lib/common/fixed_network.py b/tempest/lib/common/fixed_network.py
index e2054a4..875a79d 100644
--- a/tempest/lib/common/fixed_network.py
+++ b/tempest/lib/common/fixed_network.py
@@ -38,7 +38,12 @@
         raise exceptions.InvalidTestResource(type='network', name=name)
 
     networks = compute_networks_client.list_networks()['networks']
-    networks = [n for n in networks if n['label'] == name]
+    # NOTE(zhufl) compute networks_client uses 'label' as network name field,
+    # while neutron networks_client uses 'name' as network name field.
+    try:
+        networks = [n for n in networks if n['label'] == name]
+    except KeyError:
+        networks = [n for n in networks if n['name'] == name]
 
     # Check that a network exists, else raise an InvalidConfigurationException
     if len(networks) == 1:
diff --git a/tempest/lib/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
index 83db513..fcdeb17 100644
--- a/tempest/lib/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -344,11 +344,11 @@
         net_creds = cred_provider.TestResources(credential)
         net_clients = clients.ServiceClients(credentials=credential,
                                              identity_uri=self.identity_uri)
-        compute_network_client = net_clients.compute.NetworksClient()
+        networks_client = net_clients.network.NetworksClient()
         net_name = self.hash_dict['networks'].get(hash, None)
         try:
             network = fixed_network.get_network_from_name(
-                net_name, compute_network_client)
+                net_name, networks_client)
         except lib_exc.InvalidTestResource:
             network = {}
         net_creds.set_resources(network=network)
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
index 1676a28..94fab00 100644
--- a/tempest/lib/common/utils/linux/remote_client.py
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -10,6 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import functools
 import sys
 
 import netaddr
@@ -25,6 +26,7 @@
 
 def debug_ssh(function):
     """Decorator to generate extra debug info in case off SSH failure"""
+    @functools.wraps(function)
     def wrapper(self, *args, **kwargs):
         try:
             return function(self, *args, **kwargs)
diff --git a/tempest/lib/services/compute/images_client.py b/tempest/lib/services/compute/images_client.py
index 86bea9e..0f4eb42 100644
--- a/tempest/lib/services/compute/images_client.py
+++ b/tempest/lib/services/compute/images_client.py
@@ -17,6 +17,7 @@
 from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import images as schema
+from tempest.lib.api_schema.response.compute.v2_45 import images as schemav245
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.compute import base_compute_client
@@ -24,6 +25,10 @@
 
 class ImagesClient(base_compute_client.BaseComputeClient):
 
+    schema_versions_info = [
+        {'min': None, 'max': '2.44', 'schema': schema},
+        {'min': '2.45', 'max': None, 'schema': schemav245}]
+
     def create_image(self, server_id, **kwargs):
         """Create an image of the original server.
 
@@ -36,7 +41,10 @@
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body)
-        self.validate_response(schema.create_image, resp, body)
+        _schema = self.get_schema(self.schema_versions_info)
+        if body:
+            body = json.loads(body)
+        self.validate_response(_schema.create_image, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def list_images(self, detail=False, **params):
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 7c404ad..e4ab11c 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -195,6 +195,8 @@
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'VERIFY_RESIZE')
         self.servers_client.confirm_resize_server(server['id'])
+        server = self.servers_client.show_server(server['id'])['server']
+        self.assertEqual(resize_flavor, server['flavor']['id'])
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
 
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index 61bbe2b..ff7996a 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -81,6 +81,8 @@
         return source_body['name'], dest_body['name']
 
     def _volume_retype_with_migration(self, volume_id, new_volume_type):
+        # NOTE: The 'on-demand' migration requires admin operation, so
+        # admin_volumes_client() should be used here.
         migration_policy = 'on-demand'
         self.admin_volumes_client.retype_volume(
             volume_id, new_type=new_volume_type,
diff --git a/tempest/test.py b/tempest/test.py
index 9da85d5..27e0165 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -836,7 +836,7 @@
             manager = cls.get_client_manager()
 
         # Make sure cred_provider exists and get a network client
-        networks_client = manager.compute_networks_client
+        networks_client = manager.networks_client
         cred_provider = cls._get_credentials_provider()
         # In case of nova network, isolated tenants are not able to list the
         # network configured in fixed_network_name, even if they can use it
diff --git a/tempest/tests/lib/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
index c276386..c069af5 100644
--- a/tempest/tests/lib/cli/test_execute.py
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -125,3 +125,27 @@
                          mock_execute.call_args[0][2])
         self.assertNotIn('--os-project-domain-name',
                          mock_execute.call_args[0][2])
+
+    @mock.patch.object(cli_base, 'execute')
+    def test_execute_with_default_api_version(self, mock_execute):
+        cli = cli_base.CLIClient()
+        cli.openstack('action')
+        self.assertEqual(mock_execute.call_count, 1)
+        self.assertNotIn('--os-identity-api-version ',
+                         mock_execute.call_args[0][2])
+
+    @mock.patch.object(cli_base, 'execute')
+    def test_execute_with_empty_api_version(self, mock_execute):
+        cli = cli_base.CLIClient(identity_api_version='')
+        cli.openstack('action')
+        self.assertEqual(mock_execute.call_count, 1)
+        self.assertNotIn('--os-identity-api-version ',
+                         mock_execute.call_args[0][2])
+
+    @mock.patch.object(cli_base, 'execute')
+    def test_execute_with_explicit_api_version(self, mock_execute):
+        cli = cli_base.CLIClient(identity_api_version='0.0')
+        cli.openstack('action')
+        self.assertEqual(mock_execute.call_count, 1)
+        self.assertIn('--os-identity-api-version 0.0 ',
+                      mock_execute.call_args[0][2])
diff --git a/tempest/tests/lib/common/test_preprov_creds.py b/tempest/tests/lib/common/test_preprov_creds.py
index 9b10159..25df2a7 100644
--- a/tempest/tests/lib/common/test_preprov_creds.py
+++ b/tempest/tests/lib/common/test_preprov_creds.py
@@ -339,7 +339,7 @@
             return_value=test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
-        with mock.patch('tempest.lib.services.compute.networks_client.'
+        with mock.patch('tempest.lib.services.network.networks_client.'
                         'NetworksClient.list_networks',
                         return_value={'networks': [{'name': 'network-2',
                                                     'id': 'fake-id',
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index 011bc9b..2b5a947 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -41,7 +41,7 @@
     def test_get_tenant_network(self, mock_gtn, mock_gprov, mock_gcm):
         net_client = mock.Mock()
         mock_prov = mock.Mock()
-        mock_gcm.return_value.compute_networks_client = net_client
+        mock_gcm.return_value.networks_client = net_client
         mock_gprov.return_value = mock_prov
 
         test.BaseTestCase.get_tenant_network()
@@ -85,7 +85,7 @@
                                                mock_gcm):
         net_client = mock.Mock()
         mock_prov = mock.Mock()
-        mock_gcm.return_value.compute_networks_client = net_client
+        mock_gcm.return_value.networks_client = net_client
         mock_gprov.return_value = mock_prov
 
         test.BaseTestCase.get_tenant_network(credentials_type='alt')
@@ -102,7 +102,7 @@
                                                 mock_gcm):
         net_client = mock.Mock()
         mock_prov = mock.Mock()
-        mock_gcm.return_value.compute_networks_client = net_client
+        mock_gcm.return_value.networks_client = net_client
         mock_gprov.return_value = mock_prov
         creds = ['foo_type', 'role1']
 
diff --git a/test-requirements.txt b/test-requirements.txt
index 37644d0..e33f207 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,11 +2,7 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
-# needed for doc build
-sphinx>=1.6.2 # BSD
-openstackdocstheme>=1.17.0 # Apache-2.0
-reno>=2.5.0 # Apache-2.0
 mock>=2.0.0 # BSD
 coverage!=4.4,>=4.0 # Apache-2.0
-oslotest>=1.10.0 # Apache-2.0
+oslotest>=3.2.0 # Apache-2.0
 flake8-import-order==0.11 # LGPLv3
diff --git a/tox.ini b/tox.ini
index e7ea1e2..c0df8a6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -133,9 +133,14 @@
 commands = {posargs}
 
 [testenv:docs]
+deps =
+  -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+  -r{toxinidir}/requirements.txt
+  -r{toxinidir}/doc/requirements.txt
 commands =
-    rm -rf doc/build
-    python setup.py build_sphinx {posargs}
+  rm -rf doc/build
+  sphinx-build -b html doc/source doc/build/html
+whitelist_externals = rm
 
 [testenv:pep8]
 commands =
@@ -161,9 +166,15 @@
 import-order-style = pep8
 
 [testenv:releasenotes]
+deps =
+  -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+  -r{toxinidir}/requirements.txt
+  -r{toxinidir}/doc/requirements.txt
 commands =
-    rm -rf releasenotes/build
-    sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
+  rm -rf releasenotes/build
+  sphinx-build -a -E -W -d releasenotes/build/doctrees \
+         -b html releasenotes/source releasenotes/build/html
+whitelist_externals = rm
 
 [testenv:pip-check-reqs]
 # Do not install test-requirements as that will pollute the virtualenv for