Merge "Verify top key in _parse_body"
diff --git a/tempest/lib/api_schema/response/volume/v3_61/__init__.py b/tempest/lib/api_schema/response/volume/v3_61/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_61/__init__.py
diff --git a/tempest/lib/api_schema/response/volume/v3_61/volumes.py b/tempest/lib/api_schema/response/volume/v3_61/volumes.py
new file mode 100644
index 0000000..2e28b7e
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_61/volumes.py
@@ -0,0 +1,69 @@
+# Copyright 2022 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.volume import volumes
+
+# Volume micro version 3.61:
+# 1. Add cluster_name attribute to response body of volume details
+# for admin in Active/Active HA mode.
+# https://docs.openstack.org/cinder/latest/contributor/
+# api_microversion_history.html
+
+common_show_volume = copy.deepcopy(volumes.common_show_volume)
+common_show_volume['properties'].update(
+    {'cluster_name': parameter_types.uuid_or_null})
+
+create_volume = copy.deepcopy(volumes.create_volume)
+create_volume['response_body']['properties']['volume']['properties'].update(
+    {'cluster_name': parameter_types.uuid_or_null})
+
+# copy unchanged volumes schema
+attachments = copy.deepcopy(volumes.attachments)
+list_volumes_no_detail = copy.deepcopy(volumes.list_volumes_no_detail)
+# show_volume refers to common_show_volume
+show_volume = copy.deepcopy(volumes.show_volume)
+show_volume['response_body']['properties']['volume'] = common_show_volume
+# list copy refers to latest common_show_volume
+list_volumes_detail = copy.deepcopy(common_show_volume)
+list_volumes_with_detail = copy.deepcopy(volumes.list_volumes_with_detail)
+list_volumes_with_detail['response_body']['properties']['volumes']['items'] \
+    = list_volumes_detail
+update_volume = copy.deepcopy(volumes.update_volume)
+delete_volume = copy.deepcopy(volumes.delete_volume)
+show_volume_summary = copy.deepcopy(volumes.show_volume_summary)
+attach_volume = copy.deepcopy(volumes.attach_volume)
+set_bootable_volume = copy.deepcopy(volumes.set_bootable_volume)
+detach_volume = copy.deepcopy(volumes.detach_volume)
+reserve_volume = copy.deepcopy(volumes.reserve_volume)
+unreserve_volume = copy.deepcopy(volumes.unreserve_volume)
+extend_volume = copy.deepcopy(volumes.extend_volume)
+reset_volume_status = copy.deepcopy(volumes.reset_volume_status)
+update_volume_readonly = copy.deepcopy(volumes.update_volume_readonly)
+force_delete_volume = copy.deepcopy(volumes.force_delete_volume)
+retype_volume = copy.deepcopy(volumes.retype_volume)
+force_detach_volume = copy.deepcopy(volumes.force_detach_volume)
+create_volume_metadata = copy.deepcopy(volumes.create_volume_metadata)
+show_volume_metadata = copy.deepcopy(volumes.show_volume_metadata)
+update_volume_metadata = copy.deepcopy(volumes.update_volume_metadata)
+update_volume_metadata_item = copy.deepcopy(
+    volumes.update_volume_metadata_item)
+update_volume_image_metadata = copy.deepcopy(
+    volumes.update_volume_image_metadata)
+delete_volume_image_metadata = copy.deepcopy(
+    volumes.delete_volume_image_metadata)
+unmanage_volume = copy.deepcopy(volumes.unmanage_volume)
diff --git a/tempest/lib/api_schema/response/volume/v3_63/__init__.py b/tempest/lib/api_schema/response/volume/v3_63/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_63/__init__.py
diff --git a/tempest/lib/api_schema/response/volume/v3_63/volumes.py b/tempest/lib/api_schema/response/volume/v3_63/volumes.py
new file mode 100644
index 0000000..218db90
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_63/volumes.py
@@ -0,0 +1,69 @@
+# Copyright 2022 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.volume.v3_61 import volumes
+
+# Volume micro version 3.63:
+# 1. Includes volume type ID in the volume-show and volume-detail-list.
+# for admin in Active/Active HA mode.
+# https://docs.openstack.org/cinder/latest/contributor/
+# api_microversion_history.html
+
+common_show_volume = copy.deepcopy(volumes.common_show_volume)
+common_show_volume['properties'].update(
+    {'volume_type_id': parameter_types.uuid_or_null})
+
+create_volume = copy.deepcopy(volumes.create_volume)
+create_volume['response_body']['properties']['volume']['properties'].update(
+    {'volume_type_id': parameter_types.uuid_or_null})
+
+# copy unchanged volumes schema
+attachments = copy.deepcopy(volumes.attachments)
+list_volumes_no_detail = copy.deepcopy(volumes.list_volumes_no_detail)
+# show_volume refers to common_show_volume
+show_volume = copy.deepcopy(volumes.show_volume)
+show_volume['response_body']['properties']['volume'] = common_show_volume
+# list copy refers to latest common_show_volume
+list_volumes_detail = copy.deepcopy(common_show_volume)
+list_volumes_with_detail = copy.deepcopy(volumes.list_volumes_with_detail)
+list_volumes_with_detail['response_body']['properties']['volumes']['items'] \
+    = list_volumes_detail
+update_volume = copy.deepcopy(volumes.update_volume)
+delete_volume = copy.deepcopy(volumes.delete_volume)
+show_volume_summary = copy.deepcopy(volumes.show_volume_summary)
+attach_volume = copy.deepcopy(volumes.attach_volume)
+set_bootable_volume = copy.deepcopy(volumes.set_bootable_volume)
+detach_volume = copy.deepcopy(volumes.detach_volume)
+reserve_volume = copy.deepcopy(volumes.reserve_volume)
+unreserve_volume = copy.deepcopy(volumes.unreserve_volume)
+extend_volume = copy.deepcopy(volumes.extend_volume)
+reset_volume_status = copy.deepcopy(volumes.reset_volume_status)
+update_volume_readonly = copy.deepcopy(volumes.update_volume_readonly)
+force_delete_volume = copy.deepcopy(volumes.force_delete_volume)
+retype_volume = copy.deepcopy(volumes.retype_volume)
+force_detach_volume = copy.deepcopy(volumes.force_detach_volume)
+create_volume_metadata = copy.deepcopy(volumes.create_volume_metadata)
+show_volume_metadata = copy.deepcopy(volumes.show_volume_metadata)
+update_volume_metadata = copy.deepcopy(volumes.update_volume_metadata)
+update_volume_metadata_item = copy.deepcopy(
+    volumes.update_volume_metadata_item)
+update_volume_image_metadata = copy.deepcopy(
+    volumes.update_volume_image_metadata)
+delete_volume_image_metadata = copy.deepcopy(
+    volumes.delete_volume_image_metadata)
+unmanage_volume = copy.deepcopy(volumes.unmanage_volume)
diff --git a/tempest/lib/api_schema/response/volume/v3_64/__init__.py b/tempest/lib/api_schema/response/volume/v3_64/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_64/__init__.py
diff --git a/tempest/lib/api_schema/response/volume/v3_64/backups.py b/tempest/lib/api_schema/response/volume/v3_64/backups.py
new file mode 100644
index 0000000..01b93bc
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_64/backups.py
@@ -0,0 +1,48 @@
+# Copyright 2022 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.volume import backups
+
+# Volume micro version 3.64:
+# 1. Include the encryption_key_id in volume and backup
+# details when the associated volume is encrypted.
+# https://docs.openstack.org/cinder/latest/contributor/
+# api_microversion_history.html
+
+common_show_backup = copy.deepcopy(backups.common_show_backup)
+common_show_backup['properties'].update(
+    {'encryption_key_id': parameter_types.uuid_or_null})
+
+create_backup = copy.deepcopy(backups.create_backup)
+update_backup = copy.deepcopy(backups.update_backup)
+restore_backup = copy.deepcopy(backups.restore_backup)
+delete_backup = copy.deepcopy(backups.delete_backup)
+# show backup refers to common_show_backup
+show_backup = copy.deepcopy(backups.show_backup)
+show_backup['response_body']['properties']['backup'] = common_show_backup
+list_backups_no_detail = copy.deepcopy(backups.list_backups_no_detail)
+# list_backups_detail refers to latest common_show_backup
+list_backups_detail = copy.deepcopy(common_show_backup)
+list_backups_detail['properties'].update({'count': {'type': 'integer'}})
+list_backups_with_detail = copy.deepcopy(backups.list_backups_with_detail)
+# list_backups_with_detail refers to latest list_backups_detail
+list_backups_with_detail['response_body']['properties']['backups']['items'] =\
+    list_backups_detail
+export_backup = copy.deepcopy(backups.export_backup)
+import_backup = copy.deepcopy(backups.import_backup)
+reset_backup_status = copy.deepcopy(backups.reset_backup_status)
diff --git a/tempest/lib/api_schema/response/volume/v3_64/volumes.py b/tempest/lib/api_schema/response/volume/v3_64/volumes.py
new file mode 100644
index 0000000..0fbbb3f
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_64/volumes.py
@@ -0,0 +1,69 @@
+# Copyright 2022 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.volume.v3_63 import volumes
+
+# Volume micro version 3.64:
+# 1. Include the encryption_key_id in volume and backup
+# details when the associated volume is encrypted.
+# https://docs.openstack.org/cinder/latest/contributor/
+# api_microversion_history.html
+
+common_show_volume = copy.deepcopy(volumes.common_show_volume)
+common_show_volume['properties'].update(
+    {'encryption_key_id': parameter_types.uuid_or_null})
+
+create_volume = copy.deepcopy(volumes.create_volume)
+create_volume['response_body']['properties']['volume']['properties'].update(
+    {'encryption_key_id': parameter_types.uuid_or_null})
+
+# copy unchanged volumes schema
+attachments = copy.deepcopy(volumes.attachments)
+list_volumes_no_detail = copy.deepcopy(volumes.list_volumes_no_detail)
+# show_volume refers to common_show_volume
+show_volume = copy.deepcopy(volumes.show_volume)
+show_volume['response_body']['properties']['volume'] = common_show_volume
+# list_volumes_detail refers to latest common_show_volume
+list_volumes_detail = copy.deepcopy(common_show_volume)
+list_volumes_with_detail = copy.deepcopy(volumes.list_volumes_with_detail)
+list_volumes_with_detail['response_body']['properties']['volumes']['items'] \
+    = list_volumes_detail
+update_volume = copy.deepcopy(volumes.update_volume)
+delete_volume = copy.deepcopy(volumes.delete_volume)
+show_volume_summary = copy.deepcopy(volumes.show_volume_summary)
+attach_volume = copy.deepcopy(volumes.attach_volume)
+set_bootable_volume = copy.deepcopy(volumes.set_bootable_volume)
+detach_volume = copy.deepcopy(volumes.detach_volume)
+reserve_volume = copy.deepcopy(volumes.reserve_volume)
+unreserve_volume = copy.deepcopy(volumes.unreserve_volume)
+extend_volume = copy.deepcopy(volumes.extend_volume)
+reset_volume_status = copy.deepcopy(volumes.reset_volume_status)
+update_volume_readonly = copy.deepcopy(volumes.update_volume_readonly)
+force_delete_volume = copy.deepcopy(volumes.force_delete_volume)
+retype_volume = copy.deepcopy(volumes.retype_volume)
+force_detach_volume = copy.deepcopy(volumes.force_detach_volume)
+create_volume_metadata = copy.deepcopy(volumes.create_volume_metadata)
+show_volume_metadata = copy.deepcopy(volumes.show_volume_metadata)
+update_volume_metadata = copy.deepcopy(volumes.update_volume_metadata)
+update_volume_metadata_item = copy.deepcopy(
+    volumes.update_volume_metadata_item)
+update_volume_image_metadata = copy.deepcopy(
+    volumes.update_volume_image_metadata)
+delete_volume_image_metadata = copy.deepcopy(
+    volumes.delete_volume_image_metadata)
+unmanage_volume = copy.deepcopy(volumes.unmanage_volume)
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
index 4bf7ffb..0c32c52 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -18,6 +18,7 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.api_schema.response.volume import backups as schema
+from tempest.lib.api_schema.response.volume.v3_64 import backups as schemav364
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.volume import base_client
@@ -26,6 +27,11 @@
 class BackupsClient(base_client.BaseClient):
     """Volume V3 Backups client"""
 
