diff --git a/autotest.sh b/autotest.sh index f12b05e1e..35ac3bd6a 100755 --- a/autotest.sh +++ b/autotest.sh @@ -193,7 +193,7 @@ if [ $SPDK_TEST_LVOL -eq 1 ]; then timing_enter lvol test_cases="1,50,51,52,53,100,101,102,150,200,201,250,251,252,253,254,255," test_cases+="300,301,450,451,452,550,600,601,650,651,652,654,655," - test_cases+="700,701,800,801,802,803,804,10000" + test_cases+="700,701,750,751,752,753,754,755,800,801,802,803,804,10000" run_test ./test/lvol/lvol.sh --test-cases=$test_cases report_test_completion "lvol" timing_exit lvol diff --git a/test/lvol/lvol.sh b/test/lvol/lvol.sh index 524d2df2e..6bb88f245 100755 --- a/test/lvol/lvol.sh +++ b/test/lvol/lvol.sh @@ -58,6 +58,12 @@ function usage() { 655: 'thin_provisioning_filling_disks_less_than_lvs_size', 700: 'tasting_positive', 701: 'tasting_lvol_store_positive', + 750: 'snapshot_readonly', + 751: 'snapshot_compare_with_lvol_bdev', + 752: 'snapshot_during_io_traffic', + 753: 'snapshot_of_snapshot', + 754: 'clone_bdev_only', + 755: 'clone_writing_clone', 800: 'rename_positive', 801: 'rename_lvs_nonexistent', 802: 'rename_lvs_EEXIST', diff --git a/test/lvol/rpc_commands_lib.py b/test/lvol/rpc_commands_lib.py index 944e12ce5..6d7075cfe 100644 --- a/test/lvol/rpc_commands_lib.py +++ b/test/lvol/rpc_commands_lib.py @@ -200,3 +200,13 @@ class Commands_Rpc(object): print("INFO: Renaming lvol bdev from {old} to {new}".format(old=old_name, new=new_name)) output, rc = self.rpc.rename_lvol_bdev(old_name, new_name) return rc + + def snapshot_lvol_bdev(self, bdev_name, snapshot_name): + print("INFO: RPC COMMAND snapshot_lvol_bdev") + output, rc = self.rpc.snapshot_lvol_bdev(bdev_name, snapshot_name) + return rc + + def clone_lvol_bdev(self, snapshot_name, clone_name): + print("INFO: RPC COMMAND clone_lvol_bdev") + output, rc = self.rpc.clone_lvol_bdev(snapshot_name, clone_name) + return rc diff --git a/test/lvol/test_cases.py b/test/lvol/test_cases.py index 56252e271..dddaf672e 100644 --- a/test/lvol/test_cases.py +++ b/test/lvol/test_cases.py @@ -1,5 +1,6 @@ #!/usr/bin/env python import io +import time import sys import random import signal @@ -7,6 +8,7 @@ import subprocess import pprint import socket import threading +import os from errno import ESRCH from os import kill, path, unlink, path, listdir, remove @@ -125,6 +127,12 @@ def case_message(func): 655: 'thin_provisioning_filling_disks_less_than_lvs_size', 700: 'tasting_positive', 701: 'tasting_lvol_store_positive', + 750: 'snapshot_readonly', + 751: 'snapshot_compare_with_lvol_bdev', + 752: 'snapshot_during_io_traffic', + 753: 'snapshot_of_snapshot', + 754: 'clone_bdev_only', + 755: 'clone_writing_clone', 800: 'rename_positive', 801: 'rename_lvs_nonexistent', 802: 'rename_lvs_EEXIST', @@ -1235,6 +1243,379 @@ class TestCases(object): fail_count += 1 return fail_count + @case_message + def test_case750(self): + """ + snapshot readonly + + Create snaphot of lvol bdev and check if it is readonly. + """ + fail_count = 0 + nbd_name0 = "/dev/nbd0" + snapshot_name = "snapshot0" + # Construct malloc bdev + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + # Construct lvol store on malloc bdev + uuid_store = self.c.construct_lvol_store(base_name, + self.lvs_name, + self.cluster_size) + fail_count += self.c.check_get_lvol_stores(base_name, uuid_store, + self.cluster_size) + + lvs = self.c.get_lvol_stores()[0] + free_clusters_start = int(lvs['free_clusters']) + bdev_size = int(int(lvs['cluster_size']) * int(lvs['free_clusters']) / MEGABYTE / 3) + # Create lvol bdev with 33% of lvol store space + bdev_name = self.c.construct_lvol_bdev(uuid_store, self.lbd_name, + bdev_size) + lvol_bdev = self.c.get_lvol_bdev_with_name(bdev_name) + # Create snapshot of lvol bdev + fail_count += self.c.snapshot_lvol_bdev(lvol_bdev['name'], snapshot_name) + snapshot_bdev = self.c.get_lvol_bdev_with_name(self.lvs_name + "/" + snapshot_name) + + fail_count += self.c.start_nbd_disk(snapshot_bdev['name'], nbd_name0) + size = bdev_size * MEGABYTE + # Try to perform write operation on created snapshot + # Check if filling snapshot of lvol bdev fails + fail_count += self.run_fio_test(nbd_name0, 0, size, "write", "0xcc", 1) + + fail_count += self.c.stop_nbd_disk(nbd_name0) + # Destroy lvol bdev + fail_count += self.c.delete_bdev(lvol_bdev['name']) + # Destroy snapshot + fail_count += self.c.delete_bdev(snapshot_bdev['name']) + # Destroy lvol store + fail_count += self.c.destroy_lvol_store(uuid_store) + # Delete malloc bdev + fail_count += self.c.delete_bdev(base_name) + + # Expected result: + # - calls successful, return code = 0 + # - no other operation fails + return fail_count + + @case_message + def test_case751(self): + """ + snapshot_compare_with_lvol_bdev + + Check if lvol bdevs and snapshots contain the same data. + Check if lvol bdev and snapshot differ when writing to lvol bdev + after creating snapshot. + """ + fail_count = 0 + nbd_name = ["/dev/nbd0", "/dev/nbd1", "/dev/nbd2", "/dev/nbd3"] + snapshot_name0 = "snapshot0" + snapshot_name1 = "snapshot1" + # Construct mallov bdev + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + # Construct lvol store + uuid_store = self.c.construct_lvol_store(base_name, + self.lvs_name, + self.cluster_size) + fail_count += self.c.check_get_lvol_stores(base_name, uuid_store, + self.cluster_size) + lvs = self.c.get_lvol_stores() + size = int(int(lvs[0][u'free_clusters'] * lvs[0]['cluster_size']) / 6 / MEGABYTE) + lbd_name0 = self.lbd_name + str(0) + lbd_name1 = self.lbd_name + str(1) + # Create thin provisioned lvol bdev with size less than 25% of lvs + uuid_bdev0 = self.c.construct_lvol_bdev(uuid_store, + lbd_name0, size, thin=True) + # Create thick provisioned lvol bdev with size less than 25% of lvs + uuid_bdev1 = self.c.construct_lvol_bdev(uuid_store, + lbd_name1, size, thin=False) + lvol_bdev0 = self.c.get_lvol_bdev_with_name(uuid_bdev0) + fail_count += self.c.start_nbd_disk(lvol_bdev0['name'], nbd_name[0]) + fill_size = int(size * MEGABYTE / 2) + # Fill thin provisoned lvol bdev with 50% of its space + fail_count += self.run_fio_test(nbd_name[0], 0, fill_size, "write", "0xcc", 0) + lvol_bdev1 = self.c.get_lvol_bdev_with_name(uuid_bdev1) + fail_count += self.c.start_nbd_disk(lvol_bdev1['name'], nbd_name[1]) + fill_size = int(size * MEGABYTE) + # Fill whole thic provisioned lvol bdev + fail_count += self.run_fio_test(nbd_name[1], 0, fill_size, "write", "0xcc", 0) + + # Create snapshots of lvol bdevs + fail_count += self.c.snapshot_lvol_bdev(uuid_bdev0, snapshot_name0) + fail_count += self.c.snapshot_lvol_bdev(uuid_bdev1, snapshot_name1) + fail_count += self.c.start_nbd_disk(self.lvs_name + "/" + snapshot_name0, nbd_name[2]) + fail_count += self.c.start_nbd_disk(self.lvs_name + "/" + snapshot_name1, nbd_name[3]) + # Compare every lvol bdev with corresponding snapshot + # and check that data are the same + fail_count += self.compare_two_disks(nbd_name[0], nbd_name[2], 0) + fail_count += self.compare_two_disks(nbd_name[1], nbd_name[3], 0) + + fill_size = int(size * MEGABYTE / 2) + offset = fill_size + # Fill second half of thin provisioned lvol bdev + fail_count += self.run_fio_test(nbd_name[0], offset, fill_size, "write", "0xaa", 0) + # Compare thin provisioned lvol bdev with its snapshot and check if it fails + fail_count += self.compare_two_disks(nbd_name[0], nbd_name[2], 1) + for nbd in nbd_name: + fail_count += self.c.stop_nbd_disk(nbd) + # Delete lvol bdevs + fail_count += self.c.delete_bdev(lvol_bdev0['name']) + fail_count += self.c.delete_bdev(lvol_bdev1['name']) + # Delete snapshots + fail_count += self.c.delete_bdev(self.lvs_name + "/" + snapshot_name0) + fail_count += self.c.delete_bdev(self.lvs_name + "/" + snapshot_name1) + # Destroy snapshot + fail_count += self.c.destroy_lvol_store(uuid_store) + # Delete malloc bdev + fail_count += self.c.delete_bdev(base_name) + + # Expected result: + # - calls successful, return code = 0 + # - removing snapshot should always end with success + # - no other operation fails + return fail_count + + @case_message + def test_case752(self): + """ + snapshot_during_io_traffic + + Check that when writing to lvol bdev + creating snapshot ends with success + """ + global current_fio_pid + fail_count = 0 + nbd_name = "/dev/nbd0" + snapshot_name = "snapshot" + # Create malloc bdev + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + # Construct lvol store + uuid_store = self.c.construct_lvol_store(base_name, + self.lvs_name, + self.cluster_size) + fail_count += self.c.check_get_lvol_stores(base_name, uuid_store, + self.cluster_size) + lvs = self.c.get_lvol_stores() + # Create thin provisioned lvol bdev with size equal to 50% of lvs space + size = int(int(lvs[0][u'free_clusters'] * lvs[0]['cluster_size']) / 2 / MEGABYTE) + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, self.lbd_name, + size, thin=True) + + lvol_bdev = self.c.get_lvol_bdev_with_name(uuid_bdev) + fail_count += self.c.start_nbd_disk(lvol_bdev['name'], nbd_name) + fill_size = int(size * MEGABYTE) + # Create thread that will run fio in background + thread = FioThread(nbd_name, 0, fill_size, "write", "0xcc", 0, + extra_params="--time_based --runtime=8") + # Perform write operation with verification to created lvol bdev + thread.start() + time.sleep(4) + fail_count += is_process_alive(current_fio_pid) + # During write operation create snapshot of created lvol bdev + # and check that snapshot has been created successfully + fail_count += self.c.snapshot_lvol_bdev(lvol_bdev['name'], snapshot_name) + fail_count += is_process_alive(current_fio_pid) + thread.join() + # Check that write operation ended with success + fail_count += thread.rv + fail_count += self.c.stop_nbd_disk(nbd_name) + # Destroy lvol bdev + fail_count += self.c.delete_bdev(lvol_bdev['name']) + # Delete snapshot + fail_count += self.c.delete_bdev(self.lvs_name + "/" + snapshot_name) + # Destroy lvol store + fail_count += self.c.destroy_lvol_store(uuid_store) + # Delete malloc bdevs + fail_count += self.c.delete_bdev(base_name) + + # Expected result: + # - calls successful, return code = 0 + # - no other operation fails + return fail_count + + @case_message + def test_case753(self): + """ + snapshot_of_snapshot + + Check that creating snapshot of snapshot will fail + """ + fail_count = 0 + snapshot_name0 = "snapshot0" + snapshot_name1 = "snapshot1" + # Create malloc bdev + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + # Construct lvol store + uuid_store = self.c.construct_lvol_store(base_name, + self.lvs_name, + self.cluster_size) + fail_count += self.c.check_get_lvol_stores(base_name, uuid_store, + self.cluster_size) + lvs = self.c.get_lvol_stores() + # Create thick provisioned lvol bdev + size = int(int(lvs[0][u'free_clusters'] * lvs[0]['cluster_size']) / 2 / MEGABYTE) + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, self.lbd_name, + size, thin=False) + + lvol_bdev = self.c.get_lvol_bdev_with_name(uuid_bdev) + # Create snapshot of created lvol bdev + fail_count += self.c.snapshot_lvol_bdev(lvol_bdev['name'], snapshot_name0) + # Create snapshot of previously created snapshot + # and check if operation will fail + if self.c.snapshot_lvol_bdev(snapshot_name0, snapshot_name1) == 0: + print("ERROR: Creating snapshot of snapshot should fail") + fail_count += 1 + # Delete lvol bdev + fail_count += self.c.delete_bdev(lvol_bdev['name']) + # Destroy snapshot + fail_count += self.c.delete_bdev(self.lvs_name + "/" + snapshot_name0) + # Destroy lvol store + fail_count += self.c.destroy_lvol_store(uuid_store) + # Delete malloc bdev + fail_count += self.c.delete_bdev(base_name) + + # Expected result: + # - calls successful, return code = 0 + # - creating snapshot of snapshot should fail + # - no other operation fails + return fail_count + + @case_message + def test_case754(self): + """ + clone_bdev_only + + Check that only clone of snapshot can be created. + Creating clone of lvol bdev should fail. + """ + fail_count = 0 + clone_name = "clone" + snapshot_name = "snapshot" + # Create malloc bdev + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + # Construct lvol store + uuid_store = self.c.construct_lvol_store(base_name, + self.lvs_name, + self.cluster_size) + fail_count += self.c.check_get_lvol_stores(base_name, uuid_store, + self.cluster_size) + lvs = self.c.get_lvol_stores() + # Create thick provisioned lvol bdev with size equal to 50% of lvs space + size = int(int(lvs[0][u'free_clusters'] * lvs[0]['cluster_size']) / 2 / MEGABYTE) + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, self.lbd_name, + size, thin=False) + + lvol_bdev = self.c.get_lvol_bdev_with_name(uuid_bdev) + # Create clone of lvol bdev and check if it fails + rv = self.c.clone_lvol_bdev(lvol_bdev['name'], clone_name) + if rv == 0: + print("ERROR: Creating clone of lvol bdev ended with unexpected success") + fail_count += 1 + # Create snapshot of lvol bdev + fail_count += self.c.snapshot_lvol_bdev(lvol_bdev['name'], snapshot_name) + # Create again clone of lvol bdev and check if it fails + rv = self.c.clone_lvol_bdev(lvol_bdev['name'], clone_name) + if rv == 0: + print("ERROR: Creating clone of lvol bdev ended with unexpected success") + fail_count += 1 + # Create clone of snapshot and check if it ends with success + rv = self.c.clone_lvol_bdev(self.lvs_name + "/" + snapshot_name, clone_name) + if rv != 0: + print("ERROR: Creating clone of snapshot ended with unexpected failure") + fail_count += 1 + clone_bdev = self.c.get_lvol_bdev_with_name(self.lvs_name + "/" + clone_name) + + # Delete lvol bdev + fail_count += self.c.delete_bdev(lvol_bdev['name']) + # Destroy clone + fail_count += self.c.delete_bdev(clone_bdev['name']) + # Delete snapshot + fail_count += self.c.delete_bdev(self.lvs_name + "/" + snapshot_name) + # Delete lvol store + fail_count += self.c.destroy_lvol_store(uuid_store) + # Destroy malloc bdev + fail_count += self.c.delete_bdev(base_name) + + # Expected result: + # - calls successful, return code = 0 + # - cloning thick provisioned lvol bdev should fail + # - no other operation fails + return fail_count + + @case_message + def test_case755(self): + """ + clone_writing_to_clone + + + """ + fail_count = 0 + nbd_name = ["/dev/nbd0", "/dev/nbd1", "/dev/nbd2", "/dev/nbd3"] + snapshot_name = "snapshot" + clone_name0 = "clone0" + clone_name1 = "clone1" + # Create malloc bdev + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + # Create lvol store + uuid_store = self.c.construct_lvol_store(base_name, + self.lvs_name, + self.cluster_size) + fail_count += self.c.check_get_lvol_stores(base_name, uuid_store, + self.cluster_size) + lvs = self.c.get_lvol_stores() + size = int(int(lvs[0][u'free_clusters'] * lvs[0]['cluster_size']) / 6 / MEGABYTE) + lbd_name0 = self.lbd_name + str(0) + # Construct thick provisioned lvol bdev + uuid_bdev0 = self.c.construct_lvol_bdev(uuid_store, + lbd_name0, size, thin=False) + lvol_bdev = self.c.get_lvol_bdev_with_name(uuid_bdev0) + # Install lvol bdev on /dev/nbd0 + fail_count += self.c.start_nbd_disk(lvol_bdev['name'], nbd_name[0]) + fill_size = size * MEGABYTE + # Fill lvol bdev with 100% of its space + fail_count += self.run_fio_test(nbd_name[0], 0, fill_size, "write", "0xcc", 0) + + # Create snapshot of thick provisioned lvol bdev + fail_count += self.c.snapshot_lvol_bdev(lvol_bdev['name'], snapshot_name) + snapshot_bdev = self.c.get_lvol_bdev_with_name(self.lvs_name + "/" + snapshot_name) + # Create two clones of created snapshot + fail_count += self.c.clone_lvol_bdev(snapshot_bdev['name'], clone_name0) + fail_count += self.c.clone_lvol_bdev(snapshot_bdev['name'], clone_name1) + + lvol_clone0 = self.c.get_lvol_bdev_with_name(self.lvs_name + "/" + clone_name0) + fail_count += self.c.start_nbd_disk(lvol_clone0['name'], nbd_name[1]) + fill_size = int(size * MEGABYTE / 2) + # Perform write operation to first clone + # Change first half of its space + fail_count += self.run_fio_test(nbd_name[1], 0, fill_size, "write", "0xaa", 0) + fail_count += self.c.start_nbd_disk(self.lvs_name + "/" + snapshot_name, nbd_name[2]) + lvol_clone1 = self.c.get_lvol_bdev_with_name(self.lvs_name + "/" + clone_name1) + fail_count += self.c.start_nbd_disk(lvol_clone1['name'], nbd_name[3]) + # Compare snapshot with second clone. Data on both bdevs should be the same + time.sleep(1) + fail_count += self.compare_two_disks(nbd_name[2], nbd_name[3], 0) + + for nbd in nbd_name: + fail_count += self.c.stop_nbd_disk(nbd) + # Destroy lvol bdev + fail_count += self.c.delete_bdev(lvol_bdev['name']) + # Destroy two clones + fail_count += self.c.delete_bdev(lvol_clone0['name']) + fail_count += self.c.delete_bdev(lvol_clone1['name']) + # Delete snapshot + fail_count += self.c.delete_bdev(snapshot_bdev['name']) + # Destroy lvol store + fail_count += self.c.destroy_lvol_store(uuid_store) + # Delete malloc + fail_count += self.c.delete_bdev(base_name) + + # Expected result: + # - calls successful, return code = 0 + # - no other operation fails + return fail_count + @case_message def test_case800(self): fail_count = 0