Merge "Fix unstability in test_subnet_details"
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 59a2f64..b738fde 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -86,7 +86,7 @@
 master_doc = 'index'
 
 # General information about the project.
-copyright = u'2013, OpenStack QA Team'
+copyright = '2013, OpenStack QA Team'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -206,8 +206,8 @@
 # (source start file, target name, title, author, documentclass
 # [howto/manual]).
 latex_documents = [
-    ('index', 'doc-tempest.tex', u'Tempest Testing Project',
-     u'OpenStack Foundation', 'manual'),
+    ('index', 'doc-tempest.tex', 'Tempest Testing Project',
+     'OpenStack Foundation', 'manual'),
 ]
 
 latex_use_xindy = False
diff --git a/releasenotes/notes/add-placement-usage-client-method-8b6015cbd8a5e0f6.yaml b/releasenotes/notes/add-placement-usage-client-method-8b6015cbd8a5e0f6.yaml
new file mode 100644
index 0000000..d31a33c
--- /dev/null
+++ b/releasenotes/notes/add-placement-usage-client-method-8b6015cbd8a5e0f6.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Add ``placement`` API usage method for evaluating resource class
+    utilization of the resource provider. The following API call is available
+    for tempest from now in the resource_providers_client:
+
+    * GET /resource_providers/{uuid}/usages
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index 9a9de43..b353a18 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -59,7 +59,7 @@
 master_doc = 'index'
 
 # General information about the project.
-copyright = u'2016, tempest Developers'
+copyright = '2016, tempest Developers'
 
 # Release do not need a version number in the title, they
 # cover multiple versions.
@@ -194,8 +194,8 @@
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
     ('index', 'olso.configReleaseNotes.tex',
-     u'olso.config Release Notes Documentation',
-     u'tempest Developers', 'manual'),
+     'olso.config Release Notes Documentation',
+     'tempest Developers', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -225,8 +225,8 @@
 # (source start file, name, description, authors, manual section).
 man_pages = [
     ('index', 'olso.configreleasenotes',
-     u'tempest Release Notes Documentation',
-     [u'tempest Developers'], 1)
+     'tempest Release Notes Documentation',
+     ['tempest Developers'], 1)
 ]
 
 # If true, show URL addresses after external links.
@@ -240,8 +240,8 @@
 #  dir menu entry, description, category)
 texinfo_documents = [
     ('index', 'tempestReleaseNotes',
-     u'tempest Release Notes Documentation',
-     u'tempest Developers', 'olso.configReleaseNotes',
+     'tempest Release Notes Documentation',
+     'tempest Developers', 'olso.configReleaseNotes',
      'An OpenStack library for parsing configuration options from the command'
      ' line and configuration files.',
      'Miscellaneous'),
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 9b1719f..ed0a09f 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v27.0.0
    v26.1.0
    v26.0.0
    v24.0.0
diff --git a/releasenotes/source/v27.0.0.rst b/releasenotes/source/v27.0.0.rst
new file mode 100644
index 0000000..0009124
--- /dev/null
+++ b/releasenotes/source/v27.0.0.rst
@@ -0,0 +1,5 @@
+=====================
+v27.0.0 Release Notes
+=====================
+.. release-notes:: 27.0.0 Release Notes
+   :version: 27.0.0
diff --git a/requirements.txt b/requirements.txt
index eae5427..c71cabe 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,7 +13,6 @@
 stestr>=1.0.0 # Apache-2.0
 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
 oslo.utils>=4.7.0 # Apache-2.0
-six>=1.10.0 # MIT
 fixtures>=3.0.0 # Apache-2.0/BSD
 PyYAML>=3.12 # MIT
 python-subunit>=1.0.0 # Apache-2.0/BSD
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 37026e4..a8b3ede 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -40,7 +40,7 @@
     - "'TEMPEST_BRANCH' in devstack_localrc"
     - "'TEMPEST_VENV_UPPER_CONSTRAINTS' in devstack_localrc"
     - devstack_localrc['TEMPEST_BRANCH'] != 'master'
-    - devstack_localrc['TEMPEST_VENV_UPPER_CONSTRAINTS'] != 'default'
+    - devstack_localrc['TEMPEST_VENV_UPPER_CONSTRAINTS'] != 'master'
 
 - name: Set OS_TEST_TIMEOUT if requested
   set_fact:
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 52ccea7..c91b557 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -23,6 +23,8 @@
 from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -55,6 +57,10 @@
     def setup_clients(cls):
         super(LiveMigrationTestBase, cls).setup_clients()
         cls.admin_migration_client = cls.os_admin.migrations_client
+        cls.networks_client = cls.os_primary.networks_client
+        cls.subnets_client = cls.os_primary.subnets_client
+        cls.ports_client = cls.os_primary.ports_client
+        cls.trunks_client = cls.os_primary.trunks_client
 
     def _migrate_server_to(self, server_id, dest_host, volume_backed=False):
         kwargs = dict()
@@ -197,6 +203,90 @@
 
         self.assertEqual(volume_id1, volume_id2)
 
+    def _create_net_subnet(self, name, cidr):
+        net_name = data_utils.rand_name(name=name)
+        net = self.networks_client.create_network(name=net_name)['network']
+        self.addClassResourceCleanup(
+            self.networks_client.delete_network, net['id'])
+
+        subnet = self.subnets_client.create_subnet(
+            network_id=net['id'],
+            cidr=cidr,
+            ip_version=4)
+        self.addClassResourceCleanup(self.subnets_client.delete_subnet,
+                                     subnet['subnet']['id'])
+        return net
+
+    def _create_port(self, network_id, name):
+        name = data_utils.rand_name(name=name)
+        port = self.ports_client.create_port(name=name,
+                                             network_id=network_id)['port']
+        self.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
+                                     self.ports_client.delete_port,
+                                     port_id=port['id'])
+        return port
+
+    def _create_trunk_with_subport(self):
+        tenant_network = self.get_tenant_network()
+        parent = self._create_port(network_id=tenant_network['id'],
+                                   name='parent')
+        net = self._create_net_subnet(name='subport_net', cidr='19.80.0.0/24')
+        subport = self._create_port(network_id=net['id'], name='subport')
+
+        trunk = self.trunks_client.create_trunk(
+            name=data_utils.rand_name('trunk'),
+            port_id=parent['id'],
+            sub_ports=[{"segmentation_id": 42, "port_id": subport['id'],
+                        "segmentation_type": "vlan"}]
+        )['trunk']
+        self.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
+                                     self.trunks_client.delete_trunk,
+                                     trunk['id'])
+        return trunk, parent, subport
+
+    def _is_port_status_active(self, port_id):
+        port = self.ports_client.show_port(port_id)['port']
+        return port['status'] == 'ACTIVE'
+
+    @decorators.idempotent_id('0022c12e-a482-42b0-be2d-396b5f0cffe3')
+    @utils.requires_ext(service='network', extension='trunk')
+    @utils.services('network')
+    def test_live_migration_with_trunk(self):
+        """Test live migration with trunk and subport"""
+        trunk, parent, subport = self._create_trunk_with_subport()
+
+        server = self.create_test_server(
+            wait_until="ACTIVE", networks=[{'port': parent['id']}])
+
+        # Wait till subport status is ACTIVE
+        self.assertTrue(
+            test_utils.call_until_true(
+                self._is_port_status_active, CONF.validation.connect_timeout,
+                5, subport['id']))
+        self.assertTrue(
+            test_utils.call_until_true(
+                self._is_port_status_active, CONF.validation.connect_timeout,
+                5, parent['id']))
+        subport = self.ports_client.show_port(subport['id'])['port']
+
+        if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
+            # not to specify a host so that the scheduler will pick one
+            target_host = None
+        else:
+            target_host = self.get_host_other_than(server['id'])
+
+        self._live_migrate(server['id'], target_host, 'ACTIVE')
+
+        # Wait till subport status is ACTIVE
+        self.assertTrue(
+            test_utils.call_until_true(
+                self._is_port_status_active, CONF.validation.connect_timeout,
+                5, subport['id']))
+        self.assertTrue(
+            test_utils.call_until_true(
+                self._is_port_status_active, CONF.validation.connect_timeout,
+                5, parent['id']))
+
 
 class LiveMigrationRemoteConsolesV26Test(LiveMigrationTestBase):
     min_microversion = '2.6'
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index a9c0e56..1308b19 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -15,8 +15,6 @@
 
 import struct
 import urllib.parse as urlparse
