Merge "tempest cleanup - use admin_mgr only"
diff --git a/.zuul.yaml b/.zuul.yaml
index d893483..d729f69 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -100,6 +100,13 @@
       devstack_localrc:
         ENABLE_FILE_INJECTION: true
         ENABLE_VOLUME_MULTIATTACH: true
+      devstack_services:
+        # NOTE(mriedem): Disable the cinder-backup service from tempest-full
+        # since tempest-full is in the integrated-gate project template but
+        # the backup tests do not really involve other services so they should
+        # be run in some more cinder-specific job, especially because the
+        # tests fail at a high rate (see bugs 1483434, 1813217, 1745168)
+        c-bak: false
 
 - job:
     name: tempest-full-oslo-master
@@ -169,6 +176,12 @@
         s-object: false
         s-proxy: false
         # without Swift, c-bak cannot run (in the Gate at least)
+        # NOTE(mriedem): Disable the cinder-backup service from
+        # tempest-full-py3 since tempest-full-py3 is in the integrated-gate-py3
+        # project template but the backup tests do not really involve other
+        # services so they should be run in some more cinder-specific job,
+        # especially because the tests fail at a high rate (see bugs 1483434,
+        # 1813217, 1745168)
         c-bak: false
 
 - job:
@@ -345,8 +358,16 @@
     parent: tempest-full-py3
     nodeset: devstack-single-node-opensuse-150
     description: |
+      ** DEPRECATED - please use tempest-full-py3-opensuse15 instead.
+    voting: false
+
+- job:
+    name: tempest-full-py3-opensuse15
+    parent: tempest-full-py3
+    nodeset: devstack-single-node-opensuse-15
+    description: |
       Base integration test with Neutron networking and py36 running
-      on openSUSE Leap 15.0
+      on openSUSE Leap 15.x
     voting: false
 
 - job:
@@ -405,6 +426,16 @@
         c-bak: false
 
 - job:
+    name: tempest-full-train
+    parent: tempest-full
+    override-checkout: stable/train
+
+- job:
+    name: tempest-full-train-py3
+    parent: tempest-full-py3
+    override-checkout: stable/train
+
+- job:
     name: tempest-full-stein
     parent: tempest-full
     override-checkout: stable/stein
@@ -622,6 +653,10 @@
         - tempest-full-py3-ipv6:
             voting: false
             irrelevant-files: *tempest-irrelevant-files
+        - tempest-full-train:
+            irrelevant-files: *tempest-irrelevant-files
+        - tempest-full-train-py3:
+            irrelevant-files: *tempest-irrelevant-files
         - tempest-full-stein:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-stein-py3:
@@ -727,10 +762,12 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-pg-full:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-py3-opensuse150:
+        - tempest-full-py3-opensuse15:
             irrelevant-files: *tempest-irrelevant-files
     periodic-stable:
       jobs:
+        - tempest-full-train
+        - tempest-full-train-py3
         - tempest-full-stein
         - tempest-full-stein-py3
         - tempest-full-rocky
diff --git a/releasenotes/notes/fix-1847749-2670b1d4f6097a1a.yaml b/releasenotes/notes/fix-1847749-2670b1d4f6097a1a.yaml
new file mode 100644
index 0000000..4842f63
--- /dev/null
+++ b/releasenotes/notes/fix-1847749-2670b1d4f6097a1a.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    Bug#1847749. This provides the workaround of Skip Exception raised instead of skipping
+    the CLI tests. If you are running Tempest with stestr > 2.5.0 then use this fix.
+    Ref- https://github.com/testing-cabal/testtools/issues/272
diff --git a/releasenotes/notes/fix-credential-logging-98089c897d801355.yaml b/releasenotes/notes/fix-credential-logging-98089c897d801355.yaml
new file mode 100644
index 0000000..9534a72
--- /dev/null
+++ b/releasenotes/notes/fix-credential-logging-98089c897d801355.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    A new kwarg, ``log_req_body``, was added to the
+    ``tempest.lib.common.rest_client.RestClient`` method ``raw_request()``.
+    This kwarg takes in a string which will be used in place of the request
+    body, which is logged by default. The intent of this option is to be used
+    for security reasons to avoid logging sensitive information that are part
+    of request bodies.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 1d0d914..43c102c 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v22.0.0
    v21.0.0
    v20.0.0
    v19.0.0
