Merge "Create default network for more compute tests"
diff --git a/.zuul.yaml b/.zuul.yaml
index 403c93d..0035f7c 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -458,18 +458,6 @@
     override-checkout: stable/rocky
 
 - job:
-    name: tempest-full-queens
-    parent: tempest-full
-    nodeset: openstack-single-node-xenial
-    override-checkout: stable/queens
-
-- job:
-    name: tempest-full-queens-py3
-    parent: tempest-full-py3
-    nodeset: openstack-single-node-xenial
-    override-checkout: stable/queens
-
-- job:
     name: tempest-tox-plugin-sanity-check
     parent: tox
     description: |
@@ -665,10 +653,6 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-rocky-py3:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-queens:
-            irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-queens-py3:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-multinode-full:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-multinode-full-py3:
@@ -769,8 +753,6 @@
         - tempest-full-stein-py3
         - tempest-full-rocky
         - tempest-full-rocky-py3
-        - tempest-full-queens
-        - tempest-full-queens-py3
     periodic:
       jobs:
         - tempest-all
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 7acfd62..ab994d1 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -48,6 +48,14 @@
    workspace
    run
 
+Supported OpenStack Releases and Python Versions
+------------------------------------------------
+
+.. toctree::
+   :maxdepth: 1
+
+   supported_version
+
 Developers Guide
 ================
 
diff --git a/doc/source/supported_version.rst b/doc/source/supported_version.rst
new file mode 100644
index 0000000..60e7b97
--- /dev/null
+++ b/doc/source/supported_version.rst
@@ -0,0 +1,37 @@
+Supported OpenStack Releases and Python Versions
+================================================
+
+This Document list the officially supported OpenStack releases
+and python versions by Tempest.
+
+Compatible OpenStack Releases
+-----------------------------
+
+Tempest master supports the below OpenStack Releases:
+
+* Train
+* Stein
+* Rocky
+
+For older OpenStack Release:
+
+For any older OpenStack Release than the listed above, Tempest master might work. But if
+Tempest master starts failing then, you can use the respective Tempest tag listed in OpenStack
+release page.
+
+For example: OpenStack Stein: Tempest 20.0.0
+
+* https://releases.openstack.org/stein/index.html#stein-tempest
+
+How to use Tempest tag on Extended Maintenance stable branch:
+* https://review.opendev.org/#/c/681950/
+
+Supported Python Versions
+-------------------------
+
+Tempest master supports the below python versions:
+
+* Python 2.7
+* Python 3.5
+* Python 3.6
+* Python 3.7
diff --git a/releasenotes/notes/add-consistency-group-exceptions-01cbb792cd710231.yaml b/releasenotes/notes/add-consistency-group-exceptions-01cbb792cd710231.yaml
new file mode 100644
index 0000000..e879c2c
--- /dev/null
+++ b/releasenotes/notes/add-consistency-group-exceptions-01cbb792cd710231.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    Fixed bug #1858417. Adding consistency group exceptions
+    ``ConsistencyGroupException`` and ``ConsistencyGroupSnapshotException``
+    that didn't exist before and caused failure in cinder-tempest-plugin.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 611979d..bfd8b2d 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v23.0.0
    v22.1.0
    v22.0.0
    v21.0.0