-
-import six
 import urllib3
 
 from tempest.api.compute import base
@@ -122,7 +120,7 @@
                 'Expected authentication type None.')
             # Send to the server that we only support authentication
             # type None
-            self._websocket.send_frame(six.int2byte(1))
+            self._websocket.send_frame(bytes((1,)))
 
             # The server should send 4 bytes of 0's if security
             # handshake succeeded
@@ -135,7 +133,7 @@
                 'Server did not think security was successful.')
 
         # Say to leave the desktop as shared as part of client initialization
-        self._websocket.send_frame(six.int2byte(1))
+        self._websocket.send_frame(bytes((1,)))
         # Get the server initialization packet back and make sure it is the
         # right structure where bytes 20-24 is the name length and
         # 24-N is the name
diff --git a/tempest/api/compute/servers/test_server_tags.py b/tempest/api/compute/servers/test_server_tags.py
index 619f480..c988788 100644
--- a/tempest/api/compute/servers/test_server_tags.py
+++ b/tempest/api/compute/servers/test_server_tags.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
-
 from tempest.api.compute import base
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
@@ -84,11 +82,11 @@
         new_tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
         replaced_tags = self.client.update_all_tags(
             self.server['id'], new_tags)['tags']
-        six.assertCountEqual(self, new_tags, replaced_tags)
+        self.assertCountEqual(new_tags, replaced_tags)
 
         # List the tags and check that the tags were replaced.
         fetched_tags = self.client.list_tags(self.server['id'])['tags']
-        six.assertCountEqual(self, new_tags, fetched_tags)
+        self.assertCountEqual(new_tags, fetched_tags)
 
     @decorators.idempotent_id('a63b2a74-e918-4b7c-bcab-10c855f3a57e')
     def test_delete_all_tags(self):
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index cc013e3..1c839eb 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -103,7 +103,7 @@
         server = self.create_test_server(wait_until='ACTIVE')
         self.addCleanup(self.delete_server, server['id'])
         # Update instance name with non-ASCII characters
-        prefix_name = u'\u00CD\u00F1st\u00E1\u00F1c\u00E9'
+        prefix_name = '\u00CD\u00F1st\u00E1\u00F1c\u00E9'
         self._update_server_name(server['id'], 'ACTIVE', prefix_name)
 
         # stop server and check server name update again
diff --git a/tempest/api/identity/v2/test_tokens.py b/tempest/api/identity/v2/test_tokens.py
index a928ad9..d3776b8 100644
--- a/tempest/api/identity/v2/test_tokens.py
+++ b/tempest/api/identity/v2/test_tokens.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from oslo_utils import timeutils
-import six
 from tempest.api.identity import base
 from tempest.lib import decorators
 
@@ -36,7 +35,7 @@
         body = token_client.auth(username, password, tenant_name)
 
         self.assertNotEmpty(body['token']['id'])
-        self.assertIsInstance(body['token']['id'], six.string_types)
+        self.assertIsInstance(body['token']['id'], str)
 
         now = timeutils.utcnow()
         expires_at = timeutils.normalize_time(
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index b201285..55fcead 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -16,7 +16,6 @@
 import operator
 
 from oslo_utils import timeutils
-import six
 
 from tempest.api.identity import base
 from tempest.lib import decorators
@@ -88,7 +87,7 @@
             auth_data=True)
 
         self.assertNotEmpty(token_id)
-        self.assertIsInstance(token_id, six.string_types)
+        self.assertIsInstance(token_id, str)
 
         now = timeutils.utcnow()
         expires_at = timeutils.normalize_time(
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index efa23bb..d283ab3 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -105,12 +105,10 @@
                      'validate the image/tasks API.')
             return
 
