def test_snapshot_revert_with_backing_file( grpc_backing_controller, # NOQA grpc_backing_replica1, # NOQA grpc_backing_replica2): # NOQA address = grpc_backing_controller.address dev = get_backing_dev(grpc_backing_replica1, grpc_backing_replica2, grpc_backing_controller) offset = 0 length = 256 snap0 = cmd.snapshot_create(address) before = read_dev(dev, offset, length) assert before != "" info = cmd.snapshot_info(address) assert len(info) == 2 assert VOLUME_HEAD in info assert snap0 in info exists = read_from_backing_file(offset, length) assert before == exists snapshot_revert_test(dev, address, ENGINE_BACKING_NAME) snapshot_revert_with_frontend(address, ENGINE_BACKING_NAME, snap0) after = read_dev(dev, offset, length) assert before == after
def snapshot_revert_test(dev, address, engine_name): # NOQA existings = {} snap1 = Snapshot(dev, generate_random_data(existings), address) snap2 = Snapshot(dev, generate_random_data(existings), address) snap3 = Snapshot(dev, generate_random_data(existings), address) snapList = cmd.snapshot_ls(address) assert snap1.name in snapList assert snap2.name in snapList assert snap3.name in snapList snapshot_revert_with_frontend(address, engine_name, snap2.name) snap3.refute_data() snap2.verify_checksum() snap1.verify_data() snapshot_revert_with_frontend(address, engine_name, snap1.name) snap3.refute_data() snap2.refute_data() snap1.verify_checksum()
def test_snapshot_rm_basic( grpc_controller, # NOQA grpc_replica1, grpc_replica2): # NOQA address = grpc_controller.address dev = get_dev(grpc_replica1, grpc_replica2, grpc_controller) existings = {} snap1 = Snapshot(dev, generate_random_data(existings), address) snap2 = Snapshot(dev, generate_random_data(existings), address) snap3 = Snapshot(dev, generate_random_data(existings), address) info = cmd.snapshot_info(address) assert len(info) == 4 assert VOLUME_HEAD in info assert snap1.name in info assert snap2.name in info assert snap3.name in info cmd.snapshot_rm(address, snap2.name) cmd.snapshot_purge(address) wait_for_purge_completion(address) info = cmd.snapshot_info(address) assert len(info) == 3 assert snap1.name in info assert snap3.name in info snap3.verify_checksum() snap2.verify_data() snap1.verify_data() snapshot_revert_with_frontend(address, ENGINE_NAME, snap1.name) snap3.refute_data() snap2.refute_data() snap1.verify_checksum()
def snapshot_tree_build(dev, address, engine_name, offset, length, strict=True): # snap["0a"] -> snap["0b"] -> snap["0c"] # |-> snap["1a"] -> snap["1b"] -> snap["1c"] # \-> snap["2a"] -> snap["2b"] -> snap["2c"] # \-> snap["3a"] -> snap["3b"] -> snap["3c"] -> head snap = {} data = {} snapshot_tree_create_node(dev, address, offset, length, snap, data, "0a") snapshot_tree_create_node(dev, address, offset, length, snap, data, "0b") snapshot_tree_create_node(dev, address, offset, length, snap, data, "0c") snapshot_revert_with_frontend(address, engine_name, snap["0b"]) snapshot_tree_create_node(dev, address, offset, length, snap, data, "1a") snapshot_tree_create_node(dev, address, offset, length, snap, data, "1b") snapshot_tree_create_node(dev, address, offset, length, snap, data, "1c") snapshot_revert_with_frontend(address, engine_name, snap["0b"]) snapshot_tree_create_node(dev, address, offset, length, snap, data, "2a") snapshot_tree_create_node(dev, address, offset, length, snap, data, "2b") snapshot_tree_create_node(dev, address, offset, length, snap, data, "2c") snapshot_revert_with_frontend(address, engine_name, snap["2a"]) snapshot_tree_create_node(dev, address, offset, length, snap, data, "3a") snapshot_tree_create_node(dev, address, offset, length, snap, data, "3b") snapshot_tree_create_node(dev, address, offset, length, snap, data, "3c") snapshot_tree_verify(dev, address, engine_name, offset, length, snap, data, strict) return snap, data
def volume_expansion_with_snapshots_test( dev, grpc_controller, # NOQA volume_name, engine_name, original_data): # the default size is 4MB, will expand it to 8MB address = grpc_controller.address zero_char = b'\x00'.decode('utf-8') # write the data to the original part then do expansion data1_len = random_length(PAGE_SIZE) data1 = Data(random.randrange(0, SIZE - 2 * PAGE_SIZE, PAGE_SIZE), data1_len, random_string(data1_len)) snap1 = Snapshot(dev, data1, address) grpc_controller.volume_expand(EXPAND_SIZE) wait_for_volume_expansion(grpc_controller, EXPAND_SIZE) check_block_device_size(volume_name, EXPAND_SIZE) snap1.verify_data() assert \ dev.readat(0, SIZE) == \ original_data[0:data1.offset] + data1.content + \ original_data[data1.offset+data1.length:] assert dev.readat(SIZE, SIZE) == zero_char * SIZE # write the data to both the original part and the expanded part data2_len = random_length(PAGE_SIZE) data2 = Data(SIZE - PAGE_SIZE, data2_len, random_string(data2_len)) snap2 = Snapshot(dev, data2, address) data3_len = random_length(PAGE_SIZE) data3 = Data(random.randrange(SIZE, EXPAND_SIZE - PAGE_SIZE, PAGE_SIZE), data3_len, random_string(data3_len)) snap3 = Snapshot(dev, data3, address) snap1.verify_data() snap2.verify_data() snap3.verify_data() assert \ dev.readat(SIZE, SIZE) == zero_char*(data3.offset-SIZE) + \ data3.content + zero_char*(EXPAND_SIZE-data3.offset-data3.length) data4_len = random_length(PAGE_SIZE) data4 = Data(data1.offset, data4_len, random_string(data4_len)) snap4 = Snapshot(dev, data4, address) snap4.verify_data() # revert to snap1 then see if we can still r/w the existing data # and expanded part snapshot_revert_with_frontend(address, engine_name, snap1.name) assert \ dev.readat(0, SIZE) == \ original_data[0:data1.offset] + data1.content + \ original_data[data1.offset+data1.length:] assert dev.readat(SIZE, SIZE) == zero_char * SIZE data5_len = random_length(PAGE_SIZE) data5 = Data(random.randrange(SIZE, EXPAND_SIZE - PAGE_SIZE, PAGE_SIZE), data5_len, random_string(data5_len)) snap5 = Snapshot(dev, data5, address) snap5.verify_data() assert \ dev.readat(SIZE, SIZE) == zero_char*(data5.offset-SIZE) + \ data5.content + zero_char*(EXPAND_SIZE-data5.offset-data5.length) # delete and purge the snap1. it will coalesce with the larger snap2 cmd.snapshot_rm(address, snap1.name) cmd.snapshot_purge(address) wait_for_purge_completion(address) assert \ dev.readat(0, SIZE) == \ original_data[0:data1.offset] + data1.content + \ original_data[data1.offset+data1.length:] assert \ dev.readat(SIZE, SIZE) == zero_char*(data5.offset-SIZE) + \ data5.content + zero_char*(EXPAND_SIZE-data5.offset-data5.length)
def test_snapshot_tree_basic( grpc_controller, # NOQA grpc_replica1, grpc_replica2): # NOQA address = grpc_controller.address dev = get_dev(grpc_replica1, grpc_replica2, grpc_controller) offset = 0 length = 128 snap, data = snapshot_tree_build(dev, address, ENGINE_NAME, offset, length) snapshot_revert_with_frontend(address, ENGINE_NAME, snap["1b"]) cmd.snapshot_rm(address, snap["0a"]) cmd.snapshot_rm(address, snap["0b"]) cmd.snapshot_rm(address, snap["1c"]) cmd.snapshot_rm(address, snap["2a"]) cmd.snapshot_rm(address, snap["2b"]) cmd.snapshot_rm(address, snap["2c"]) cmd.snapshot_rm(address, snap["3a"]) cmd.snapshot_rm(address, snap["3b"]) cmd.snapshot_rm(address, snap["3c"]) cmd.snapshot_purge(address) wait_for_purge_completion(address) # the result should looks like this # snap["0b"](r) -> snap["0c"] # \-> snap["1a"] -> snap["1b"] -> head info = cmd.snapshot_info(address) assert len(info) == 5 assert snap["0b"] in info assert info[snap["0b"]]["parent"] == "" assert len(info[snap["0b"]]["children"]) == 2 assert snap["0c"] in info[snap["0b"]]["children"] assert snap["1a"] in info[snap["0b"]]["children"] assert info[snap["0b"]]["removed"] is True assert snap["0c"] in info assert info[snap["0c"]]["parent"] == snap["0b"] assert not info[snap["0c"]]["children"] assert snap["1a"] in info assert info[snap["1a"]]["parent"] == snap["0b"] assert snap["1b"] in info[snap["1a"]]["children"] assert snap["1b"] in info assert info[snap["1b"]]["parent"] == snap["1a"] assert VOLUME_HEAD in info[snap["1b"]]["children"] assert VOLUME_HEAD in info assert info[VOLUME_HEAD]["parent"] == snap["1b"] snapshot_tree_verify_node(dev, address, ENGINE_NAME, offset, length, snap, data, "0b") snapshot_tree_verify_node(dev, address, ENGINE_NAME, offset, length, snap, data, "0c") snapshot_tree_verify_node(dev, address, ENGINE_NAME, offset, length, snap, data, "1a") snapshot_tree_verify_node(dev, address, ENGINE_NAME, offset, length, snap, data, "1b")
def restore_to_file_with_backing_file_test( backup_target, # NOQA grpc_backing_controller, # NOQA grpc_backing_replica1, # NOQA grpc_backing_replica2): # NOQA address = grpc_backing_controller.address backing_dev = get_backing_dev(grpc_backing_replica1, grpc_backing_replica2, grpc_backing_controller) length0 = 4 * 1024 length1 = 256 length2 = 128 offset0 = 0 offset1 = length1 + offset0 offset2 = length2 + offset0 output_raw_path = file(OUTPUT_FILE_RAW) output_qcow2_path = file(OUTPUT_FILE_QCOW2) # create 1 empty snapshot. # data in output image == data in backing check_backing() check_empty_volume(backing_dev) snap0 = cmd.snapshot_create(address) backup = create_backup(address, snap0, backup_target)["URL"] volume_data = read_dev(backing_dev, offset0, length0) backing_data = read_from_backing_file(offset0, length0) dev_checksum = checksum_dev(backing_dev) assert volume_data != "" assert volume_data == backing_data cmd.restore_to_file(address, backup, file(BACKING_FILE_QCOW), output_raw_path, IMAGE_FORMAT_RAW) output0_raw = read_file(output_raw_path, offset0, length0) output0_checksum = checksum_data( read_file(output_raw_path, 0, SIZE).encode('utf-8')) assert output0_raw == backing_data assert output0_checksum == dev_checksum os.remove(output_raw_path) assert not os.path.exists(output_raw_path) cmd.restore_to_file(address, backup, file(BACKING_FILE_QCOW), output_qcow2_path, IMAGE_FORMAT_QCOW2) output0_qcow2 = read_qcow2_file_without_backing_file( output_qcow2_path, offset0, length0) output0_checksum = checksum_data( read_qcow2_file_without_backing_file(output_qcow2_path, 0, SIZE)) assert output0_qcow2.decode('utf-8') == backing_data assert output0_qcow2.decode('utf-8') == volume_data assert output0_checksum == dev_checksum os.remove(output_qcow2_path) assert not os.path.exists(output_qcow2_path) rm_backups(address, ENGINE_BACKING_NAME, [backup]) # create 1 snapshot with 256B data. # output = snap1(offset0, length1) + backing(offset1, ...) snap1_data = random_string(length1) verify_data(backing_dev, offset0, snap1_data) snap1 = cmd.snapshot_create(address) backup = create_backup(address, snap1, backup_target)["URL"] volume_data = read_dev(backing_dev, offset0, length0) backing_data = read_from_backing_file(offset1, length0 - offset1) dev_checksum = checksum_dev(backing_dev) cmd.restore_to_file(address, backup, file(BACKING_FILE_QCOW), output_raw_path, IMAGE_FORMAT_RAW) output1_raw_snap1 = read_file(output_raw_path, offset0, length1) output1_raw_backing = read_file(output_raw_path, offset1, length0 - offset1) output1_checksum = checksum_data( read_file(output_raw_path, 0, SIZE).encode('utf-8')) assert output1_raw_snap1 == snap1_data assert output1_raw_backing == backing_data assert output1_raw_snap1 + output1_raw_backing == volume_data assert output1_checksum == dev_checksum os.remove(output_raw_path) assert not os.path.exists(output_raw_path) cmd.restore_to_file(address, backup, file(BACKING_FILE_QCOW), output_qcow2_path, IMAGE_FORMAT_QCOW2) output1_qcow2_snap1 = read_qcow2_file_without_backing_file( output_qcow2_path, offset0, length1) output1_qcow2_backing = read_qcow2_file_without_backing_file( output_qcow2_path, offset1, length0 - offset1) output1_checksum = checksum_data( read_qcow2_file_without_backing_file(output_qcow2_path, 0, SIZE)) assert output1_qcow2_snap1.decode('utf-8') == snap1_data assert output1_qcow2_backing.decode('utf-8') == backing_data assert output1_qcow2_snap1.decode('utf-8') + \ output1_qcow2_backing.decode('utf-8') == volume_data assert output1_checksum == dev_checksum os.remove(output_qcow2_path) assert not os.path.exists(output_qcow2_path) snapshot_revert_with_frontend(address, ENGINE_BACKING_NAME, snap0) rm_snaps(address, [snap1]) rm_backups(address, ENGINE_BACKING_NAME, [backup]) check_backing() check_empty_volume(backing_dev) # create 2 snapshots with 256B data and 128B data # output = snap2(offset0, length1 - length2) + # snap1(offset2, length2) + backing(offset2, ...) snap1_data = random_string(length1) verify_data(backing_dev, offset0, snap1_data) snap1 = cmd.snapshot_create(address) snap2_data = random_string(length2) verify_data(backing_dev, offset0, snap2_data) snap2 = cmd.snapshot_create(address) backup = create_backup(address, snap2, backup_target)["URL"] volume_data = read_dev(backing_dev, offset0, length0) backing_data = read_from_backing_file(offset1, length0 - offset1) dev_checksum = checksum_dev(backing_dev) cmd.restore_to_file(address, backup, file(BACKING_FILE_QCOW), output_raw_path, IMAGE_FORMAT_RAW) output2_raw_snap2 = read_file(output_raw_path, offset0, length2) output2_raw_snap1 = read_file(output_raw_path, offset2, length1 - length2) output2_raw_backing = read_file(output_raw_path, offset1, length0 - offset1) output2_checksum = checksum_data( read_file(output_raw_path, 0, SIZE).encode('utf-8')) assert output2_raw_snap2 == snap2_data assert output2_raw_snap1 == snap1_data[offset2:length1] assert output2_raw_backing == backing_data assert \ volume_data == \ output2_raw_snap2 + output2_raw_snap1 + output2_raw_backing assert output2_checksum == dev_checksum os.remove(output_raw_path) assert not os.path.exists(output_raw_path) cmd.restore_to_file(address, backup, file(BACKING_FILE_QCOW), output_qcow2_path, IMAGE_FORMAT_QCOW2) output2_qcow2_snap2 = read_qcow2_file_without_backing_file( output_qcow2_path, offset0, length2) output2_qcow2_snap1 = read_qcow2_file_without_backing_file( output_qcow2_path, offset2, length1 - length2) output2_qcow2_backing = read_qcow2_file_without_backing_file( output_qcow2_path, offset1, length0 - offset1) output2_checksum = checksum_data( read_qcow2_file_without_backing_file(output_qcow2_path, 0, SIZE)) assert output2_qcow2_snap2.decode('utf-8') == snap2_data assert output2_qcow2_snap1.decode('utf-8') == snap1_data[offset2:length1] assert output2_qcow2_backing.decode('utf-8') == backing_data assert \ volume_data == \ output2_qcow2_snap2.decode('utf-8') + \ output2_qcow2_snap1.decode('utf-8') + \ output1_qcow2_backing.decode('utf-8') assert output2_checksum == dev_checksum os.remove(output_qcow2_path) assert not os.path.exists(output_qcow2_path) snapshot_revert_with_frontend(address, ENGINE_BACKING_NAME, snap0) rm_snaps(address, [snap1, snap2]) rm_backups(address, ENGINE_BACKING_NAME, [backup]) check_backing() check_empty_volume(backing_dev)
def restore_to_file_without_backing_file_test( backup_target, # NOQA grpc_controller, # NOQA grpc_replica1, # NOQA grpc_replica2): # NOQA address = grpc_controller.address dev = get_dev(grpc_replica1, grpc_replica2, grpc_controller) length0 = 256 length1 = 128 offset0 = 0 offset1 = length1 + offset0 output_raw_path = file(OUTPUT_FILE_RAW) output_qcow2_path = file(OUTPUT_FILE_QCOW2) # create 1 empty snapshot for converting to init state. snap0 = cmd.snapshot_create(address) # create 1 snapshot with 256B data. # output = snap2(offset0, length1) snap1_data = random_string(length0) verify_data(dev, offset0, snap1_data) snap1 = cmd.snapshot_create(address) backup = create_backup(address, snap1, backup_target)["URL"] cmd.restore_to_file(address, backup, "", output_raw_path, IMAGE_FORMAT_RAW) output1_raw = read_file(output_raw_path, offset0, length0) assert output1_raw == snap1_data os.remove(output_raw_path) assert not os.path.exists(output_raw_path) cmd.restore_to_file(address, backup, "", output_qcow2_path, IMAGE_FORMAT_QCOW2) output1_qcow2 = read_qcow2_file_without_backing_file( output_qcow2_path, offset0, length0) assert output1_qcow2.decode('utf-8') == snap1_data os.remove(output_qcow2_path) assert not os.path.exists(output_qcow2_path) snapshot_revert_with_frontend(address, ENGINE_NAME, snap0) rm_snaps(address, [snap1]) rm_backups(address, ENGINE_NAME, [backup]) # create 2 snapshots with 256B data and 128B data # output = snap2(offset0, length1 - length2) + # snap1(offset2, length2) snap1_data = random_string(length0) verify_data(dev, offset0, snap1_data) snap1 = cmd.snapshot_create(address) snap2_data = random_string(length1) verify_data(dev, offset0, snap2_data) snap2 = cmd.snapshot_create(address) backup = create_backup(address, snap2, backup_target)["URL"] cmd.restore_to_file(address, backup, "", output_raw_path, IMAGE_FORMAT_RAW) output2_raw_snap2 = read_file(output_raw_path, offset0, length1) output2_raw_snap1 = read_file(output_raw_path, offset1, length0 - length1) assert output2_raw_snap2 == snap2_data assert output2_raw_snap1 == snap1_data[offset1:length0] cmd.restore_to_file(address, backup, "", output_qcow2_path, IMAGE_FORMAT_QCOW2) output2_qcow2_snap2 = read_qcow2_file_without_backing_file( output_qcow2_path, offset0, length1) output2_qcow2_snap1 = read_qcow2_file_without_backing_file( output_qcow2_path, offset1, length0 - length1) assert output2_qcow2_snap2.decode('utf-8') == snap2_data assert output2_qcow2_snap1.decode('utf-8') == snap1_data[offset1:length0] os.remove(output_qcow2_path) assert not os.path.exists(output_qcow2_path) snapshot_revert_with_frontend(address, ENGINE_NAME, snap0) rm_snaps(address, [snap1, snap2]) rm_backups(address, ENGINE_NAME, [backup])
def snapshot_tree_verify_node(dev, address, engine_name, offset, length, snap, data, name): snapshot_revert_with_frontend(address, engine_name, snap[name]) readed = read_dev(dev, offset, length) assert readed == data[name]