diff --git a/releasenotes/source/v23.0.0.rst b/releasenotes/source/v23.0.0.rst
new file mode 100644
index 0000000..7c5edf8
--- /dev/null
+++ b/releasenotes/source/v23.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v23.0.0 Release Notes
+=====================
+
+.. release-notes:: 23.0.0 Release Notes
+   :version: 23.0.0
diff --git a/tempest/api/compute/admin/test_security_group_default_rules.py b/tempest/api/compute/admin/test_security_group_default_rules.py
deleted file mode 100644
index bca6a22..0000000
--- a/tempest/api/compute/admin/test_security_group_default_rules.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# Copyright 2014 NEC Corporation.  All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import testtools
-
-from tempest.api.compute import base
-from tempest import config
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class SecurityGroupDefaultRulesTest(base.BaseV2ComputeAdminTest):
-    max_microversion = '2.35'
-
-    @classmethod
-    # TODO(GMann): Once Bug# 1311500 is fixed, these test can run
-    # for Neutron also.
-    @testtools.skipIf(CONF.service_available.neutron,
-                      "Skip as this functionality is not yet "
-                      "implemented in Neutron. Related Bug#1311500")
-    def setup_credentials(cls):
-        # A network and a subnet will be created for these tests
-        cls.set_network_resources(network=True, subnet=True)
-        super(SecurityGroupDefaultRulesTest, cls).setup_credentials()
-
-    @classmethod
-    def setup_clients(cls):
-        super(SecurityGroupDefaultRulesTest, cls).setup_clients()
-        cls.adm_client = cls.os_admin.security_group_default_rules_client
-
-    def _create_security_group_default_rules(self, ip_protocol='tcp',
-                                             from_port=22, to_port=22,
-                                             cidr='10.10.0.0/24'):
-        # Create Security Group default rule
-        rule = self.adm_client.create_security_default_group_rule(
-            ip_protocol=ip_protocol,
-            from_port=from_port,
-            to_port=to_port,
-            cidr=cidr)['security_group_default_rule']
-        self.assertEqual(ip_protocol, rule['ip_protocol'])
-        self.assertEqual(from_port, rule['from_port'])
-        self.assertEqual(to_port, rule['to_port'])
-        self.assertEqual(cidr, rule['ip_range']['cidr'])
-        return rule
-
-    @decorators.idempotent_id('6d880615-eec3-4d29-97c5-7a074dde239d')
-    def test_create_delete_security_group_default_rules(self):
-        # Create and delete Security Group default rule
-        ip_protocols = ['tcp', 'udp', 'icmp']
-        for ip_protocol in ip_protocols:
-            rule = self._create_security_group_default_rules(ip_protocol)
-            # Delete Security Group default rule
-            self.adm_client.delete_security_group_default_rule(rule['id'])
-            self.assertRaises(lib_exc.NotFound,
-                              self.adm_client.show_security_group_default_rule,
-                              rule['id'])
-
-    @decorators.idempotent_id('4d752e0a-33a1-4c3a-b498-ff8667ca22e5')
-    def test_create_security_group_default_rule_without_cidr(self):
-        ip_protocol = 'udp'
-        from_port = 80
-        to_port = 80
-        rule = self.adm_client.create_security_default_group_rule(
-            ip_protocol=ip_protocol,
-            from_port=from_port,
-            to_port=to_port)['security_group_default_rule']
-        self.addCleanup(self.adm_client.delete_security_group_default_rule,
-                        rule['id'])
-        self.assertNotEqual(0, rule['id'])
-        self.assertEqual('0.0.0.0/0', rule['ip_range']['cidr'])
-
-    @decorators.idempotent_id('29f2d218-69b0-4a95-8f3d-6bd0ef732b3a')
-    def test_create_security_group_default_rule_with_blank_cidr(self):
-        ip_protocol = 'icmp'
-        from_port = 10
-        to_port = 10
-        cidr = ''
-        rule = self.adm_client.create_security_default_group_rule(
-            ip_protocol=ip_protocol,
-            from_port=from_port,
-            to_port=to_port,
-            cidr=cidr)['security_group_default_rule']
-        self.addCleanup(self.adm_client.delete_security_group_default_rule,
-                        rule['id'])
-        self.assertNotEqual(0, rule['id'])
-        self.assertEqual('0.0.0.0/0', rule['ip_range']['cidr'])
-
-    @decorators.idempotent_id('6e6de55e-9146-4ae0-89f2-3569586e0b9b')
-    def test_security_group_default_rules_list(self):
-        ip_protocol = 'tcp'
-        from_port = 22
-        to_port = 22
-        cidr = '10.10.0.0/24'
-        rule = self._create_security_group_default_rules(ip_protocol,
-                                                         from_port,
-                                                         to_port,
-                                                         cidr)
-        self.addCleanup(self.adm_client.delete_security_group_default_rule,
-                        rule['id'])
-        rules = (self.adm_client.list_security_group_default_rules()
-                 ['security_group_default_rules'])
-        self.assertNotEmpty(rules)
-        self.assertIn(rule, rules)
-
-    @decorators.idempotent_id('15cbb349-86b4-4f71-a048-04b7ef3f150b')
-    def test_default_security_group_default_rule_show(self):
-        ip_protocol = 'tcp'
-        from_port = 22
-        to_port = 22
-        cidr = '10.10.0.0/24'
-        rule = self._create_security_group_default_rules(ip_protocol,
-                                                         from_port,
-                                                         to_port,
-                                                         cidr)
-        self.addCleanup(self.adm_client.delete_security_group_default_rule,
-                        rule['id'])
-        fetched_rule = self.adm_client.show_security_group_default_rule(
-            rule['id'])['security_group_default_rule']
-        self.assertEqual(rule, fetched_rule)
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index bee2b7b..97813a5 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -80,7 +80,7 @@
             # NOTE(andreaf) We need to ensure the ssh key has been
             # injected in the guest before we power cycle
             linux_client.validate_authentication()