-        # Make sure we can access the task and that some of the key
-        # fields look legit.
-        tasks = self.client.show_image_tasks(image_id)
-        self.assertEqual(1, len(tasks['tasks']))
-        task = tasks['tasks'][0]
-        self.assertEqual('success', task['status'])
+        tasks = waiters.wait_for_image_tasks_status(
+            self.client, image_id, 'success')
+        self.assertEqual(1, len(tasks))
+        task = tasks[0]
         self.assertEqual(resp.response['x-openstack-request-id'],
                          task['request_id'])
         self.assertEqual('glance-direct',
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index 0b9d381..905bf13 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
-
 from tempest.api.network import base
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
@@ -99,8 +97,7 @@
         body = self.ports_client.update_port(
             port_id, allowed_address_pairs=allowed_address_pairs)
         allowed_address_pair = body['port']['allowed_address_pairs']
-        six.assertCountEqual(self, allowed_address_pair,
-                             allowed_address_pairs)
+        self.assertCountEqual(allowed_address_pair, allowed_address_pairs)
 
     @decorators.idempotent_id('9599b337-272c-47fd-b3cf-509414414ac4')
     def test_update_port_with_address_pair(self):
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 7646b63..caaf964 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 import netaddr
-import six
 import testtools
 
 from tempest.api.network import base
@@ -595,9 +594,9 @@
         subnets = [sub['id'] for sub in body['subnets']
                    if sub['network_id'] == network['id']]
         test_subnet_ids = [sub['id'] for sub in (subnet1, subnet2)]
-        six.assertCountEqual(self, subnets,
-                             test_subnet_ids,
-                             'Subnet are not in the same network')
+        self.assertCountEqual(subnets,
+                              test_subnet_ids,
+                              'Subnet are not in the same network')
 
 
 class NetworksIpV6TestAttrs(BaseNetworkTestResources):
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index ffea6f6..4966ec4 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -14,8 +14,6 @@
 #    under the License.
 
 import random
-
-import six
 import testtools
 
 from tempest.api.object_storage import base
@@ -43,7 +41,7 @@
     def resource_setup(cls):
         super(AccountTest, cls).resource_setup()
         for i in range(ord('a'), ord('f') + 1):
-            name = data_utils.rand_name(name='%s-' % six.int2byte(i))
+            name = data_utils.rand_name(name='%s-' % bytes((i,)))
             cls.container_client.update_container(name)
             cls.addClassResourceCleanup(base.delete_containers,
                                         [name],
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index a5de987..83733bd 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -10,7 +10,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
 from tempest.api.volume import base
 from tempest.common import waiters
 from tempest import config
@@ -149,4 +148,4 @@
         # assert that volumes are each created on separate hosts:
         msg = ("volumes %s were created in the same backend" % ", "
                .join(volume_hosts))
-        six.assertCountEqual(self, volume_hosts, set(volume_hosts), msg)
+        self.assertCountEqual(volume_hosts, set(volume_hosts), msg)
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 9f9fc3b..d5c6fd9 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -155,6 +155,10 @@
 
         backup = backup_client.create_backup(
             volume_id=volume_id, **kwargs)['backup']
+        # addCleanup uses list pop to cleanup. Wait should be added before
+        # the backup is deleted
+        self.addCleanup(backup_client.wait_for_resource_deletion,
+                        backup['id'])
         self.addCleanup(backup_client.delete_backup, backup['id'])
         waiters.wait_for_volume_resource_status(backup_client, backup['id'],
                                                 'available')
diff --git a/tempest/clients.py b/tempest/clients.py
index c4e00fe..6807fc4 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -72,6 +72,7 @@
         self.qos_client = self.network.QosClient()
         self.qos_min_bw_client = self.network.QosMinimumBandwidthRulesClient()
         self.segments_client = self.network.SegmentsClient()
+        self.trunks_client = self.network.TrunksClient()
 
     def _set_image_clients(self):
         if CONF.service_available.glance:
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index eaac05e..3750b11 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -193,6 +193,30 @@
     raise lib_exc.TimeoutException(message)
 
 
+def wait_for_image_tasks_status(client, image_id, status):
+    """Waits for an image tasks to reach a given status."""
+    pending_tasks = []
+    start = int(time.time())
+    while int(time.time()) - start < client.build_timeout:
+        tasks = client.show_image_tasks(image_id)['tasks']
+
+        pending_tasks = [task for task in tasks if task['status'] != status]
+        if not pending_tasks:
+            return tasks
+        time.sleep(client.build_interval)
+
+    message = ('Image %(image_id)s tasks: %(pending_tasks)s '
+               'failed to reach %(status)s state within the required '
+               'time (%(timeout)s s).' % {'image_id': image_id,
+                                          'pending_tasks': pending_tasks,
+                                          'status': status,
+                                          'timeout': client.build_timeout})
+    caller = test_utils.find_test_caller()
+    if caller:
+        message = '(%s) %s' % (caller, message)
+    raise lib_exc.TimeoutException(message)
+
+
 def wait_for_image_imported_to_stores(client, image_id, stores=None):
     """Waits for an image to be imported to all requested stores.
 
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 6a97a00..c1e6b2d 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -140,20 +140,10 @@
                "decorators.skip_because from tempest.lib")
 
 
-def _common_service_clients_check(logical_line, physical_line, filename,
-                                  ignored_list_file=None):
+def _common_service_clients_check(logical_line, physical_line, filename):
     if not re.match('tempest/(lib/)?services/.*', filename):
         return False
 
-    if ignored_list_file is not None:
-        ignored_list = []
-        with open('tempest/hacking/' + ignored_list_file) as f:
-            for line in f:
-                ignored_list.append(line.strip())
-
-        if filename in ignored_list:
-            return False
-
     if not METHOD.match(physical_line):
         return False
 
@@ -171,7 +161,7 @@
     T110
     """
     if not _common_service_clients_check(logical_line, physical_line,
-                                         filename, 'ignored_list_T110.txt'):
+                                         filename):
         return
 
     for line in lines[line_number:]:
@@ -199,7 +189,7 @@
     T111
     """
     if not _common_service_clients_check(logical_line, physical_line,
-                                         filename, 'ignored_list_T111.txt'):
+                                         filename):
         return
 
     for line in lines[line_number:]:
diff --git a/tempest/lib/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
index 80dbc1d..db5c8c3 100644
--- a/tempest/lib/common/api_version_utils.py
+++ b/tempest/lib/common/api_version_utils.py
@@ -12,7 +12,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
 import testtools
 
 from tempest.lib.common import api_version_request
@@ -117,7 +116,7 @@
     :param response_header: Response header where microversion is
             expected to be present.
     """
-    if not isinstance(api_microversion, six.string_types):
+    if not isinstance(api_microversion, str):
         raise TypeError('api_microversion must be a string')
     api_microversion_header_name = api_microversion_header_name.lower()
     if (api_microversion_header_name not in response_header or
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index f334c36..d86522a 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -16,7 +16,6 @@
 
 import netaddr
 from oslo_log import log as logging
-import six
 
 from tempest.lib.common import cred_client
 from tempest.lib.common import cred_provider
@@ -556,7 +555,7 @@
         if not self._creds:
             return
         self._clear_isolated_net_resources()
-        for creds in six.itervalues(self._creds):
+        for creds in self._creds.values():
             try:
                 self.creds_client.delete_user(creds.user_id)
             except lib_exc.NotFound:
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
index 8c1a802..33f871b 100644
--- a/tempest/lib/common/http.py
+++ b/tempest/lib/common/http.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
 import urllib3
 
 
@@ -89,7 +88,7 @@
                 for key, value in info.getheaders().items():
                     # We assume HTTP header name to be string, not random
                     # bytes, thus ensure we have string keys.
-                    self[six.u(key).lower()] = value
+                    self[str(key).lower()] = value
                 self.status = info.status
                 self['status'] = str(self.status)
                 self.reason = info.reason
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 00f2aeb..573d64e 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -24,7 +24,6 @@
 from oslo_log import log as logging
 from oslo_log import versionutils
 from oslo_serialization import jsonutils as json
-import six
 
 from tempest.lib.common import http
 from tempest.lib.common import jsonschema_validator
@@ -509,7 +508,7 @@
             if not hasattr(body, "keys") or len(body.keys()) != 1:
                 return body
             # Just return the "wrapped" element
-            _, first_item = six.next(six.iteritems(body))
+            _, first_item = tuple(body.items())[0]
             if isinstance(first_item, (dict, list)):
                 return first_item
         except (ValueError, IndexError):
@@ -907,9 +906,14 @@
                 return
             if int(time.time()) - start_time >= self.build_timeout:
                 message = ('Failed to delete %(resource_type)s %(id)s within '
-                           'the required time (%(timeout)s s).' %
+                           'the required time (%(timeout)s s). Timer started '
+                           'at %(start_time)s. Timer ended at %(end_time)s'
+                           'waited for %(wait_time)s' %
                            {'resource_type': self.resource_type, 'id': id,
-                            'timeout': self.build_timeout})
+                            'timeout': self.build_timeout,
+                            'start_time': start_time,
+                            'end_time': int(time.time()),
+                            'wait_time': int(time.time()) - start_time})
                 caller = test_utils.find_test_caller()
                 if caller:
                     message = '(%s) %s' % (caller, message)
diff --git a/tempest/lib/common/ssh.py b/tempest/lib/common/ssh.py
index 60107d7..ee15375 100644
--- a/tempest/lib/common/ssh.py
+++ b/tempest/lib/common/ssh.py
@@ -21,7 +21,6 @@
 import warnings
 
 from oslo_log import log as logging
-import six
 
 from tempest.lib import exceptions
 
@@ -66,7 +65,7 @@
         self.username = username
         self.port = port
         self.password = password
-        if isinstance(pkey, six.string_types):
+        if isinstance(pkey, str):
             pkey = paramiko.RSAKey.from_private_key(
                 io.StringIO(str(pkey)))
         self.pkey = pkey
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index b6671b5..1e94f86 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -19,7 +19,6 @@
 import uuid
 
 from oslo_utils import uuidutils
-import six.moves
 
 
 def rand_uuid():
