Improve snapshot data integrity test

Currently the test require lot of resources i.e. 4 instance.
This patch uses recommendation from the comments of initial patch[1]
i.e. creates 1 instance, attach volumes to it, snapshot etc

[1] https://review.opendev.org/c/openstack/cinder-tempest-plugin/+/702495

Depends-On: https://review.opendev.org/c/openstack/barbican/+/796059

Change-Id: I151b034dd28834290ffeb3ce9c052a6c3bbfb7d8
diff --git a/cinder_tempest_plugin/scenario/test_snapshots.py b/cinder_tempest_plugin/scenario/test_snapshots.py
index 3153281..0abc13c 100644
--- a/cinder_tempest_plugin/scenario/test_snapshots.py
+++ b/cinder_tempest_plugin/scenario/test_snapshots.py
@@ -14,10 +14,15 @@
 #    under the License.
 
 from tempest.common import utils
+from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 from cinder_tempest_plugin.scenario import manager
 
+CONF = config.CONF
+
 
 class SnapshotDataIntegrityTests(manager.ScenarioTest):
 
@@ -26,62 +31,109 @@
         self.keypair = self.create_keypair()
         self.security_group = self._create_security_group()
 
-    def _get_file_md5(self, ip_address, filename, mount_path='/mnt',
-                      private_key=None, server=None):
+    def _attached_volume_name(
+            self, disks_list_before_attach, ip_address, private_key):
+        ssh = self.get_remote_client(ip_address, private_key=private_key)
+
+        def _wait_for_volume_available_on_system():
+            disks_list_after_attach = ssh.list_disks()
+            return len(disks_list_after_attach) > len(disks_list_before_attach)
+
+        if not test_utils.call_until_true(_wait_for_volume_available_on_system,
+                                          CONF.compute.build_timeout,
+                                          CONF.compute.build_interval):
+            raise lib_exc.TimeoutException
+
+        disks_list_after_attach = ssh.list_disks()
+        volume_name = [item for item in disks_list_after_attach
+                       if item not in disks_list_before_attach][0]
+        return volume_name
+
+    def _get_file_md5(self, ip_address, filename, dev_name=None,
+                      mount_path='/mnt', private_key=None, server=None):
+
         ssh_client = self.get_remote_client(ip_address,
                                             private_key=private_key,
                                             server=server)
+        if dev_name is not None:
+            ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
+                                                               mount_path))
 
         md5_sum = ssh_client.exec_command(
             'sudo md5sum %s/%s|cut -c 1-32' % (mount_path, filename))
+        if dev_name is not None:
+            ssh_client.exec_command('sudo umount %s' % mount_path)
         return md5_sum
 
-    def _count_files(self, ip_address, mount_path='/mnt', private_key=None,
-                     server=None):
+    def _count_files(self, ip_address, dev_name=None, mount_path='/mnt',
+                     private_key=None, server=None):
         ssh_client = self.get_remote_client(ip_address,
                                             private_key=private_key,
                                             server=server)
+        if dev_name is not None:
+            ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
+                                                               mount_path))
         count = ssh_client.exec_command('sudo ls -l %s | wc -l' % mount_path)
-        return int(count) - 1
+        if dev_name is not None:
+            ssh_client.exec_command('sudo umount %s' % mount_path)
+        # We subtract 2 from the count since `wc -l` also includes the count
+        # of new line character and while creating the filesystem, a
+        # lost+found folder is also created
+        return int(count) - 2
 
-    def _launch_instance_from_snapshot(self, snap):
-        volume_snap = self.create_volume(snapshot_id=snap['id'],
-                                         size=snap['size'])
-
-        server_snap = self.boot_instance_from_resource(
-            source_id=volume_snap['id'],
-            source_type='volume',
-            keypair=self.keypair,
-            security_group=self.security_group)
-
-        return server_snap
-
-    def create_md5_new_file(self, ip_address, filename, mount_path='/mnt',
-                            private_key=None, server=None):
+    def _make_fs(self, ip_address, private_key, server, dev_name, fs='ext4'):
         ssh_client = self.get_remote_client(ip_address,
                                             private_key=private_key,
                                             server=server)
 