-            disks_before_attach = linux_client.count_disks()
+            disks_before_attach = linux_client.list_disks()
 
         volume = self.create_volume()
 
@@ -102,8 +102,10 @@
                                        'ACTIVE')
 
         if CONF.validation.run_validation:
-            disks_after_attach = linux_client.count_disks()
-            self.assertGreater(disks_after_attach, disks_before_attach)
+            disks_after_attach = linux_client.list_disks()
+            self.assertGreater(
+                len(disks_after_attach),
+                len(disks_before_attach))
 
         self.servers_client.detach_volume(server['id'], attachment['volumeId'])
         waiters.wait_for_volume_resource_status(
@@ -118,8 +120,8 @@
                                        'ACTIVE')
 
         if CONF.validation.run_validation:
-            disks_after_detach = linux_client.count_disks()
-            self.assertEqual(disks_before_attach, disks_after_detach)
+            disks_after_detach = linux_client.list_disks()
+            self.assertEqual(len(disks_before_attach), len(disks_after_detach))
 
     @decorators.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
     def test_list_get_volume_attachments(self):
diff --git a/tempest/api/object_storage/test_crossdomain.py b/tempest/api/object_storage/test_crossdomain.py
index f61d9f8..1567e06 100644
--- a/tempest/api/object_storage/test_crossdomain.py
+++ b/tempest/api/object_storage/test_crossdomain.py
@@ -34,13 +34,12 @@
     def setUp(self):
         super(CrossdomainTest, self).setUp()
 
-        # Turning http://.../v1/foobar into http://.../
-        self.account_client.skip_path()
-
     @decorators.idempotent_id('d1b8b031-b622-4010-82f9-ff78a9e915c7')
     @utils.requires_ext(extension='crossdomain', service='object')
     def test_get_crossdomain_policy(self):