@@ -171,7 +170,7 @@
     """
     if size > 1 << 20:
         raise RuntimeError('Size should be less than 1MiB')
-    return b''.join([six.int2byte(random.randint(0, 255))
+    return b''.join([bytes((random.randint(0, 255),))
                      for i in range(size)])
 
 
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
index 71fed02..d84dd28 100644
--- a/tempest/lib/common/utils/linux/remote_client.py
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -15,7 +15,6 @@
 
 import netaddr
 from oslo_log import log as logging
-import six
 
 from tempest.lib.common import ssh
 from tempest.lib.common.utils import test_utils
@@ -55,8 +54,8 @@
                             except Exception:
                                 msg = 'Could not get console_log for server %s'
                                 LOG.debug(msg, self.server['id'])
-                    # re-raise the original ssh timeout exception
-                    six.reraise(*original_exception)
+                    # raise the original ssh timeout exception
+                    raise
                 finally:
                     # Delete the traceback to avoid circular references
                     _, _, trace = original_exception
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index 25ff473..a4633ca 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -16,7 +16,6 @@
 import uuid
 
 from oslo_log import log as logging
-import six
 import testtools
 
 from tempest.lib import exceptions as lib_exc
@@ -110,7 +109,7 @@
 
 def idempotent_id(id):
     """Stub for metadata decorator"""
-    if not isinstance(id, six.string_types):
+    if not isinstance(id, str):
         raise TypeError('Test idempotent_id must be string not %s'
                         '' % type(id).__name__)
     uuid.UUID(id)
@@ -140,7 +139,7 @@
         # Check to see if the attr should be conditional applied.
         if 'condition' in kwargs and not kwargs.get('condition'):
             return f
-        if 'type' in kwargs and isinstance(kwargs['type'], six.string_types):
+        if 'type' in kwargs and isinstance(kwargs['type'], str):
             f = testtools.testcase.attr(kwargs['type'])(f)
         elif 'type' in kwargs and isinstance(kwargs['type'], list):
             for attr in kwargs['type']:
diff --git a/tempest/lib/services/compute/assisted_volume_snapshots_client.py b/tempest/lib/services/compute/assisted_volume_snapshots_client.py
index 8b67491..7a949df 100644
--- a/tempest/lib/services/compute/assisted_volume_snapshots_client.py
+++ b/tempest/lib/services/compute/assisted_volume_snapshots_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
diff --git a/tempest/lib/services/identity/v3/identity_providers_client.py b/tempest/lib/services/identity/v3/identity_providers_client.py
index af6a245..002bc8c 100644
--- a/tempest/lib/services/identity/v3/identity_providers_client.py
+++ b/tempest/lib/services/identity/v3/identity_providers_client.py
@@ -13,8 +13,9 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/mappings_client.py b/tempest/lib/services/identity/v3/mappings_client.py
index 9ec5384..a924b33 100644
--- a/tempest/lib/services/identity/v3/mappings_client.py
+++ b/tempest/lib/services/identity/v3/mappings_client.py
@@ -13,8 +13,9 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/oauth_token_client.py b/tempest/lib/services/identity/v3/oauth_token_client.py
index 236b224..564d6d6 100644
--- a/tempest/lib/services/identity/v3/oauth_token_client.py
+++ b/tempest/lib/services/identity/v3/oauth_token_client.py
@@ -20,8 +20,6 @@
 import time
 from urllib import parse as urlparse
 
-import six
-
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.common import rest_client
@@ -35,7 +33,7 @@
         safe = b'~'
         s = s.encode('utf-8') if isinstance(s, str) else s
         s = urlparse.quote(s, safe)
-        if isinstance(s, six.binary_type):
+        if isinstance(s, bytes):
             s = s.decode('utf-8')
         return s
 
diff --git a/tempest/lib/services/identity/v3/protocols_client.py b/tempest/lib/services/identity/v3/protocols_client.py
index 2e0221b..19aa426 100644
--- a/tempest/lib/services/identity/v3/protocols_client.py
+++ b/tempest/lib/services/identity/v3/protocols_client.py
@@ -13,8 +13,9 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/service_providers_client.py b/tempest/lib/services/identity/v3/service_providers_client.py
index b84cf43..5d4f014 100644
--- a/tempest/lib/services/identity/v3/service_providers_client.py
+++ b/tempest/lib/services/identity/v3/service_providers_client.py
@@ -13,8 +13,9 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index f7ac046..7e57499 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__init__.py
@@ -36,6 +36,7 @@
 from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
 from tempest.lib.services.network.subnets_client import SubnetsClient
 from tempest.lib.services.network.tags_client import TagsClient
+from tempest.lib.services.network.trunks_client import TrunksClient
 from tempest.lib.services.network.versions_client import NetworkVersionsClient
 
 __all__ = ['AgentsClient', 'ExtensionsClient', 'FloatingIPsClient',
@@ -44,4 +45,4 @@
            'QosClient', 'QosMinimumBandwidthRulesClient', 'QuotasClient',
            'RoutersClient', 'SecurityGroupRulesClient', 'SecurityGroupsClient',
            'SegmentsClient', 'ServiceProvidersClient', 'SubnetpoolsClient',
-           'SubnetsClient', 'TagsClient']
+           'SubnetsClient', 'TagsClient', 'TrunksClient']
diff --git a/tempest/lib/services/network/trunks_client.py b/tempest/lib/services/network/trunks_client.py
new file mode 100644
index 0000000..2fd9e01
--- /dev/null
+++ b/tempest/lib/services/network/trunks_client.py
@@ -0,0 +1,100 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.network import base
+
+
+class TrunksClient(base.BaseNetworkClient):
+
+    def create_trunk(self, **kwargs):
+        """Creates a trunk.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/network/v2/index.html#create-trunk
+        """
+        uri = '/trunks'
+        post_data = {'trunk': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_trunk(self, trunk_id, **kwargs):
+        """Updates a trunk.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/network/v2/index.html#update-trunk
+        """
+        uri = '/trunks/%s' % trunk_id
+        put_data = {'trunk': kwargs}
+        return self.update_resource(uri, put_data)
+
+    def show_trunk(self, trunk_id):
+        """Shows details for a trunk.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/network/v2/index.html#show-trunk
+        """
+        uri = '/trunks/%s' % trunk_id
+        return self.show_resource(uri)
+
+    def delete_trunk(self, trunk_id):
+        """Deletes a trunk.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/network/v2/index.html#delete-trunk
+        """
+        uri = '/trunks/%s' % trunk_id
+        return self.delete_resource(uri)
+
+    def list_trunks(self, **filters):
+        """Lists trunks to which the tenant has access.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/network/v2/index.html#list-trunks
+        """
+        uri = '/trunks'
+        return self.list_resources(uri, **filters)
+
+    def add_subports_to_trunk(self, trunk_id, sub_ports):
+        """Add subports to a trunk.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/network/v2/index.html#add-subports-to-trunk
+        """
+        uri = '/trunks/%s/add_subports' % trunk_id
+        put_data = {'sub_ports': sub_ports}
+        return self.update_resource(uri, put_data)
+
+    def delete_subports_from_trunk(self, trunk_id, sub_ports):
+        """Deletes subports from a trunk.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/network/v2/index.html#delete-subports-from-trunk
+        """
+        uri = '/trunks/%s/remove_subports' % trunk_id
+        put_data = {'sub_ports': sub_ports}
+        return self.update_resource(uri, put_data)
+
+    def list_subports_of_trunk(self, trunk_id):
+        """List subports of a trunk.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/network/v2/index.html#list-subports-for-trunk
+        """
+        uri = '/trunks/%s/get_subports' % trunk_id
+        return self.list_resources(uri)
diff --git a/tempest/lib/services/placement/resource_providers_client.py b/tempest/lib/services/placement/resource_providers_client.py
index e6fbcb2..3214053 100644
--- a/tempest/lib/services/placement/resource_providers_client.py
+++ b/tempest/lib/services/placement/resource_providers_client.py
@@ -26,6 +26,7 @@
     * resource providers
     * resource provider inventories
     * resource provider aggregates
+    * resource provider usages
     """
 
     def list_resource_providers(self, **params):
@@ -69,6 +70,19 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
+    def list_resource_provider_usages(self, rp_uuid):
+        """List resource provider usages.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/placement/#list-resource-provider-usages
+        """
+        url = '/resource_providers/%s/usages' % rp_uuid
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
     def list_resource_provider_aggregates(self, rp_uuid):
         """List resource provider aggregates.
 
diff --git a/tempest/lib/services/volume/v1/volumes_client.py b/tempest/lib/services/volume/v1/volumes_client.py
index 9fca800..5d8d73b 100644
--- a/tempest/lib/services/volume/v1/volumes_client.py
+++ b/tempest/lib/services/volume/v1/volumes_client.py
@@ -16,7 +16,6 @@
 from urllib import parse as urllib
 
 from oslo_serialization import jsonutils as json
-import six
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
@@ -31,7 +30,7 @@
         If params is a string it will be left as it is, but if it's not it will
         be urlencoded.
         """
-        if isinstance(params, six.string_types):
+        if isinstance(params, str):
             return params
         return urllib.urlencode(params)
 
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index 147a79b..9c6fe68 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -16,7 +16,6 @@
 from urllib import parse as urllib
 
 from oslo_serialization import jsonutils as json
-import six
 
 from tempest.lib.api_schema.response.volume import volumes as schema
 from tempest.lib.common import rest_client