+        ssh_client.make_fs(dev_name, fs=fs)
+
+    def create_md5_new_file(self, ip_address, filename, dev_name=None,
+                            mount_path='/mnt', private_key=None, server=None):
+        ssh_client = self.get_remote_client(ip_address,
+                                            private_key=private_key,
+                                            server=server)
+
+        if dev_name is not None:
+            ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
+                                                               mount_path))
         ssh_client.exec_command(
             'sudo dd bs=1024 count=100 if=/dev/urandom of=/%s/%s' %
             (mount_path, filename))
         md5 = ssh_client.exec_command(
             'sudo md5sum -b %s/%s|cut -c 1-32' % (mount_path, filename))
         ssh_client.exec_command('sudo sync')
+        if dev_name is not None:
+            ssh_client.exec_command('sudo umount %s' % mount_path)
         return md5
 
-    def get_md5_from_file(self, instance, filename):
-
-        instance_ip = self.get_server_ip(instance)
+    def get_md5_from_file(self, instance, instance_ip, filename,
+                          dev_name=None):
 
         md5_sum = self._get_file_md5(instance_ip, filename=filename,
+                                     dev_name=dev_name,
                                      private_key=self.keypair['private_key'],
                                      server=instance)
-        count = self._count_files(instance_ip,
+        count = self._count_files(instance_ip, dev_name=dev_name,
                                   private_key=self.keypair['private_key'],
                                   server=instance)
         return count, md5_sum
 
+    def _attach_and_get_volume_device_name(self, server, volume, instance_ip,
+                                           private_key):
+        ssh_client = self.get_remote_client(
+            instance_ip, private_key=private_key,
+            server=server)
+        # List disks before volume attachment
+        disks_list_before_attach = ssh_client.list_disks()
+        # Attach volume
+        volume = self.nova_volume_attach(server, volume)
+        # Find the difference between disks before and after attachment that
+        # gives us the volume device name
+        volume_device_name = self._attached_volume_name(
+            disks_list_before_attach, instance_ip, private_key)
+        return volume_device_name
+
     @decorators.idempotent_id('ff10644e-5a70-4a9f-9801-8204bb81fb61')
     @utils.services('compute', 'volume', 'image', 'network')
     def test_snapshot_data_integrity(self):
@@ -89,32 +141,39 @@
 
         snapshots. The procedure is as follows:
 
-        1) create a volume from image
-        2) Boot an instance from the volume
-        3) create file on vm and write data into it
+        1) Create an instance with ephemeral disk
+        2) Create a volume, attach it to the instance and create a filesystem
+           on it and mount it
+        3) Mount the volume, create a file and write data into it, Unmount it
         4) create snapshot
         5) repeat 3 and 4 two more times (simply creating 3 snapshots)
 