+    schema_versions_info = [
+        {'min': None, 'max': '3.63', 'schema': schema},
+        {'min': '3.64', 'max': None, 'schema': schemav364}
+        ]
+
     def create_backup(self, **kwargs):
         """Creates a backup of volume.
 
@@ -76,6 +82,7 @@
         url = "backups/%s" % backup_id
         resp, body = self.get(url)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.show_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -88,6 +95,7 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-backups-with-detail
         """
         url = "backups"
+        schema = self.get_schema(self.schema_versions_info)
         list_backups_schema = schema.list_backups_no_detail
         if detail:
             url += "/detail"
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index 9c6fe68..9934e47 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -17,6 +17,9 @@
 
 from oslo_serialization import jsonutils as json
 
+from tempest.lib.api_schema.response.volume.v3_61 import volumes as schemav361
+from tempest.lib.api_schema.response.volume.v3_63 import volumes as schemav363
+from tempest.lib.api_schema.response.volume.v3_64 import volumes as schemav364
 from tempest.lib.api_schema.response.volume import volumes as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
@@ -26,6 +29,13 @@
 class VolumesClient(base_client.BaseClient):
     """Client class to send CRUD Volume V3 API requests"""
 
+    schema_versions_info = [
+        {'min': None, 'max': '3.60', 'schema': schema},
+        {'min': '3.61', 'max': '3.62', 'schema': schemav361},
+        {'min': '3.63', 'max': '3.63', 'schema': schemav363},
+        {'min': '3.64', 'max': None, 'schema': schemav364}
+        ]
+
     def _prepare_params(self, params):
         """Prepares params for use in get or _ext_get methods.
 
@@ -56,6 +66,7 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-accessible-volumes
         """
         url = 'volumes'