@@ -33,7 +32,7 @@
         If params is a string it will be left as it is, but if it's not it will
         be urlencoded.
         """
-        if isinstance(params, six.string_types):
+        if isinstance(params, str):
             return params
         return urllib.urlencode(params)
 
diff --git a/tempest/test.py b/tempest/test.py
index 655b9a4..8ea3b16 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -20,7 +20,6 @@
 import debtcollector.moves
 import fixtures
 from oslo_log import log as logging
-import six
 import testtools
 
 from tempest import clients
@@ -179,7 +178,7 @@
                      etype, cls.__name__)
             cls.tearDownClass()
             try:
-                six.reraise(etype, value, trace)
+                raise value.with_traceback(trace)
             finally:
                 del trace  # to avoid circular refs
         finally:
@@ -233,7 +232,7 @@
         # the first one
         if re_raise and etype is not None:
             try:
-                six.reraise(etype, value, trace)
+                raise value.with_traceback(trace)
             finally:
                 del trace  # to avoid circular refs
 
@@ -384,7 +383,7 @@
             # This may raise an exception in case credentials are not available
             # In that case we want to let the exception through and the test
             # fail accordingly
-            if isinstance(credentials_type, six.string_types):
+            if isinstance(credentials_type, str):
                 manager = cls.get_client_manager(
                     credential_type=credentials_type)
                 setattr(cls, 'os_%s' % credentials_type, manager)
@@ -859,7 +858,7 @@
         """
         # Get a manager for the given credentials_type, but at least
         # always fall back on getting the manager for primary credentials
-        if isinstance(credentials_type, six.string_types):
+        if isinstance(credentials_type, str):
             manager = cls.get_client_manager(credential_type=credentials_type)
         elif isinstance(credentials_type, list):
             scope = 'project'