diff --git a/releasenotes/source/v22.0.0.rst b/releasenotes/source/v22.0.0.rst
new file mode 100644
index 0000000..519b081
--- /dev/null
+++ b/releasenotes/source/v22.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v22.0.0 Release Notes
+=====================
+
+.. release-notes:: 22.0.0 Release Notes
+   :version: 22.0.0
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index c938cee..5a27a43 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -57,6 +57,13 @@
         self.assertIn('status', image)
         self.assertEqual('queued', image['status'])
 
+        # NOTE: This Glance API returns different status codes for image
+        # condition. In this empty data case, Glance should return 204,
+        # so here should check the status code.
+        image_file = self.client.show_image_file(image['id'])
+        self.assertEqual(0, len(image_file.data))
+        self.assertEqual(204, image_file.response.status)
+
         # Now try uploading an image file
         file_content = data_utils.random_bytes()
         image_file = six.BytesIO(file_content)
@@ -115,17 +122,6 @@
                                   visibility='private')
         self.assertEqual('queued', image['status'])
 
-        # NOTE: This Glance API returns different status codes for image
-        # condition. In this empty data case, Glance should return 204,
-        # so here should check the status code.
-        image_file = self.client.show_image_file(image['id'])
-        self.assertEqual(0, len(image_file.data))
-        self.assertEqual(204, image_file.response.status)
-
-        # Now try uploading an image file
-        image_file = six.BytesIO(data_utils.random_bytes())
-        self.client.store_image_file(image['id'], image_file)
-
         # Update Image
         new_image_name = data_utils.rand_name('new-image')
         self.client.update_image(image['id'], [
diff --git a/tempest/config.py b/tempest/config.py
index d67d3e0..32cebc5 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -667,7 +667,7 @@
                default=28,
                help="The mask bits for project ipv4 subnets"),
     cfg.StrOpt('project_network_v6_cidr',
-               default="2003::/48",
+               default="2001:db8::/48",
                help="The cidr block to allocate project ipv6 subnets from"),
     cfg.IntOpt('project_network_v6_mask_bits',
                default=64,
diff --git a/tempest/lib/base.py b/tempest/lib/base.py
index 3be55c0..74ae77c 100644
--- a/tempest/lib/base.py
+++ b/tempest/lib/base.py
@@ -14,11 +14,29 @@
 #    under the License.
 
 import os
+import sys
 
 import fixtures
+import pkg_resources
 import testtools
 
 
+def _handle_skip_exception():
+    try:
+        stestr_version = pkg_resources.parse_version(
+            pkg_resources.get_distribution("stestr").version)
+        stestr_min = pkg_resources.parse_version('2.5.0')
+        new_stestr = (stestr_version >= stestr_min)
+        import unittest
+        import unittest2
+        if sys.version_info >= (3, 5) and new_stestr:
+            testtools.TestCase.skipException = unittest.case.SkipTest
+        else:
+            testtools.TestCase.skipException = unittest2.case.SkipTest
+    except Exception:
+        pass
+
+
 class BaseTestCase(testtools.testcase.WithAttributes, testtools.TestCase):
     setUpClassCalled = False
 
@@ -33,6 +51,18 @@
         if hasattr(super(BaseTestCase, cls), 'setUpClass'):
             super(BaseTestCase, cls).setUpClass()
         cls.setUpClassCalled = True
+        # TODO(gmann): cls.handle_skip_exception is really workaround for
+        # testtools bug- https://github.com/testing-cabal/testtools/issues/272
+        # stestr which is used by Tempest internally to run the test switch
+        # the customize test runner(which use stdlib unittest) for >=py3.5
+        # else testtools.run.- https://github.com/mtreinish/stestr/pull/265
+        # These two test runner are not compatible due to skip exception
+        # handling(due to unittest2). testtools.run treat unittestt.SkipTest
+        # as error and stdlib unittest treat unittest2.case.SkipTest raised
+        # by testtools.TestCase.skipException.
+        # The below workaround can be removed once testtools fix issue# 272.
+        cls.orig_skip_exception = testtools.TestCase.skipException
+        _handle_skip_exception()
 
     @classmethod
     def tearDownClass(cls):
@@ -40,6 +70,7 @@
             super(BaseTestCase, cls).tearDownClass()
 
     def setUp(self):
+        testtools.TestCase.skipException = self.orig_skip_exception
         super(BaseTestCase, self).setUp()
         if not self.setUpClassCalled:
             raise RuntimeError("setUpClass does not calls the super's "
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 6cc0561..431a0a0 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -556,7 +556,8 @@
 
         return resp, resp_body
 
-    def raw_request(self, url, method, headers=None, body=None, chunked=False):
+    def raw_request(self, url, method, headers=None, body=None, chunked=False,
+                    log_req_body=None):
         """Send a raw HTTP request without the keystone catalog or auth
 
         This method sends a HTTP request in the same manner as the request()
@@ -572,6 +573,11 @@
                              explicitly requires no headers use an empty dict.
         :param str body: Body to send with the request
         :param bool chunked: sends the body with chunked encoding
+        :param str log_req_body: Whether to log the request body or not.
+                                 It is default to None which means request
+                                 body is safe to log otherwise pass any string
+                                 you want to log in place of request body.
+                                 For example: '<omitted>'
         :rtype: tuple
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
@@ -585,8 +591,9 @@
             url, method, headers=headers,
             body=body, chunked=chunked)
         end = time.time()
+        req_body = body if log_req_body is None else log_req_body
         self._log_request(method, url, resp, secs=(end - start),
-                          req_headers=headers, req_body=body,
+                          req_headers=headers, req_body=req_body,
                           resp_body=resp_body)
         return resp, resp_body
 
diff --git a/tempest/lib/services/compute/versions_client.py b/tempest/lib/services/compute/versions_client.py
index 8fbb136..c6e1783 100644
--- a/tempest/lib/services/compute/versions_client.py
+++ b/tempest/lib/services/compute/versions_client.py
@@ -12,8 +12,6 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import time
-
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.api_schema.response.compute.v2_1 import versions as schema
@@ -26,11 +24,7 @@
     def list_versions(self):
         version_url = self._get_base_version_url()
 
-        start = time.time()
         resp, body = self.raw_request(version_url, 'GET')
-        end = time.time()
-        self._log_request('GET', version_url, resp, secs=(end - start),
-                          resp_body=body)
 
         self._error_checker(resp, body)
         body = json.loads(body)
diff --git a/tempest/lib/services/identity/v2/token_client.py b/tempest/lib/services/identity/v2/token_client.py
index 458c862..9f10f58 100644
--- a/tempest/lib/services/identity/v2/token_client.py
+++ b/tempest/lib/services/identity/v2/token_client.py
@@ -105,9 +105,8 @@
                 headers = self.get_headers(accept_type="json")
 
         resp, resp_body = self.raw_request(url, method,
-                                           headers=headers, body=body)
-        self._log_request(method, url, resp, req_headers=headers,
-                          req_body='<omitted>', resp_body=resp_body)
+                                           headers=headers, body=body,
+                                           log_req_body='<omitted>')
 
         if resp.status in [401, 403]:
             resp_body = json.loads(resp_body)
diff --git a/tempest/lib/services/identity/v3/token_client.py b/tempest/lib/services/identity/v3/token_client.py
index d591f03..6956297 100644
--- a/tempest/lib/services/identity/v3/token_client.py
+++ b/tempest/lib/services/identity/v3/token_client.py
@@ -160,10 +160,8 @@
                 headers = self.get_headers(accept_type="json")
 
         resp, resp_body = self.raw_request(url, method,
-                                           headers=headers, body=body)
-        self._log_request(method, url, resp, req_headers=headers,
-                          req_body='<omitted>', resp_body=resp_body)
-
+                                           headers=headers, body=body,
+                                           log_req_body='<omitted>')
         if resp.status in [401, 403]:
             resp_body = json.loads(resp_body)
             raise exceptions.Unauthorized(resp_body['error']['message'])
diff --git a/tempest/lib/services/identity/v3/versions_client.py b/tempest/lib/services/identity/v3/versions_client.py
index 441ee0d..f3a8986 100644
--- a/tempest/lib/services/identity/v3/versions_client.py
+++ b/tempest/lib/services/identity/v3/versions_client.py
@@ -12,8 +12,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import time
-
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.common import rest_client
@@ -26,11 +24,7 @@
         """List API versions"""
         version_url = self._get_base_version_url()
 
-        start = time.time()
         resp, body = self.raw_request(version_url, 'GET')
-        end = time.time()
-        self._log_request('GET', version_url, resp, secs=(end - start),
-                          resp_body=body)
         self._error_checker(resp, body)
 
         self.expected_success(300, resp.status)
diff --git a/tempest/lib/services/image/v2/versions_client.py b/tempest/lib/services/image/v2/versions_client.py
index 1adc466..1b7f806 100644
--- a/tempest/lib/services/image/v2/versions_client.py
+++ b/tempest/lib/services/image/v2/versions_client.py
@@ -12,8 +12,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import time
-
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.common import rest_client
@@ -26,11 +24,7 @@
         """List API versions"""
         version_url = self._get_base_version_url()
 
-        start = time.time()
         resp, body = self.raw_request(version_url, 'GET')
-        end = time.time()
-        self._log_request('GET', version_url, resp, secs=(end - start),
-                          resp_body=body)
         self._error_checker(resp, body)
 
         self.expected_success(300, resp.status)
diff --git a/tempest/lib/services/volume/v3/versions_client.py b/tempest/lib/services/volume/v3/versions_client.py
index 175f1f5..aa6c867 100644
--- a/tempest/lib/services/volume/v3/versions_client.py
+++ b/tempest/lib/services/volume/v3/versions_client.py
@@ -12,8 +12,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import time
-
 from six.moves.urllib.parse import urljoin
 
 from oslo_serialization import jsonutils as json
@@ -34,14 +32,10 @@
         """
         version_url = self._get_base_version_url()
 
-        start = time.time()
         resp, body = self.raw_request(version_url, 'GET')
-        end = time.time()
         # NOTE: We need a raw_request() here instead of request() call because
         # "list API versions" API doesn't require an authentication and we can
         # skip it with raw_request() call.
-        self._log_request('GET', version_url, resp, secs=(end - start),
-                          resp_body=body)
         self._error_checker(resp, body)
 
         body = json.loads(body)
diff --git a/tempest/test.py b/tempest/test.py
index 1e5cd19..f383bc1 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 pkg_resources
 import six
 import testtools
 
@@ -28,6 +27,7 @@
 from tempest.common import credentials_factory as credentials
 from tempest.common import utils
 from tempest import config
+from tempest.lib import base as lib_base
 from tempest.lib.common import fixed_network
 from tempest.lib.common import profiler
 from tempest.lib.common import validation_resources as vr
@@ -78,10 +78,6 @@
 atexit.register(validate_tearDownClass)
 
 
-class DummyException(Exception):
-    pass
-
-
 class BaseTestCase(testtools.testcase.WithAttributes,
                    testtools.TestCase):
     """The test base class defines Tempest framework for class level fixtures.
@@ -145,26 +141,6 @@
         cls._teardowns = []
 
     @classmethod
-    def handle_skip_exception(cls):
-        try:
-            stestr_version = pkg_resources.parse_version(
-                pkg_resources.get_distribution("stestr").version)
-            stestr_min = pkg_resources.parse_version('2.5.0')
-            new_stestr = (stestr_version >= stestr_min)
-            import unittest
-            import unittest2
-            if sys.version_info >= (3, 5) and new_stestr:
-                exc = unittest2.case.SkipTest
-                exc_to_raise = unittest.case.SkipTest
-            else:
-                exc = unittest.case.SkipTest
-                exc_to_raise = unittest2.case.SkipTest
-        except Exception:
-            exc = DummyException
-            exc_to_raise = DummyException
-        return exc, exc_to_raise
-
-    @classmethod
     def setUpClass(cls):
         cls.__setupclass_called = True
         # Reset state
@@ -183,8 +159,9 @@
         # as error and stdlib unittest treat unittest2.case.SkipTest raised
         # by testtools.TestCase.skipException.
         # The below workaround can be removed once testtools fix issue# 272.
+        orig_skip_exception = testtools.TestCase.skipException
+        lib_base._handle_skip_exception()
         try:
-            exc, exc_to_raise = cls.handle_skip_exception()
             cls.skip_checks()
 
             if not cls.__skip_checks_called:
@@ -202,12 +179,6 @@
             # Additional class-wide test resources
             cls._teardowns.append(('resources', cls.resource_cleanup))
             cls.resource_setup()
-        except exc as e:
-            # NOTE(dviroel): the exception may be raised after setting up the
-            # user credentials, so we must call tearDownClass to release all
-            # allocated resources.
-            cls.tearDownClass()
-            raise exc_to_raise(e.args)
         except Exception:
             etype, value, trace = sys.exc_info()
             LOG.info("%s raised in %s.setUpClass. Invoking tearDownClass.",
@@ -217,6 +188,8 @@
                 six.reraise(etype, value, trace)
             finally:
                 del trace  # to avoid circular refs
+        finally:
+            testtools.TestCase.skipException = orig_skip_exception
 
     @classmethod
     def tearDownClass(cls):
diff --git a/tempest/tests/lib/fake_identity.py b/tempest/tests/lib/fake_identity.py
index 8bae34f..9d7b0fd 100644
--- a/tempest/tests/lib/fake_identity.py
+++ b/tempest/tests/lib/fake_identity.py
@@ -192,7 +192,7 @@
 
 
 def _fake_v3_response(self, uri, method="GET", body=None, headers=None,
-                      redirections=5, connection_type=None):
+                      redirections=5, connection_type=None, log_req_body=None):
     fake_headers = {
         "x-subject-token": TOKEN
     }
@@ -202,7 +202,7 @@
 
 def _fake_v3_response_domain_scope(self, uri, method="GET", body=None,
                                    headers=None, redirections=5,
-                                   connection_type=None):
+                                   connection_type=None, log_req_body=None):
     fake_headers = {
         "status": "201",
         "x-subject-token": TOKEN
@@ -213,7 +213,7 @@
 
 def _fake_v3_response_no_scope(self, uri, method="GET", body=None,
                                headers=None, redirections=5,
-                               connection_type=None):
+                               connection_type=None, log_req_body=None):
     fake_headers = {
         "status": "201",
         "x-subject-token": TOKEN
@@ -223,7 +223,7 @@
 
 
 def _fake_v2_response(self, uri, method="GET", body=None, headers=None,
-                      redirections=5, connection_type=None):
+                      redirections=5, connection_type=None, log_req_body=None):
     return (fake_http.fake_http_response({}, status=200),
             json.dumps(IDENTITY_V2_RESPONSE))
 
diff --git a/tempest/tests/lib/services/identity/v2/test_token_client.py b/tempest/tests/lib/services/identity/v2/test_token_client.py
index a592ada..5b4e210 100644
--- a/tempest/tests/lib/services/identity/v2/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -86,6 +86,9 @@
         with mock.patch.object(token_client_v2, 'raw_request') as mock_raw_r:
             mock_raw_r.return_value = response, body
             resp, body = token_client_v2.request('GET', 'fake_uri')
+        mock_raw_r.assert_called_once_with('fake_uri', 'GET',
+                                           headers=mock.ANY, body=None,
+                                           log_req_body='<omitted>')
         self.assertIsInstance(body, dict)
 
     def test_request_with_bytes_body(self):
diff --git a/tempest/tests/lib/services/identity/v3/test_token_client.py b/tempest/tests/lib/services/identity/v3/test_token_client.py
index a9c58df..656e10a 100644
--- a/tempest/tests/lib/services/identity/v3/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_token_client.py
@@ -136,6 +136,9 @@
             mock_raw_r.return_value = (
                 fake_identity._fake_v3_response(None, None))
             resp, body = token_client_v3.request('GET', 'fake_uri')
+        mock_raw_r.assert_called_once_with('fake_uri', 'GET',
+                                           headers=mock.ANY, body=None,
+                                           log_req_body='<omitted>')
 
         self.assertIsInstance(body, dict)
 
diff --git a/tempest/tests/lib/services/identity/v3/test_users_client.py b/tempest/tests/lib/services/identity/v3/test_users_client.py
index d030c5e..c0dfdae 100644
--- a/tempest/tests/lib/services/identity/v3/test_users_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_users_client.py
@@ -25,6 +25,11 @@
             'enabled': True,
             'name': 'Tempest User',
             'password': 'TempestPassword',
+            "description": "Tempest User",
+            "email": "TempestUser@example.com",
+            "options": {
+                "ignore_password_expiry": True
+            }
         }
     }
 
diff --git a/tempest/tests/lib/services/volume/v3/test_backups_client.py b/tempest/tests/lib/services/volume/v3/test_backups_client.py
index 5412064..97e1132 100644
--- a/tempest/tests/lib/services/volume/v3/test_backups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_backups_client.py
@@ -60,8 +60,11 @@
                 ],
                 "name": "backup001",
                 "object_count": 22,
+                "os-backup-project-attr:project_id": "2c67a14be9314c5dae2ee6",
+                "user_id": "515ba0dd59f84f25a6a084a45d8d93b2",
                 "size": 1,
                 "status": "available",
+                "updated_at": "2013-04-02T10:35:27.000000",
                 "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
                 "is_incremental": True,
                 "has_dependent_backups": False
@@ -73,7 +76,16 @@
         "backup": {
             "id": "4c65c15f-a5c5-464b-b92a-90e4c04636a7",
             "name": "fake-backup-name",
-            "links": "fake-links"
+            "links": [
+                {
+                    "href": "fake-url-1",
+                    "rel": "self"
+                },
+                {
+                    "href": "fake-url-2",
+                    "rel": "bookmark"
+                }
+            ]
         }
     }
 
diff --git a/tempest/tests/lib/services/volume/v3/test_volumes_client.py b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
index 1250536..56c1a35 100644
--- a/tempest/tests/lib/services/volume/v3/test_volumes_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
@@ -24,27 +24,25 @@
 
     FAKE_VOLUME_SUMMARY = {
         "volume-summary": {
-            "total_size": 20,
-            "total_count": 5
+            "total_size": 4,
+            "total_count": 4,
+            "metadata": {
+                "key1": ["value1", "value2"],
+                "key2": ["value2"]
+            }
         }
     }
 
     FAKE_VOLUME_METADATA_ITEM = {
         "meta": {
-            "key1": "value1"
+            "name": "metadata1"
         }
     }
 
     FAKE_VOLUME_IMAGE_METADATA = {
         "metadata": {
-            "container_format": "bare",
-            "min_ram": "0",
-            "disk_format": "raw",
-            "image_name": "xly-ubuntu16-server",
-            "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
-            "checksum": "008f5d22fe3cb825d714da79607a90f9",
-            "min_disk": "0",
-            "size": "8589934592"
+            "key1": "value1",
+            "key2": "value2"
         }
     }
 
diff --git a/tempest/tests/lib/test_base.py b/tempest/tests/lib/test_base.py
index 27cda1a..2c16e1c 100644
--- a/tempest/tests/lib/test_base.py
+++ b/tempest/tests/lib/test_base.py
@@ -48,6 +48,7 @@
     @classmethod
     def setUpClass(cls):  # noqa
         """Simulate absence of super() call."""
+        cls.orig_skip_exception = cls.skipException
 
     def setUp(self):
         try:
diff --git a/tempest/tests/test_test.py b/tempest/tests/test_test.py
index a2e0efd..49fd010 100644
--- a/tempest/tests/test_test.py
+++ b/tempest/tests/test_test.py
@@ -531,8 +531,8 @@
     def test_skip_only(self):
         # If a skip condition is hit in the test, no credentials or resource
         # is provisioned / cleaned-up
-        exc, _ = test.BaseTestCase.handle_skip_exception()
-        self.mocks['skip_checks'].side_effect = (exc)
+        self.mocks['skip_checks'].side_effect = (
+            testtools.TestCase.skipException())
         suite = unittest.TestSuite((self.test,))
         log = []
         result = LoggingTestResult(log)
diff --git a/tools/tempest-plugin-sanity.sh b/tools/tempest-plugin-sanity.sh
index a087a4c..b484a41 100644
--- a/tools/tempest-plugin-sanity.sh
+++ b/tools/tempest-plugin-sanity.sh
@@ -60,6 +60,9 @@
     fi
 }
 
+: ${UPPER_CONSTRAINTS_FILE:="https://releases.openstack.org/constraints/upper/master"}
+DEPS="-c${UPPER_CONSTRAINTS_FILE}"
+
 # function to create virtualenv to perform sanity operation
 function prepare_workspace {
     SANITY_DIR=$(pwd)
@@ -73,10 +76,10 @@
 
 # Function to install project
 function install_project {
-    "$TVENV" pip install "$SANITY_DIR"/"$1"
+    "$TVENV" pip install $DEPS "$SANITY_DIR"/"$1"
     # Check for test-requirements.txt file in a project then install it.
     if [ -e "$SANITY_DIR"/"$1"/test-requirements.txt ]; then
-        "$TVENV" pip install -r "$SANITY_DIR"/"$1"/test-requirements.txt
+        "$TVENV" pip install $DEPS -r "$SANITY_DIR"/"$1"/test-requirements.txt
     fi
 }