+        schema = self.get_schema(self.schema_versions_info)
         list_schema = schema.list_volumes_no_detail
         if detail:
             list_schema = schema.list_volumes_with_detail
@@ -86,6 +97,7 @@
         url = "volumes/%s" % volume_id
         resp, body = self.get(url)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.show_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -99,6 +111,7 @@
         post_body = json.dumps({'volume': kwargs})
         resp, body = self.post('volumes', post_body)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.create_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index ce45ff6..73ce08f 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -477,7 +477,8 @@
 
         self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
                         snapshot['id'])
-        self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.snapshots_client.delete_snapshot, snapshot['id'])
         waiters.wait_for_volume_resource_status(self.snapshots_client,
                                                 snapshot['id'], 'available')
         snapshot = self.snapshots_client.show_snapshot(
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 5aac19c..8cafd1f 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.common import custom_matchers
 from tempest.common import utils
 from tempest.common import waiters
@@ -29,24 +31,11 @@
 
     """This is a basic minimum scenario test.
 
-    This test below:
+    These tests below:
     * across the multiple components
     * as a regular user
     * with and without optional parameters
     * check command outputs
-
-    Steps:
-    1. Create image
-    2. Create keypair
-    3. Boot instance with keypair and get list of instances
-    4. Create volume and show list of volumes
-    5. Attach volume to instance and getlist of volumes
-    6. Add IP to instance
-    7. Create and add security group to instance
-    8. Check SSH connection to instance
-    9. Reboot instance
-    10. Check SSH connection to instance after reboot
-
     """
 
     def nova_show(self, server):
@@ -67,8 +56,9 @@
             volume, custom_matchers.MatchesDictExceptForKeys(
                 got_volume, excluded_keys=excluded_keys))
 