diff --git a/tempest/tests/cmd/test_cleanup_services.py b/tempest/tests/cmd/test_cleanup_services.py
index fc44793..2301be6 100644
--- a/tempest/tests/cmd/test_cleanup_services.py
+++ b/tempest/tests/cmd/test_cleanup_services.py
@@ -143,40 +143,40 @@
 
     saved_state = {
         # Static list to ensure global service saved items are not deleted
-        "users": {u'32rwef64245tgr20121qw324bgg': u'Lightning'},
-        "flavors": {u'42': u'm1.tiny'},
-        "images": {u'34yhwr-4t3q': u'stratus-0.3.2-x86_64-disk'},
-        "roles": {u'3efrt74r45hn': u'president'},
-        "projects": {u'f38ohgp93jj032': u'manhattan'},
-        "domains": {u'default': u'Default'},
+        "users": {'32rwef64245tgr20121qw324bgg': 'Lightning'},
+        "flavors": {'42': 'm1.tiny'},
+        "images": {'34yhwr-4t3q': 'stratus-0.3.2-x86_64-disk'},
+        "roles": {'3efrt74r45hn': 'president'},
+        "projects": {'f38ohgp93jj032': 'manhattan'},
+        "domains": {'default': 'Default'},
         # Static list to ensure project service saved items are not deleted
-        "snapshots": {u'1ad4c789-7e8w-4dwg-afc5': u'saved-snapshot'},
-        "servers": {u'7a6d4v7w-36ds-4216': u'saved-server'},
-        "server_groups": {u'as6d5f7g-46ca-475e': u'saved-server-group'},
-        "keypairs": {u'saved-key-pair': {
-            u'fingerprint': u'7e:eb:ab:24',
-            u'name': u'saved-key-pair'
+        "snapshots": {'1ad4c789-7e8w-4dwg-afc5': 'saved-snapshot'},
+        "servers": {'7a6d4v7w-36ds-4216': 'saved-server'},
+        "server_groups": {'as6d5f7g-46ca-475e': 'saved-server-group'},
+        "keypairs": {'saved-key-pair': {
+            'fingerprint': '7e:eb:ab:24',
+            'name': 'saved-key-pair'
         }},
-        "volumes": {u'aa77asdf-1234': u'saved-volume'},
-        "networks": {u'6722fc13-4319': {
-            u'id': u'6722fc13-4319',
-            u'name': u'saved-network'
+        "volumes": {'aa77asdf-1234': 'saved-volume'},
+        "networks": {'6722fc13-4319': {
+            'id': '6722fc13-4319',
+            'name': 'saved-network'
         }},
-        "floatingips": {u'9e82d248-408a': {
-            u'id': u'9e82d248-408a',
-            u'status': u'ACTIVE'
+        "floatingips": {'9e82d248-408a': {
+            'id': '9e82d248-408a',
+            'status': 'ACTIVE'
         }},
-        "routers": {u'4s5w34hj-id44': u'saved-router'},
-        "metering_label_rules": {u'93a973ce-4dc5': {
-            u'direction': u'ingress',
-            u'id': u'93a973ce-4dc5'
+        "routers": {'4s5w34hj-id44': 'saved-router'},
+        "metering_label_rules": {'93a973ce-4dc5': {
+            'direction': 'ingress',
+            'id': '93a973ce-4dc5'
         }},
-        "metering_labels": {u'723b346ce866-4c7q': u'saved-label'},
-        "ports": {u'aa74aa4v-741a': u'saved-port'},
-        "security_groups": {u'7q844add-3697': u'saved-sec-group'},
-        "subnets": {u'55ttda4a-2584': u'saved-subnet'},
-        "subnetpools": {u'8acf64c1-43fc': u'saved-subnet-pool'},
-        "regions": {u'RegionOne': {}}
+        "metering_labels": {'723b346ce866-4c7q': 'saved-label'},
+        "ports": {'aa74aa4v-741a': 'saved-port'},
+        "security_groups": {'7q844add-3697': 'saved-sec-group'},
+        "subnets": {'55ttda4a-2584': 'saved-subnet'},
+        "subnetpools": {'8acf64c1-43fc': 'saved-subnet-pool'},
+        "regions": {'RegionOne': {}}
     }
     # Mocked methods
     get_method = 'tempest.lib.common.rest_client.RestClient.get'
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index d64d7b0..f801243 100755
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -120,6 +120,29 @@
                           waiters.wait_for_image_copied_to_stores,
                           self.client, 'fake_image_id')
 
+    def test_wait_for_image_tasks_status(self):
+        self.client.show_image_tasks.return_value = ({
+            'tasks': [{'status': 'success'}]})
+        start_time = int(time.time())
+        waiters.wait_for_image_tasks_status(
+            self.client, 'fake_image_id', 'success')
+        end_time = int(time.time())
+        # Ensure waiter returns before build_timeout
+        self.assertLess((end_time - start_time), 10)
+
+    def test_wait_for_image_tasks_status_timeout(self):
+        time_mock = self.patch('time.time')
+        self.patch('time.time', side_effect=[0., 1.])
+        time_mock.side_effect = utils.generate_timeout_series(1)
+
+        self.client.show_image_tasks.return_value = ({
+            'tasks': [
+                {'status': 'success'},
+                {'status': 'processing'}]})
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_image_tasks_status,
+                          self.client, 'fake_image_id', 'success')
+
 
 class TestInterfaceWaiters(base.TestCase):
 
diff --git a/tempest/tests/lib/common/test_preprov_creds.py b/tempest/tests/lib/common/test_preprov_creds.py
index fe7fcd2..f2131dc 100644
--- a/tempest/tests/lib/common/test_preprov_creds.py
+++ b/tempest/tests/lib/common/test_preprov_creds.py
@@ -144,8 +144,7 @@
         # Emulate the lock existing on the filesystem
         self.useFixture(fixtures.MockPatch(
             'os.path.isfile', return_value=True))
-        with mock.patch('six.moves.builtins.open', mock.mock_open(),
-                        create=True):
+        with mock.patch('builtins.open', mock.mock_open(), create=True):
             test_account_class = (
                 preprov_creds.PreProvisionedCredentialProvider(
                     **self.fixed_params))
@@ -157,8 +156,7 @@
         # Emulate the lock not existing on the filesystem
         self.useFixture(fixtures.MockPatch(
             'os.path.isfile', return_value=False))
-        with mock.patch('six.moves.builtins.open', mock.mock_open(),
-                        create=True):
+        with mock.patch('builtins.open', mock.mock_open(), create=True):
             test_account_class = (
                 preprov_creds.PreProvisionedCredentialProvider(
                     **self.fixed_params))
@@ -177,7 +175,7 @@
             'os.path.isfile', return_value=False))
         test_account_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
-        with mock.patch('six.moves.builtins.open', mock.mock_open(),
+        with mock.patch('builtins.open', mock.mock_open(),
                         create=True) as open_mock:
             test_account_class._get_free_hash(hash_list)
             lock_path = os.path.join(self.fixed_params['accounts_lock_dir'],
@@ -196,8 +194,7 @@
             'os.path.isfile', return_value=True))
         test_account_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
-        with mock.patch('six.moves.builtins.open', mock.mock_open(),
-                        create=True):
+        with mock.patch('builtins.open', mock.mock_open(), create=True):
             self.assertRaises(lib_exc.InvalidCredentials,
                               test_account_class._get_free_hash, hash_list)
 
@@ -217,7 +214,7 @@
             return True
 
         self.patchobject(os.path, 'isfile', _fake_is_file)
-        with mock.patch('six.moves.builtins.open', mock.mock_open(),
+        with mock.patch('builtins.open', mock.mock_open(),
                         create=True) as open_mock:
             test_account_class._get_free_hash(hash_list)
             lock_path = os.path.join(self.fixed_params['accounts_lock_dir'],
diff --git a/tempest/tests/lib/common/test_rest_client.py b/tempest/tests/lib/common/test_rest_client.py
index b861582..c5f6d7a 100644
--- a/tempest/tests/lib/common/test_rest_client.py
+++ b/tempest/tests/lib/common/test_rest_client.py
@@ -17,7 +17,6 @@
 import fixtures
 import jsonschema
 from oslo_serialization import jsonutils as json
-import six
 
 from tempest.lib.common import http
 from tempest.lib.common import rest_client
@@ -93,7 +92,7 @@
 class TestRestClientHeadersJSON(TestRestClientHTTPMethods):
 
     def _verify_headers(self, resp):
-        resp = dict((k.lower(), v) for k, v in six.iteritems(resp))
+        resp = dict((k.lower(), v) for k, v in resp.items())
         self.assertEqual(self.header_value, resp['accept'])
         self.assertEqual(self.header_value, resp['content-type'])
 
@@ -526,9 +525,11 @@
                           self.rest_client.wait_for_resource_deletion,
                           '1234')
 
-        # time.time() should be called twice, first to start the timer
-        # and then to compute the timedelta
-        self.assertEqual(2, time_mock.call_count)
+        # time.time() should be called 4 times,
+        # 1. Start timer
+        # 2. End timer
+        # 3 & 4. To generate timeout exception message
+        self.assertEqual(4, time_mock.call_count)
 
     def test_wait_for_deletion_with_unimplemented_deleted_method(self):
         self.rest_client.is_resource_deleted = self.original_deleted_method
diff --git a/tempest/tests/lib/services/compute/test_aggregates_client.py b/tempest/tests/lib/services/compute/test_aggregates_client.py
index 674d92a..1448a4d 100644
--- a/tempest/tests/lib/services/compute/test_aggregates_client.py
+++ b/tempest/tests/lib/services/compute/test_aggregates_client.py
@@ -37,7 +37,7 @@
     FAKE_CREATE_AGGREGATE = {
         "aggregate":
         {
-            "name": u'\xf4',
+            "name": '\xf4',
             "availability_zone": None,
             "deleted": False,
             "created_at": "2015-07-21T04:11:18.000000",
@@ -50,7 +50,7 @@
     FAKE_UPDATE_AGGREGATE = {
         "aggregate":
         {
-            "name": u'\xe9',
+            "name": '\xe9',
             "availability_zone": None,
             "deleted": False,
             "created_at": "2015-07-16T03:07:32.000000",
@@ -74,7 +74,7 @@
         "metadata": {
             "availability_zone": "nova"
         },
-        "name": u'\xe9',
+        "name": '\xe9',
         "updated_at": None
     }
 
diff --git a/tempest/tests/lib/services/compute/test_availability_zone_client.py b/tempest/tests/lib/services/compute/test_availability_zone_client.py
index 6608592..aef98fc 100644
--- a/tempest/tests/lib/services/compute/test_availability_zone_client.py
+++ b/tempest/tests/lib/services/compute/test_availability_zone_client.py
@@ -27,7 +27,7 @@
                     "available": True
                 },
                 "hosts": None,
-                "zoneName": u'\xf4'
+                "zoneName": '\xf4'
             }
         ]
     }
diff --git a/tempest/tests/lib/services/compute/test_extensions_client.py b/tempest/tests/lib/services/compute/test_extensions_client.py
index d7e217e..053b84c 100644
--- a/tempest/tests/lib/services/compute/test_extensions_client.py
+++ b/tempest/tests/lib/services/compute/test_extensions_client.py
@@ -27,7 +27,7 @@
             "namespace":
             "http://docs.openstack.org/compute/ext/multinic/api/v1.1",
             "alias": "NMN",
-            "description": u'\u2740(*\xb4\u25e1`*)\u2740'
+            "description": '\u2740(*\xb4\u25e1`*)\u2740'
         }
     }
 
diff --git a/tempest/tests/lib/services/compute/test_floating_ip_pools_client.py b/tempest/tests/lib/services/compute/test_floating_ip_pools_client.py
index b0c00f0..6278df4 100644
--- a/tempest/tests/lib/services/compute/test_floating_ip_pools_client.py
+++ b/tempest/tests/lib/services/compute/test_floating_ip_pools_client.py
@@ -22,8 +22,8 @@
     FAKE_FLOATING_IP_POOLS = {
         "floating_ip_pools":
         [
-            {"name": u'\u3042'},
-            {"name": u'\u3044'}
+            {"name": '\u3042'},
+            {"name": '\u3044'}
         ]
     }
 
diff --git a/tempest/tests/lib/services/compute/test_keypairs_client.py b/tempest/tests/lib/services/compute/test_keypairs_client.py
index ed3b9dd..214d0e5 100644
--- a/tempest/tests/lib/services/compute/test_keypairs_client.py
+++ b/tempest/tests/lib/services/compute/test_keypairs_client.py
@@ -23,7 +23,7 @@
 
     FAKE_KEYPAIR = {"keypair": {
         "public_key": "ssh-rsa foo Generated-by-Nova",
-        "name": u'\u2740(*\xb4\u25e1`*)\u2740',
+        "name": '\u2740(*\xb4\u25e1`*)\u2740',
         "user_id": "525d55f98980415ba98e634972fa4a10",
         "fingerprint": "76:24:66:49:d7:ca:6e:5c:77:ea:8e:bb:9c:15:5f:98"
         }}
diff --git a/tempest/tests/lib/services/compute/test_networks_client.py b/tempest/tests/lib/services/compute/test_networks_client.py
index 1908b57..5a88671 100644
--- a/tempest/tests/lib/services/compute/test_networks_client.py
+++ b/tempest/tests/lib/services/compute/test_networks_client.py
@@ -31,7 +31,7 @@
         "deleted_at": None,
         "gateway": None,
         "rxtx_base": None,
-        "label": u'30d7',
+        "label": '30d7',
         "priority": None,
         "project_id": None,
         "vpn_private_address": None,
diff --git a/tempest/tests/lib/services/compute/test_quota_classes_client.py b/tempest/tests/lib/services/compute/test_quota_classes_client.py
index 22d8b91..6921365 100644
--- a/tempest/tests/lib/services/compute/test_quota_classes_client.py
+++ b/tempest/tests/lib/services/compute/test_quota_classes_client.py
@@ -29,7 +29,7 @@
         "ram": 51200,
         "floating_ips": 10,
         "key_pairs": 100,
-        "id": u'\u2740(*\xb4\u25e1`*)\u2740',
+        "id": '\u2740(*\xb4\u25e1`*)\u2740',
         "instances": 10,
         "security_group_rules": 20,
         "security_groups": 10,
diff --git a/tempest/tests/lib/services/compute/test_tenant_networks_client.py b/tempest/tests/lib/services/compute/test_tenant_networks_client.py
index f71aad9..a042a1a 100644
--- a/tempest/tests/lib/services/compute/test_tenant_networks_client.py
+++ b/tempest/tests/lib/services/compute/test_tenant_networks_client.py
@@ -22,7 +22,7 @@
     FAKE_NETWORK = {
         "cidr": "None",
         "id": "c2329eb4-cc8e-4439-ac4c-932369309e36",
-        "label": u'\u30d7'
+        "label": '\u30d7'
         }
 
     FAKE_NETWORKS = [FAKE_NETWORK]
diff --git a/tempest/tests/lib/services/network/test_trunks_client.py b/tempest/tests/lib/services/network/test_trunks_client.py
new file mode 100644
index 0000000..b637d5e
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_trunks_client.py
@@ -0,0 +1,201 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.services.network import trunks_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTrunksClient(base.BaseServiceTest):
+
+    FAKE_TRUNK_ID = "dfbc2103-93cf-4edf-952a-ef6deb32ddc6"
+    FAKE_PORT_ID = "1f04eb36-6c84-11eb-b0ab-4fc62961629d"
+    FAKE_TRUNKS = {
+        "trunks": [
+            {
+                "admin_state_up": True,
+                "description": "",
+                "id": "dfbc2103-93cf-4edf-952a-ef6deb32ddc6",
+                "name": "trunk0",
+                "port_id": "00130aab-bb51-42a1-a7c4-6703a3a43aa5",
+                "project_id": "",
+                "revision_number": 2,
+                "status": "DOWN",
+                "sub_ports": [
+                    {
+                        "port_id": "87d2483d-e5e6-483d-b5f0-81b9ed1d1a91",
+                        "segmentation_id": 101,
+                        "segmentation_type": "vlan"
+                        }
+                    ],
+                "tags": [],
+            },
+            {
+                "admin_state_up": True,
+                "description": "",
+                "id": "9eb0e72e-11d3-4295-bcaf-6c89008d9f0a",
+                "name": "trunk1",
+                "port_id": "035a12bf-2ae3-42ae-8ad6-9f70640cddde",
+                "project_id": "",
+                "revision_number": 2,
+                "status": "DOWN",
+                "sub_ports": [
+                    {
+                        "port_id": "cba839d5-02e2-4e09-b964-81356da78165",
+                        "segmentation_id": 102,
+                        "segmentation_type": "vlan"
+                        }
+                    ],
+                "tags": [],
+            },
+        ]
+    }
+
+    FAKE_TRUNK_1 = {
+        "name": "trunk0",
+        "port_id": "00130aab-bb51-42a1-a7c4-6703a3a43aa5"
+    }
+
+    def setUp(self):
+        super(TestTrunksClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.trunks_client = trunks_client.TrunksClient(
+            fake_auth, "network", "regionOne")
+
+    def _test_create_trunk(self, bytes_body=False):
+        self.check_service_client_function(
+            self.trunks_client.create_trunk,
+            "tempest.lib.common.rest_client.RestClient.post",
+            {"trunk": self.FAKE_TRUNKS["trunks"][0]},
+            bytes_body,
+            201,
+            **self.FAKE_TRUNK_1)
+
+    def _test_list_trunks(self, bytes_body=False):
+        self.check_service_client_function(
+            self.trunks_client.list_trunks,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_TRUNKS,
+            bytes_body,
+            200)
+
+    def _test_show_trunk(self, bytes_body=False):
+        self.check_service_client_function(
+            self.trunks_client.show_trunk,
+            "tempest.lib.common.rest_client.RestClient.get",
+            {"trunk": self.FAKE_TRUNKS["trunks"][0]},
+            bytes_body,
+            200,
+            trunk_id=self.FAKE_TRUNK_ID)
+
+    def _test_update_trunk(self, bytes_body=False):
+        update_kwargs = {
+            "admin_state_up": True,
+            "name": "new_trunk"
+        }
+
+        resp_body = {
+            "trunk": copy.deepcopy(
+                self.FAKE_TRUNKS["trunks"][0]
+            )
+        }
+        resp_body["trunk"].update(update_kwargs)
+
+        self.check_service_client_function(
+            self.trunks_client.update_trunk,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            trunk_id=self.FAKE_TRUNK_ID,
+            **update_kwargs)
+
+    def _test_add_subports_to_trunk(self, bytes_body=False):
+        sub_ports = [{
+            "port_id": "f04eb36-6c84-11eb-b0ab-4fc62961629d",
+            "segmentation_type": "vlan",
+            "segmentation_id": "1001"
+        }]
+        resp_body = copy.deepcopy(self.FAKE_TRUNKS["trunks"][0])
+
+        resp_body["sub_ports"].append(sub_ports)
+        self.check_service_client_function(
+            self.trunks_client.add_subports_to_trunk,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            trunk_id=self.FAKE_TRUNK_ID,
+            sub_ports=sub_ports)
+
+    def _test_delete_subports_from_trunk(self, bytes_body=False):
+        fake_sub_ports = self.FAKE_TRUNKS['trunks'][0]['sub_ports']
+        sub_ports = [
+            {"port_id": fake_sub_ports[0]['port_id']}
+        ]
+        resp_body = copy.deepcopy(self.FAKE_TRUNKS["trunks"][0])
+
+        resp_body['sub_ports'] = []
+        self.check_service_client_function(
+            self.trunks_client.delete_subports_from_trunk,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            trunk_id=self.FAKE_TRUNK_ID,
+            sub_ports=sub_ports)
+
+    def test_create_trunk_with_str_body(self):
+        self._test_create_trunk()
+
+    def test_create_trunk_with_bytes_body(self):
+        self._test_create_trunk(bytes_body=True)
+
+    def test_list_trunks_with_str_body(self):
+        self._test_list_trunks()
+
+    def test_list_trunks_with_bytes_body(self):
+        self._test_list_trunks(bytes_body=True)
+
+    def test_show_trunk_with_str_body(self):
+        self._test_show_trunk()
+
+    def test_show_trunk_with_bytes_body(self):
+        self._test_show_trunk(bytes_body=True)
+
+    def test_update_trunk_with_str_body(self):
+        self._test_update_trunk()
+
+    def test_update_trunk_with_bytes_body(self):
+        self._test_update_trunk(bytes_body=True)
+
+    def test_add_subports_to_trunk_str_body(self):
+        self._test_add_subports_to_trunk()
+
+    def test_add_subports_to_trunk_bytes_body(self):
+        self._test_add_subports_to_trunk(bytes_body=True)
+
+    def test_delete_subports_from_trunk_str_body(self):
+        self._test_delete_subports_from_trunk()
+
+    def test_delete_subports_from_trunk_bytes_body(self):
+        self._test_delete_subports_from_trunk(bytes_body=True)
+
+    def test_delete_trunk(self):
+        self.check_service_client_function(
+            self.trunks_client.delete_trunk,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            trunk_id=self.FAKE_TRUNK_ID)
diff --git a/tempest/tests/lib/services/placement/test_resource_providers_client.py b/tempest/tests/lib/services/placement/test_resource_providers_client.py
index 485f584..2871395 100644
--- a/tempest/tests/lib/services/placement/test_resource_providers_client.py
+++ b/tempest/tests/lib/services/placement/test_resource_providers_client.py
@@ -85,6 +85,14 @@
         },
         "resource_provider_generation": 1
     }
+    FAKE_RESOURCE_PROVIDER_USAGES = {
+        "usages": {
+            "VCPU": 2,
+            "MEMORY_MB": 1024,
+            "DISK_GB": 10
+        },
+        "resource_provider_generation": 3
+    }
 
     def setUp(self):
         super(TestResourceProvidersClient, self).setUp()
@@ -180,3 +188,19 @@
 
     def test_list_resource_provider_aggregates_with_bytes_body(self):
         self._test_list_resource_provider_aggregates(bytes_body=True)
+
+    def _test_list_resource_provider_usages(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_resource_provider_usages,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_RESOURCE_PROVIDER_USAGES,
+            to_utf=bytes_body,
+            status=200,
+            rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID
+        )
+
+    def test_show_resource_provider_usages_with_str_body(self):
+        self._test_list_resource_provider_inventories()
+
+    def test_show_resource_provider_usages_with_with_bytes_body(self):
+        self._test_list_resource_provider_inventories(bytes_body=True)
diff --git a/tempest/tests/lib/services/test_clients.py b/tempest/tests/lib/services/test_clients.py
index f83064a..6c79db6 100644
--- a/tempest/tests/lib/services/test_clients.py
+++ b/tempest/tests/lib/services/test_clients.py
@@ -16,7 +16,6 @@
 from unittest import mock
 
 import fixtures
-import six
 import testtools
 
 from tempest.lib import auth
@@ -270,8 +269,7 @@
                           'module_path': 'This neither',
                           'client_names': ['SomeClient1']}]}
         msg = "(?=.*{0})(?=.*{1})".format(
-            *[x[1][0]['module_path'] for x in six.iteritems(
-                fake_service_clients)])
+            *[x[1][0]['module_path'] for x in fake_service_clients.items()])
         self.useFixture(fixtures.MockPatchObject(
             clients.ClientsRegistry(), 'get_service_clients',
             return_value=fake_service_clients))
