Merge "Fix backward compatibility of compare volume stats"
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index e1e6597..9b5aad3 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v31.1.0
    v31.0.0
    v30.0.0
    v29.2.0
diff --git a/releasenotes/source/v31.1.0.rst b/releasenotes/source/v31.1.0.rst
new file mode 100644
index 0000000..ecd7c36
--- /dev/null
+++ b/releasenotes/source/v31.1.0.rst
@@ -0,0 +1,5 @@
+=====================
+v31.1.0 Release Notes
+=====================
+.. release-notes:: 31.1.0 Release Notes
+   :version: 31.1.0
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 2823185..a11bed8 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -182,7 +182,6 @@
         self.assertEqual(data, body)
 
     @decorators.idempotent_id('4f84422a-e2f2-4403-b601-726a4220b54e')
-    @decorators.unstable_test(bug='1905432')
     def test_create_object_with_transfer_encoding(self):
         """Test creating object with transfer_encoding"""
         object_name = data_utils.rand_name(name='TestObject')
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index fc86914..a11b7c1 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -492,7 +492,7 @@
             self._log_request_full(resp, req_headers, req_body,
                                    resp_body, extra)
 
-    def _parse_resp(self, body):
+    def _parse_resp(self, body, top_key_to_verify=None):
         try:
             body = json.loads(body)
         except ValueError:
@@ -516,8 +516,17 @@
             if not hasattr(body, "keys") or len(body.keys()) != 1:
                 return body
             # Just return the "wrapped" element
-            _, first_item = tuple(body.items())[0]
+            first_key, first_item = tuple(body.items())[0]
             if isinstance(first_item, (dict, list)):
+                if top_key_to_verify is not None:
+                    msg_args = {
+                        'top_key': top_key_to_verify,
+                        'actual_key': first_key,
+                    }
+                    assert_msg = ("The expected top level key is "
+                                  "'%(top_key)s' but we found "
+                                  "'%(actual_key)s'." % msg_args)
+                    assert top_key_to_verify == first_key, assert_msg
                 return first_item
         except (ValueError, IndexError):
             pass
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 73ce08f..2843498 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -815,7 +815,9 @@
             name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
         LOG.debug("Creating a snapshot image for server: %s", server['name'])
         image = _images_client.create_image(server['id'], name=name, **kwargs)
-        image_id = image.response['location'].split('images/')[1]
+        # microversion 2.45 and above returns image_id
+        image_id = image.get('image_id') or image.response['location'].split(
+            'images/')[1]
         waiters.wait_for_image_status(_image_client, image_id, 'active')
 
         self.addCleanup(_image_client.wait_for_resource_deletion,
diff --git a/tempest/tests/lib/common/test_rest_client.py b/tempest/tests/lib/common/test_rest_client.py
index 1dea5f5..910756f 100644
--- a/tempest/tests/lib/common/test_rest_client.py
+++ b/tempest/tests/lib/common/test_rest_client.py
@@ -280,6 +280,26 @@
         body = self.rest_client._parse_resp(json.dumps(empty_list))
         self.assertEqual(empty_list, body)
 
+    def test_parse_top_key_match(self):
+        body = self.rest_client._parse_resp(json.dumps(self.dict_expected),
+                                            top_key_to_verify="body_dict")
+        self.assertEqual(self.dict_expected["body_dict"], body)
+
+
+class TestRestClientParseErrorRespJSON(BaseRestClientTestClass):
+
+    dict_expected = {"body_dict": {"fake_key": "fake_value"}}
+
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2()
+        super(TestRestClientParseErrorRespJSON, self).setUp()
+
+    def test_parse_top_key_no_match(self):
+        self.assertRaises(AssertionError,
+                          self.rest_client._parse_resp,
+                          json.dumps(self.dict_expected),
+                          top_key_to_verify="body_key")
+
 
 class TestRestClientErrorCheckerJSON(base.TestCase):
     c_type = "application/json"
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index 0b34ae0..1f93903 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -98,7 +98,6 @@
         - tempest-slow-py3:
             irrelevant-files: *tempest-irrelevant-files
         - nova-live-migration:
-            voting: false
             irrelevant-files: *tempest-irrelevant-files
         - devstack-plugin-ceph-tempest-py3:
             # TODO(kopecmartin): make it voting once the below bug is fixed
@@ -147,6 +146,8 @@
         #    irrelevant-files: *tempest-irrelevant-files
         #- tempest-full-centos-9-stream:
         #    irrelevant-files: *tempest-irrelevant-files
+        - nova-live-migration:
+            irrelevant-files: *tempest-irrelevant-files
     experimental:
       jobs:
         - tempest-with-latest-microversion