-    def nova_reboot(self, server):
-        self.servers_client.reboot_server(server['id'], type='SOFT')
+    def nova_reboot(self, server, hard=False):
+        self.servers_client.reboot_server(server['id'],
+                                          type="HARD" if hard else "SOFT")
         waiters.wait_for_server_status(self.servers_client,
                                        server['id'], 'ACTIVE')
 
@@ -96,9 +86,37 @@
                    '%s' % (secgroup['id'], server['id']))
             raise exceptions.TimeoutException(msg)
 
+    def _get_floating_ip_in_server_addresses(self, floating_ip, server):
+        for addresses in server['addresses'].values():
+            for address in addresses:
+                if (address['OS-EXT-IPS:type'] == 'floating' and
+                        address['addr'] == floating_ip['floating_ip_address']):
+                    return address
+
+    def _is_floating_ip_detached_from_server(self, server, floating_ip):
+        server_info = self.servers_client.show_server(
+            server['id'])['server']
+        address = self._get_floating_ip_in_server_addresses(
+            floating_ip, server_info)
+        return (not address)
+
     @decorators.idempotent_id('bdbb5441-9204-419d-a225-b4fdbfb1a1a8')
     @utils.services('compute', 'volume', 'image', 'network')
     def test_minimum_basic_scenario(self):
+        """This is a basic minimum scenario with multiple components
+
+        Steps:
+        1. Create image
+        2. Create keypair
+        3. Boot instance with keypair and get list of instances
+        4. Create volume and show list of volumes
+        5. Attach volume to instance and getlist of volumes
+        6. Add IP to instance
+        7. Create and add security group to instance
+        8. Check SSH connection to instance
+        9. Reboot instance
+        10. Check SSH connection to instance after reboot
+        """
         image = self.image_create()
         keypair = self.create_keypair()
 
@@ -121,7 +139,7 @@
         floating_ip = None
         server = self.servers_client.show_server(server['id'])['server']
         if (CONF.network_feature_enabled.floating_ips and
-            CONF.network.floating_network_name):
+                CONF.network.floating_network_name):
             fip = self.create_floating_ip(server)
             floating_ip = self.associate_floating_ip(
                 fip, server)
@@ -154,3 +172,116 @@
             waiters.wait_for_server_floating_ip(
                 self.servers_client, server, floating_ip,
                 wait_for_disassociate=True)