-        resp, body = self.account_client.get("crossdomain.xml", {})
+        url = self.account_client._get_base_version_url() + "crossdomain.xml"
+        resp, body = self.account_client.raw_request(url, "GET")
+        self.account_client._error_checker(resp, body)
         body = body.decode()
 
         self.assertTrue(body.startswith(self.xml_start) and
diff --git a/tempest/api/object_storage/test_healthcheck.py b/tempest/api/object_storage/test_healthcheck.py
index a186f9e..8e9e406 100644
--- a/tempest/api/object_storage/test_healthcheck.py
+++ b/tempest/api/object_storage/test_healthcheck.py
@@ -22,13 +22,12 @@
 
     def setUp(self):
         super(HealthcheckTest, self).setUp()
-        # Turning http://.../v1/foobar into http://.../
-        self.account_client.skip_path()
 
     @decorators.idempotent_id('db5723b1-f25c-49a9-bfeb-7b5640caf337')
     def test_get_healthcheck(self):
-
-        resp, _ = self.account_client.get("healthcheck", {})
+        url = self.account_client._get_base_version_url() + "healthcheck"
+        resp, body = self.account_client.raw_request(url, "GET")
+        self.account_client._error_checker(resp, body)
 
         # The target of the request is not any Swift resource. Therefore, the
         # existence of response header is checked without a custom matcher.
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 5875da3..b68a879 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -73,12 +73,12 @@
             msg = "'TYPE' column is required but the output doesn't have it: "
             raise tempest.lib.exceptions.TempestException(msg + output)
 
-    def count_disks(self):
+    def list_disks(self):
         disks_list = self.get_disks()
         disks_list = [line[0] for line in
                       [device_name.split()
                        for device_name in disks_list.splitlines()][1:]]
-        return len(disks_list)
+        return disks_list
 
     def get_boot_time(self):
         cmd = 'cut -f1 -d. /proc/uptime'
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index 13af890..b25b4b2 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -280,3 +280,12 @@
 
 class InvalidParam(TempestException):
     message = ("Invalid Parameter passed: %(invalid_param)s")
+
+
+class ConsistencyGroupException(TempestException):
+    message = "Consistency group %(cg_id)s failed and is in ERROR status"
+
+
+class ConsistencyGroupSnapshotException(TempestException):
+    message = ("Consistency group snapshot %(cgsnapshot_id)s failed and is "
+               "in ERROR status")
diff --git a/tempest/lib/services/object_storage/capabilities_client.py b/tempest/lib/services/object_storage/capabilities_client.py
index d31bbc2..f08bd9a 100644
--- a/tempest/lib/services/object_storage/capabilities_client.py
+++ b/tempest/lib/services/object_storage/capabilities_client.py
@@ -21,9 +21,10 @@
 class CapabilitiesClient(rest_client.RestClient):
 
     def list_capabilities(self):
-        self.skip_path()
         try:
-            resp, body = self.get('info')
+            url = self._get_base_version_url() + 'info'
+            resp, body = self.raw_request(url, 'GET')
+            self._error_checker(resp, body)
         finally:
             self.reset_path()
         body = json.loads(body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 1252f09..db8da84 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -634,8 +634,7 @@
 
     def nova_volume_attach(self, server, volume_to_attach):
         volume = self.servers_client.attach_volume(
-            server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
-            % CONF.compute.volume_device_name)['volumeAttachment']
+            server['id'], volumeId=volume_to_attach['id'])['volumeAttachment']
         self.assertEqual(volume_to_attach['id'], volume['id'])
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'in-use')
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index af79ea0..c3b3670 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -55,20 +55,24 @@
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder volume snapshots are disabled")
 
-    def _wait_for_volume_available_on_the_system(self, ip_address,
-                                                 private_key):
+    def _attached_volume_name(
+            self, disks_list_before_attach, ip_address, private_key):
         ssh = self.get_remote_client(ip_address, private_key=private_key)
 
-        def _func():
-            disks = ssh.get_disks()
-            LOG.debug("Disks: %s", disks)
-            return CONF.compute.volume_device_name in disks
+        def _wait_for_volume_available_on_system():
+            disks_list_after_attach = ssh.list_disks()
+            return len(disks_list_after_attach) > len(disks_list_before_attach)
 