@@ -300,8 +298,8 @@
                           'module_path': 'fake_path_2',
                           'client_names': ['SomeClient2']}]}
         msg = "(?=.*{0})(?=.*{1})".format(
-            *[x[1][0]['service_version'] for x in six.iteritems(
-                fake_service_clients)])
+            *[x[1][0]['service_version'] for x in
+                fake_service_clients.items()])
         self.useFixture(fixtures.MockPatchObject(
             clients.ClientsRegistry(), 'get_service_clients',
             return_value=fake_service_clients))
diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py
index 035bdb0..886d99c 100644
--- a/tempest/tests/lib/test_ssh.py
+++ b/tempest/tests/lib/test_ssh.py
@@ -16,7 +16,6 @@
 import socket
 from unittest import mock
 
-import six
 import testtools
 
 from tempest.lib.common import ssh
@@ -240,7 +239,7 @@
 
         return chan_mock, poll_mock, select_mock, client_mock
 
-    _utf8_string = six.unichr(1071)
+    _utf8_string = chr(1071)
     _utf8_bytes = _utf8_string.encode("utf-8")
 
     @mock.patch('select.POLLIN', SELECT_POLLIN, create=True)
diff --git a/tools/check_logs.py b/tools/check_logs.py
index cc74b17..8ab3af2 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -23,7 +23,6 @@
 import sys
 import urllib.request as urlreq
 