+
+            if not test_utils.call_until_true(
+                    self._is_floating_ip_detached_from_server,
+                    CONF.compute.build_timeout,
+                    CONF.compute.build_interval, server, floating_ip):
+                msg = ("Floating IP '%s' should not be in server addresses: %s"
+                       % (floating_ip['floating_ip_address'],
+                          server['addresses']))
+                raise exceptions.TimeoutException(msg)
+
+    @decorators.idempotent_id('a8fd48ec-1d01-4895-b932-02321661ec1e')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          "Cinder volume snapshots are disabled")
+    @utils.services('compute', 'volume', 'image', 'network')
+    def test_minimum_basic_instance_hard_reboot_after_vol_snap_deletion(self):
+        """Test compute hard reboot after volume snapshot deleted
+
+        Steps:
+        1. Create image
+        2. Create keypair
+        3. Boot instance with keypair and get list of instances
+        4. Create volume and show list of volumes
+        5. Attach volume to instance and getlist of volumes
+        6. Create a snapshot from volume
+        7. Add IP to instance
+        8. Create and add security group to instance
+        9. Check SSH connection to instance
+        10. Write data timestamp to the attached volume
+        11. Delete volume snapshot before reboot instance
+        12. Reboot instance (HARD)
+        13. Check SSH connection to instance after reboot
+        14. Verify attached disk data timestamp post instance reboot
+        """
+        image = self.image_create()
+        keypair = self.create_keypair()
+
+        server = self.create_server(image_id=image, key_name=keypair['name'])
+        servers = self.servers_client.list_servers()['servers']
+        self.assertIn(server['id'], [x['id'] for x in servers])
+
+        self.nova_show(server)
+
+        volume = self.create_volume()
+        volumes = self.volumes_client.list_volumes()['volumes']
+        self.assertIn(volume['id'], [x['id'] for x in volumes])
+
+        self.cinder_show(volume)
+
+        volume = self.nova_volume_attach(server, volume)
+        self.addCleanup(self.nova_volume_detach, server, volume)
+        snapshot = self.create_volume_snapshot(volume['id'], force=True)
+        self.cinder_show(volume)
+
+        floating_ip = None
+        server = self.servers_client.show_server(server['id'])['server']
+        if (CONF.network_feature_enabled.floating_ips and
+                CONF.network.floating_network_name):
+            fip = self.create_floating_ip(server)
+            floating_ip = self.associate_floating_ip(
+                fip, server)
+            # fetch the server again to make sure the addresses were refreshed
+            # after associating the floating IP
+            server = self.servers_client.show_server(server['id'])['server']
+            address = self._get_floating_ip_in_server_addresses(
+                floating_ip, server)
+            self.assertIsNotNone(
+                address,
+                "Failed to find floating IP '%s' in server addresses: %s" %
+                (floating_ip['floating_ip_address'], server['addresses']))
+            ssh_ip = floating_ip['floating_ip_address']
+        else:
+            ssh_ip = self.get_server_ip(server)
+
+        self.create_and_add_security_group_to_server(server)
+
+        # check that we can SSH to the server before reboot
+        self.linux_client = self.get_remote_client(
+            ssh_ip, private_key=keypair['private_key'],
+            server=server)
+
+        # write data to the volume before reboot instance
+        timestamp_before = self.create_timestamp(
+            ssh_ip, private_key=keypair['private_key'], server=server)
+        # delete the snapshot before rebooting the instance
+        self.snapshots_client.delete_snapshot(snapshot['id'])
+        self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
+        self.nova_reboot(server, hard=True)
+
+        # check that we can SSH to the server after reboot
+        # (both connections are part of the scenario)
+        self.linux_client = self.get_remote_client(
+            ssh_ip, private_key=keypair['private_key'],
+            server=server)
+
+        self.check_disks()
+        timestamp_after = self.get_timestamp(
+            ssh_ip, private_key=keypair['private_key'], server=server)
+        self.assertEqual(timestamp_before, timestamp_after)
+        if floating_ip:
+            # delete the floating IP, this should refresh the server addresses
+            self.disassociate_floating_ip(floating_ip)
+            waiters.wait_for_server_floating_ip(
+                self.servers_client, server, floating_ip,
+                wait_for_disassociate=True)
+
+            if not test_utils.call_until_true(
+                self._is_floating_ip_detached_from_server,
+                    CONF.compute.build_timeout, CONF.compute.build_interval,
+                    server, floating_ip):
+                msg = ("Floating IP '%s' should not be in server addresses: %s"
+                       % (floating_ip['floating_ip_address'],
+                          server['addresses']))
+                raise exceptions.TimeoutException(msg)
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