-        Now restore the snapshots one by one into volume, create instances
-        from it and check the number of files and file content at each
+        Now create volume from the snapshots one by one, attach it to the
+        instance and check the number of files and file content at each
         point when snapshot was created.
         """
 
-        # Create a volume from image
-        volume = self.create_volume_from_image()
+        # Create an instance
+        server = self.create_server(
+            key_name=self.keypair['name'],
+            security_groups=[{'name': self.security_group['name']}])
 
-        # create an instance from bootable volume
-        server = self.boot_instance_from_resource(
-            source_id=volume['id'],
-            source_type='volume',
-            keypair=self.keypair,
-            security_group=self.security_group)
+        # Create an empty volume
+        volume = self.create_volume()
 
         instance_ip = self.get_server_ip(server)
 
+        # Attach volume to instance and find it's device name (eg: /dev/vdb)
+        volume_device_name = self._attach_and_get_volume_device_name(
+            server, volume, instance_ip, self.keypair['private_key'])
+
+        # Create filesystem on the volume
+        self._make_fs(instance_ip, self.keypair['private_key'], server,
+                      volume_device_name)
+
         # Write data to volume
         file1_md5 = self.create_md5_new_file(
-            instance_ip, filename="file1",
+            instance_ip, dev_name=volume_device_name, filename="file1",
             private_key=self.keypair['private_key'],
             server=instance_ip)
 
@@ -123,7 +182,7 @@
 
         # Write data to volume
         file2_md5 = self.create_md5_new_file(
-            instance_ip, filename="file2",
+            instance_ip, dev_name=volume_device_name, filename="file2",
             private_key=self.keypair['private_key'],
             server=instance_ip)
 
@@ -132,33 +191,51 @@
 
         # Write data to volume
         file3_md5 = self.create_md5_new_file(
-            instance_ip, filename="file3",
+            instance_ip, dev_name=volume_device_name, filename="file3",
             private_key=self.keypair['private_key'],
             server=instance_ip)
 
         # Create third snapshot
         snapshot3 = self.create_volume_snapshot(volume['id'], force=True)
 
-        # Create volume, instance and check file and contents for snap1
-        instance_1 = self._launch_instance_from_snapshot(snapshot1)
-        count_snap_1, md5_file_1 = self.get_md5_from_file(instance_1,
-                                                          'file1')
+        # Detach the volume
+        self.nova_volume_detach(server, volume)
+
+        # Create volume from snapshot, attach it to instance and check file
+        # and contents for snap1
+        volume_snap_1 = self.create_volume(snapshot_id=snapshot1['id'])
+        volume_device_name = self._attach_and_get_volume_device_name(
+            server, volume_snap_1, instance_ip, self.keypair['private_key'])
+        count_snap_1, md5_file_1 = self.get_md5_from_file(
+            server, instance_ip, 'file1', dev_name=volume_device_name)
+        # Detach the volume
+        self.nova_volume_detach(server, volume_snap_1)
 
         self.assertEqual(count_snap_1, 1)
         self.assertEqual(file1_md5, md5_file_1)
 
-        # Create volume, instance and check file and contents for snap2
-        instance_2 = self._launch_instance_from_snapshot(snapshot2)
-        count_snap_2, md5_file_2 = self.get_md5_from_file(instance_2,
-                                                          'file2')
+        # Create volume from snapshot, attach it to instance and check file
+        # and contents for snap2
+        volume_snap_2 = self.create_volume(snapshot_id=snapshot2['id'])
+        volume_device_name = self._attach_and_get_volume_device_name(
+            server, volume_snap_2, instance_ip, self.keypair['private_key'])
+        count_snap_2, md5_file_2 = self.get_md5_from_file(
+            server, instance_ip, 'file2', dev_name=volume_device_name)
+        # Detach the volume
+        self.nova_volume_detach(server, volume_snap_2)
 
         self.assertEqual(count_snap_2, 2)
         self.assertEqual(file2_md5, md5_file_2)
 
-        # Create volume, instance and check file and contents for snap3
-        instance_3 = self._launch_instance_from_snapshot(snapshot3)
-        count_snap_3, md5_file_3 = self.get_md5_from_file(instance_3,
-                                                          'file3')
+        # Create volume from snapshot, attach it to instance and check file
+        # and contents for snap3
+        volume_snap_3 = self.create_volume(snapshot_id=snapshot3['id'])
+        volume_device_name = self._attach_and_get_volume_device_name(
+            server, volume_snap_3, instance_ip, self.keypair['private_key'])
+        count_snap_3, md5_file_3 = self.get_md5_from_file(
+            server, instance_ip, 'file3', dev_name=volume_device_name)
+        # Detach the volume
+        self.nova_volume_detach(server, volume_snap_3)
 
         self.assertEqual(count_snap_3, 3)
         self.assertEqual(file3_md5, md5_file_3)