-import six
 import yaml
 
 # DEVSTACK_GATE_GRENADE is either unset if grenade is not running
@@ -137,7 +136,7 @@
     with open(ALLOW_LIST_FILE) as stream:
         loaded = yaml.safe_load(stream)
         if loaded:
-            for (name, l) in six.iteritems(loaded):
+            for (name, l) in loaded.values():
                 for w in l:
                     assert 'module' in w, 'no module in %s' % name
                     assert 'message' in w, 'no message in %s' % name
diff --git a/tox.ini b/tox.ini
index d4789a5..cd32174 100644
--- a/tox.ini
+++ b/tox.ini
@@ -11,7 +11,7 @@
     VIRTUAL_ENV={envdir}
     OS_TEST_PATH=./tempest/test_discover
 deps =
-    -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/wallaby}
+    -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
     -r{toxinidir}/requirements.txt
 
 [testenv]
@@ -28,7 +28,7 @@
 install_command = pip install {opts} {packages}
 allowlist_externals = *
 deps =
-    -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/wallaby}
+    -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
     -r{toxinidir}/requirements.txt
     -r{toxinidir}/test-requirements.txt
 commands =
@@ -263,7 +263,7 @@
 
 [testenv:venv]
 deps =
-  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/wallaby}
+  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/requirements.txt
   -r{toxinidir}/doc/requirements.txt
 commands = {posargs}
@@ -278,7 +278,7 @@
 
 [testenv:docs]
 deps =
-  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/wallaby}
+  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/doc/requirements.txt
 commands =
   sphinx-apidoc -f -o doc/source/tests/compute tempest/api/compute
@@ -363,7 +363,7 @@
 
 [testenv:releasenotes]
 deps =
-  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/wallaby}
+  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/doc/requirements.txt
 commands =
   rm -rf releasenotes/build
@@ -409,3 +409,15 @@
 allowlist_externals = bash
 commands =
   bash tools/tempest-plugin-sanity.sh
+
+[testenv:stestr-master]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+basepython = {[tempestenv]basepython}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+# The below command install stestr master version and run smoke tests
+commands =
+    find . -type f -name "*.pyc" -delete
+    pip install -U git+https://github.com/mtreinish/stestr
+    tempest run --regex '\[.*\bsmoke\b.*\]' {posargs}
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index 5a14430..b83eb34 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -302,6 +302,7 @@
       devstack_services:
         neutron-placement: true
         neutron-qos: true
+        neutron-trunk: true
     group-vars:
       subnode:
         devstack_localrc:
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index d5b2787..698df53 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -3,7 +3,7 @@
       - check-requirements
       - integrated-gate-py3
       - openstack-cover-jobs
-      - openstack-python3-victoria-jobs
+      - openstack-python3-xena-jobs
       - publish-openstack-docs-pti
       - release-notes-jobs-python3
     check:
@@ -31,6 +31,8 @@
         - glance-multistore-cinder-import:
             voting: false
             irrelevant-files: *tempest-irrelevant-files
+        - tempest-full-wallaby-py3:
+            irrelevant-files: *tempest-irrelevant-files
         - tempest-full-victoria-py3:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-ussuri-py3:
@@ -107,6 +109,7 @@
             irrelevant-files: *tempest-irrelevant-files
     experimental:
       jobs:
+        - tempest-stestr-master
         - tempest-cinder-v2-api:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-all:
@@ -123,6 +126,7 @@
             irrelevant-files: *tempest-irrelevant-files
     periodic-stable:
       jobs:
+        - tempest-full-wallaby-py3
         - tempest-full-victoria-py3
         - tempest-full-ussuri-py3
         - tempest-full-train-py3
@@ -130,3 +134,4 @@
       jobs:
         - tempest-all
         - tempest-full-oslo-master
+        - tempest-stestr-master
diff --git a/zuul.d/stable-jobs.yaml b/zuul.d/stable-jobs.yaml
index 769b280..2f0df66 100644
--- a/zuul.d/stable-jobs.yaml
+++ b/zuul.d/stable-jobs.yaml
@@ -1,5 +1,10 @@
 # NOTE(gmann): This file includes all stable release jobs definition.
 - job:
+    name: tempest-full-wallaby-py3
+    parent: tempest-full-py3
+    override-checkout: stable/wallaby
+
+- job:
     name: tempest-full-victoria-py3
     parent: tempest-full-py3
     override-checkout: stable/victoria
diff --git a/zuul.d/tempest-specific.yaml b/zuul.d/tempest-specific.yaml
index fd348cc..5063d89 100644
--- a/zuul.d/tempest-specific.yaml
+++ b/zuul.d/tempest-specific.yaml
@@ -113,3 +113,19 @@
     vars:
       devstack_localrc:
         TEMPEST_HAS_ADMIN: False
+
+- job:
+    name: tempest-stestr-master
+    parent: devstack-tempest
+    description: |
+      Smoke integration test with stestr master.
+      This ensures that new stestr release does
+      not break Temepst.
+    vars:
+      tox_envlist: stestr-master
+      devstack_services:
+        s-account: false
+        s-container: false
+        s-object: false
+        s-proxy: false
+        c-bak: false