-        if not test_utils.call_until_true(_func,
+        if not test_utils.call_until_true(_wait_for_volume_available_on_system,
                                           CONF.compute.build_timeout,
                                           CONF.compute.build_interval):
             raise lib_exc.TimeoutException
 
+        disks_list_after_attach = ssh.list_disks()
+        volume_name = [item for item in disks_list_after_attach
+                       if item not in disks_list_before_attach][0]
+        return volume_name
+
     @decorators.attr(type='slow')
     @decorators.idempotent_id('10fd234a-515c-41e5-b092-8323060598c5')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -91,15 +95,16 @@
         ip_for_server = self.get_server_ip(server)
 
         # Make sure the machine ssh-able before attaching the volume
-        self.get_remote_client(ip_for_server,
-                               private_key=keypair['private_key'],
-                               server=server)
-
+        linux_client = self.get_remote_client(
+            ip_for_server, private_key=keypair['private_key'],
+            server=server)
+        disks_list_before_attach = linux_client.list_disks()
         self.nova_volume_attach(server, volume)
-        self._wait_for_volume_available_on_the_system(ip_for_server,
-                                                      keypair['private_key'])
+        volume_device_name = self._attached_volume_name(
+            disks_list_before_attach, ip_for_server, keypair['private_key'])
+
         timestamp = self.create_timestamp(ip_for_server,
-                                          CONF.compute.volume_device_name,
+                                          volume_device_name,
                                           private_key=keypair['private_key'],
                                           server=server)
         self.nova_volume_detach(server, volume)
@@ -126,18 +131,19 @@
         # Make sure the machine ssh-able before attaching the volume
         # Just a live machine is responding
         # for device attache/detach as expected
-        self.get_remote_client(ip_for_snapshot,
-                               private_key=keypair['private_key'],
-                               server=server_from_snapshot)
+        linux_client = self.get_remote_client(
+            ip_for_snapshot, private_key=keypair['private_key'],
+            server=server_from_snapshot)
+        disks_list_before_attach = linux_client.list_disks()
 
         # attach volume2 to instance2
         self.nova_volume_attach(server_from_snapshot, volume_from_snapshot)
-        self._wait_for_volume_available_on_the_system(ip_for_snapshot,
-                                                      keypair['private_key'])
+        volume_device_name = self._attached_volume_name(
+            disks_list_before_attach, ip_for_snapshot, keypair['private_key'])
 
         # check the existence of the timestamp file in the volume2
         timestamp2 = self.get_timestamp(ip_for_snapshot,
-                                        CONF.compute.volume_device_name,
+                                        volume_device_name,
                                         private_key=keypair['private_key'],
                                         server=server_from_snapshot)
         self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index caad41c..937f93a 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -106,14 +106,15 @@
         self.assertEqual(self.conn.get_disks(), result)
         self._assert_exec_called_with('lsblk -lb --nodeps')
 
-    def test_count_disk(self):
+    def test_list_disks(self):
         output_lsblk = """\
 NAME       MAJ:MIN    RM          SIZE RO TYPE MOUNTPOINT
 sda          8:0       0  128035676160  0 disk
 sdb          8:16      0 1000204886016  0 disk
 sr0         11:0       1    1073741312  0 rom"""
+        disk_list = ['sda', 'sdb']
         self.ssh_mock.mock.exec_command.return_value = output_lsblk
-        self.assertEqual(self.conn.count_disks(), 2)
+        self.assertEqual(self.conn.list_disks(), disk_list)
 
     def test_get_boot_time(self):
         booted_at = 10000
diff --git a/tempest/tests/lib/services/object_storage/test_capabilities_client.py b/tempest/tests/lib/services/object_storage/test_capabilities_client.py
index b7f972a..9df7c7c 100644
--- a/tempest/tests/lib/services/object_storage/test_capabilities_client.py
+++ b/tempest/tests/lib/services/object_storage/test_capabilities_client.py
@@ -43,7 +43,7 @@
         }
         self.check_service_client_function(
             self.client.list_capabilities,
-            'tempest.lib.common.rest_client.RestClient.get',
+            'tempest.lib.common.rest_client.RestClient.raw_request',
             resp,
             bytes_body)
 
diff --git a/tox.ini b/tox.ini
index effd400..ff31fe8 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,9 +1,11 @@
 [tox]
 envlist = pep8,py36,py37,py27,bashate,pip-check-reqs
-minversion = 2.3.1
+minversion = 3.1.1
 skipsdist = True
+ignore_basepython_conflict = True
 
 [tempestenv]
