diff --git a/autotest.sh b/autotest.sh index 4d8362e9a..123411179 100755 --- a/autotest.sh +++ b/autotest.sh @@ -201,7 +201,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,750,751,752,753,754,755,756,757," + test_cases+="700,701,750,751,752,753,754,755,756,757,758,759," test_cases+="800,801,802,803,804,10000" run_test ./test/lvol/lvol.sh --test-cases=$test_cases report_test_completion "lvol" diff --git a/test/lvol/lvol.sh b/test/lvol/lvol.sh index 7786de4f7..081a883d3 100755 --- a/test/lvol/lvol.sh +++ b/test/lvol/lvol.sh @@ -66,6 +66,8 @@ function usage() { 755: 'clone_writing_clone', 756: 'clone_and_snapshot_consistency', 757: 'clone_inflate', + 758: 'clone_decouple_parent', + 759: 'clone_decouple_parent_rw', 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 1db578447..a19cb96e9 100644 --- a/test/lvol/rpc_commands_lib.py +++ b/test/lvol/rpc_commands_lib.py @@ -225,3 +225,8 @@ class Commands_Rpc(object): print("INFO: RPC COMMAND inflate_lvol_bdev") output, rc = self.rpc.inflate_lvol_bdev(clone_name) return rc + + def decouple_parent_lvol_bdev(self, clone_name): + print("INFO: RPC COMMAND decouple_parent_lvol_bdev") + output, rc = self.rpc.decouple_parent_lvol_bdev(clone_name) + return rc diff --git a/test/lvol/test_cases.py b/test/lvol/test_cases.py index e299a4e13..7b0e4a429 100644 --- a/test/lvol/test_cases.py +++ b/test/lvol/test_cases.py @@ -135,6 +135,8 @@ def case_message(func): 755: 'clone_writing_clone', 756: 'clone_and_snapshot_consistency', 757: 'clone_inflate', + 758: 'decouple_parent', + 759: 'decouple_parent_rw', 800: 'rename_positive', 801: 'rename_lvs_nonexistent', 802: 'rename_lvs_EEXIST', @@ -1895,6 +1897,194 @@ class TestCases(object): # - no other operation fails return fail_count + @case_message + def test_case758(self): + """ + clone_decouple_parent + + Detach parent from clone and check if parent can be safely removed. + Check data consistency. + """ + + fail_count = 0 + snapshot_name = "snapshot" + nbd_name = "/dev/nbd0" + + # 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']) / 4 / MEGABYTE) + + # Construct thin provisioned lvol bdev + uuid_bdev0 = self.c.construct_lvol_bdev(uuid_store, + self.lbd_name, size, thin=True) + lvol_bdev = self.c.get_lvol_bdev_with_name(uuid_bdev0) + + # Decouple parent lvol bdev and check if it fails + ret_value = self.c.decouple_parent_lvol_bdev(lvol_bdev['name']) + if ret_value == 0: + print("ERROR: Decouple parent on bdev without parent should " + "fail but didn't") + fail_count += 1 + + # Create snapshot of thin 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) + + # Try to destroy snapshot and check if it fails + ret_value = self.c.destroy_lvol_bdev(snapshot_bdev['name']) + if ret_value == 0: + print("ERROR: Delete snapshot should fail but didn't") + fail_count += 1 + + # Decouple parent lvol bdev + fail_count += self.c.decouple_parent_lvol_bdev(lvol_bdev['name']) + lvol_bdev = self.c.get_lvol_bdev_with_name(uuid_bdev0) + snapshot_bdev = self.c.get_lvol_bdev_with_name(self.lvs_name + "/" + snapshot_name) + if lvol_bdev['driver_specific']['lvol']['thin_provision'] is not True: + fail_count += 1 + if lvol_bdev['driver_specific']['lvol']['clone'] is not False: + fail_count += 1 + if lvol_bdev['driver_specific']['lvol']['snapshot'] is not False: + fail_count += 1 + if snapshot_bdev['driver_specific']['lvol']['clone'] is not False: + fail_count += 1 + + # Destroy snapshot + fail_count += self.c.destroy_lvol_bdev(snapshot_bdev['name']) + + # Destroy lvol bdev + fail_count += self.c.destroy_lvol_bdev(lvol_bdev['name']) + + # Destroy lvol store + fail_count += self.c.destroy_lvol_store(uuid_store) + + # Delete malloc + fail_count += self.c.delete_malloc_bdev(base_name) + + # Expected result: + # - calls successful, return code = 0 + # - no other operation fails + return fail_count + + @case_message + def test_case759(self): + """ + clone_decouple_parent_rw + + Create tree level snaphot-snapshot2-clone structure. + Detach snapshot2 from clone. Check if snapshot2 can be safely removed. + Each time check consistency of snapshot-clone relations and written data. + """ + fail_count = 0 + snapshot_name = "snapshot" + snapshot_name2 = "snapshot2" + nbd_name = "/dev/nbd0" + + # 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(5 * lvs[0]['cluster_size'] / MEGABYTE) + + # Construct thin provisioned lvol bdev + uuid_bdev0 = self.c.construct_lvol_bdev(uuid_store, + self.lbd_name, size, thin=True) + lvol_bdev = self.c.get_lvol_bdev_with_name(uuid_bdev0) + + # Fill first four out of 5 culsters of clone with data of known pattern + fail_count += self.c.start_nbd_disk(lvol_bdev['name'], nbd_name) + begin_fill = 0 + end_fill = int(size * 4 / 5) + fail_count += self.run_fio_test(nbd_name, begin_fill * MEGABYTE, + end_fill * MEGABYTE, "write", "0xdd", 0) + + # Create snapshot of thin 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) + + # Fill second and fourth cluster of clone with data of known pattern + start_fill = int(size / 5) + fill_range = int(size / 5) + fail_count += self.run_fio_test(nbd_name, start_fill * MEGABYTE, + fill_range * MEGABYTE, "write", "0xcc", 0) + start_fill = int(size * 3 / 5) + fail_count += self.run_fio_test(nbd_name, start_fill * MEGABYTE, + fill_range * MEGABYTE, "write", "0xcc", 0) + + # Create second snapshot of thin provisioned lvol bdev + fail_count += self.c.snapshot_lvol_bdev(lvol_bdev['name'], snapshot_name2) + snapshot_bdev2 = self.c.get_lvol_bdev_with_name(self.lvs_name + "/" + snapshot_name2) + + # Fill second cluster of clone with data of known pattern + start_fill = int(size / 5) + fail_count += self.run_fio_test(nbd_name, start_fill * MEGABYTE, + fill_range * MEGABYTE, "write", "0xee", 0) + + # Check data consistency + pattern = ["0xdd", "0xee", "0xdd", "0xcc", "0x00"] + for i in range(0, 5): + begin_fill = int(size * i / 5) + fail_count += self.run_fio_test(nbd_name, begin_fill * MEGABYTE, + fill_range * MEGABYTE, "read", pattern[i]) + + # Delete snapshot and check if it fails + ret_value = self.c.destroy_lvol_bdev(snapshot_bdev2['name']) + if ret_value == 0: + print("ERROR: Delete snapshot should fail but didn't") + fail_count += 1 + + # Decouple parent + fail_count += self.c.decouple_parent_lvol_bdev(lvol_bdev['name']) + lvol_bdev = self.c.get_lvol_bdev_with_name(uuid_bdev0) + + # Check data consistency + for i in range(0, 5): + begin_fill = int(size * i / 5) + fail_count += self.run_fio_test(nbd_name, begin_fill * MEGABYTE, + fill_range * MEGABYTE, "read", pattern[i]) + + # Delete second snapshot + ret_value = self.c.destroy_lvol_bdev(snapshot_bdev2['name']) + + # Check data consistency + for i in range(0, 5): + begin_fill = int(size * i / 5) + fail_count += self.run_fio_test(nbd_name, begin_fill * MEGABYTE, + fill_range * MEGABYTE, "read", pattern[i]) + + # Destroy lvol bdev + fail_count += self.c.destroy_lvol_bdev(lvol_bdev['name']) + + # Destroy snapshot + fail_count += self.c.destroy_lvol_bdev(snapshot_bdev['name']) + + # Destroy lvol store + fail_count += self.c.destroy_lvol_store(uuid_store) + + # Delete malloc + fail_count += self.c.delete_malloc_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