+basepython = python3.6
 sitepackages = False
 setenv =
     VIRTUAL_ENV={envdir}
@@ -13,6 +15,7 @@
     -r{toxinidir}/requirements.txt
 
 [testenv]
+basepython = python3
 setenv =
     VIRTUAL_ENV={envdir}
     OS_LOG_CAPTURE=1
@@ -49,12 +52,12 @@
   coverage report
 
 [testenv:debug]
-basepython = python3
 commands = oslo_debug_helper -t tempest/tests {posargs}
 
 [testenv:all]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 # 'all' includes slow tests
 setenv =
     {[tempestenv]setenv}
@@ -77,6 +80,7 @@
 setenv =
     {[tempestenv]setenv}
     OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
+basepython = {[tempestenv]basepython}
 deps = {[tempestenv]deps}
 commands =
     echo "WARNING: The all-plugin env is deprecated and will be removed"
@@ -90,6 +94,7 @@
 setenv =
     {[tempestenv]setenv}
     OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:1200}
+basepython = {[tempestenv]basepython}
 deps = {[tempestenv]deps}
 commands =
     find . -type f -name "*.pyc" -delete
@@ -98,6 +103,7 @@
 [testenv:full]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag:
@@ -111,6 +117,7 @@
 [testenv:full-parallel]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select all tempest scenario and including the non slow api tests
@@ -121,6 +128,7 @@
 [testenv:integrated-network]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag and
@@ -133,6 +141,7 @@
 [testenv:integrated-compute]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag and
@@ -145,6 +154,7 @@
 [testenv:integrated-placement]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag and
@@ -157,6 +167,7 @@
 [testenv:integrated-storage]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag and
@@ -169,6 +180,7 @@
 [testenv:integrated-object-storage]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag and
@@ -181,6 +193,7 @@
 [testenv:full-serial]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag:
@@ -193,6 +206,7 @@
 [testenv:scenario]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select all scenario tests
@@ -203,6 +217,7 @@
 [testenv:smoke]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 commands =
@@ -212,6 +227,7 @@
 [testenv:smoke-serial]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # This is still serial because neutron doesn't work with parallel. See:
@@ -224,6 +240,7 @@
 [testenv:slow-serial]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # The regex below is used to select the slow tagged tests to run serially:
@@ -234,6 +251,7 @@
 [testenv:ipv6-only]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 # Run only smoke and ipv6 tests. This env is used to tests
@@ -253,12 +271,12 @@
 [testenv:venv-tempest]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 commands = {posargs}
 
 [testenv:docs]
-basepython = python3
 deps =
   -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/requirements.txt
@@ -269,7 +287,6 @@
 whitelist_externals = rm
 
 [testenv:pdf-docs]
-basepython = python3
 deps = {[testenv:docs]deps}
 whitelist_externals =
    make
@@ -281,7 +298,6 @@
 deps =
     -r{toxinidir}/test-requirements.txt
     autopep8
-basepython = python3
 commands =
     autopep8 --exit-code --max-line-length=79 --experimental --diff -r tempest setup.py
     flake8 {posargs}
@@ -289,7 +305,6 @@
 
 [testenv:autopep8]
 deps = autopep8
-basepython = python3
 commands =
     {toxinidir}/tools/format.sh
 
@@ -313,7 +328,6 @@
 import-order-style = pep8
 
 [testenv:releasenotes]
-basepython = python3
 deps =
   -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/requirements.txt
@@ -325,7 +339,6 @@
 whitelist_externals = rm
 
 [testenv:bashate]
-basepython = python3
 # if you want to test out some changes you have made to bashate
 # against tempest, just set BASHATE_INSTALL_PATH=/path/... to your
 # modified bashate tree
@@ -360,7 +373,6 @@
 
 [testenv:plugin-sanity-check]
 # perform tempest plugin sanity
-basepython = python3
 whitelist_externals = bash
 commands =
   bash tools/tempest-plugin-sanity.sh