def create_partitions(device, sizes: [], partition_table_type=PartitionTable.gpt): from storage_devices.partition import Partition if create_partition_table(device, partition_table_type): device.partition_table = partition_table_type partition_type = PartitionType.primary partition_number_offset = 0 for s in sizes: size = Size( s.get_value(device.block_size) - device.block_size.value, device.block_size) if partition_table_type == PartitionTable.msdos and \ len(sizes) > 4 and len(device.partitions) == 3: create_partition(device, Size.zero(), 4, PartitionType.extended, Unit.MebiByte, True) partition_type = PartitionType.logical partition_number_offset = 1 partition_number = len( device.partitions) + 1 + partition_number_offset if create_partition(device, size, partition_number, partition_type, Unit.MebiByte, True): new_part = Partition(device, partition_type, partition_number) dd = Dd().input("/dev/zero") \ .output(new_part.system_path) \ .count(1) \ .block_size(Size(1, Unit.Blocks4096)) \ .oflag("direct") dd.run() device.partitions.append(new_part)
def run_dd(target_path, count, seek): dd = Dd() \ .input("/dev/zero") \ .output(target_path) \ .block_size(Size(1, Unit.Blocks4096)) \ .count(count) \ .oflag("direct") \ .seek(seek) dd.run() TestRun.LOGGER.info(f"dd command:\n{dd}")
def create_partition( device, part_size, part_number, part_type: PartitionType = PartitionType.primary, unit=Unit.MebiByte, aligned: bool = True): TestRun.LOGGER.info( f"Creating {part_type.name} partition on device: {device.path}") begin = get_first_partition_offset(device, aligned) for part in device.partitions: begin += part.size if part.type == PartitionType.logical: begin += Size(1, Unit.MebiByte if not aligned else device.block_size) if part_type == PartitionType.logical: begin += Size(1, Unit.MebiByte if not aligned else device.block_size) end = (begin + part_size) if part_size != Size.zero() else '100%' cmd = f'parted --script {device.path} mkpart ' \ f'{part_type.name} ' \ f'{begin.get_value(unit)}{unit_to_string(unit)} ' \ f'{end.get_value(unit)}{unit_to_string(unit)}' output = TestRun.executor.run(cmd) if output.exit_code != 0: TestRun.executor.run_expect_success("partprobe") TestRun.executor.run_expect_success("udevadm settle") if not check_partition_after_create( part_size, part_number, device.path, part_type, aligned): raise Exception("Could not create partition!") if part_type != PartitionType.extended: from storage_devices.partition import Partition new_part = Partition(device, part_type, part_number, begin, end if type(end) is Size else device.size) dd = Dd().input("/dev/zero") \ .output(new_part.path) \ .count(1) \ .block_size(Size(1, Unit.Blocks4096)) \ .oflag("direct") dd.run() device.partitions.append(new_part) TestRun.LOGGER.info(f"Successfully created {part_type.name} partition on {device.path}")
def read_files_with_reclassification_check(cache, target_ioclass_id: int, source_ioclass_id: int, directory: Directory, with_delay: bool): start_time = datetime.now() target_occupancy_after = cache.get_io_class_statistics( io_class_id=target_ioclass_id).usage_stats.occupancy source_occupancy_after = cache.get_io_class_statistics( io_class_id=source_ioclass_id).usage_stats.occupancy files_to_reclassify = [] target_ioclass_is_enabled = ioclass_is_enabled(cache, target_ioclass_id) for file in [item for item in directory.ls() if isinstance(item, File)]: target_occupancy_before = target_occupancy_after source_occupancy_before = source_occupancy_after time_from_start = datetime.now() - start_time dd = Dd().input(file.full_path).output("/dev/null").block_size( Size(1, Unit.Blocks4096)) dd.run() target_occupancy_after = cache.get_io_class_statistics( io_class_id=target_ioclass_id).usage_stats.occupancy source_occupancy_after = cache.get_io_class_statistics( io_class_id=source_ioclass_id).usage_stats.occupancy if target_ioclass_is_enabled: if target_occupancy_after < target_occupancy_before: TestRun.LOGGER.error("Target IO class occupancy lowered!") elif target_occupancy_after - target_occupancy_before < file.size: files_to_reclassify.append(file) if with_delay and time_from_start <= ioclass_config.MAX_CLASSIFICATION_DELAY: continue TestRun.LOGGER.error( "Target IO class occupancy not changed properly!\n" f"Expected: {file.size + target_occupancy_before}\n" f"Actual: {target_occupancy_after}") elif target_occupancy_after > target_occupancy_before and with_delay: files_to_reclassify.append(file) if source_occupancy_after >= source_occupancy_before: if file not in files_to_reclassify: files_to_reclassify.append(file) if with_delay and time_from_start <= ioclass_config.MAX_CLASSIFICATION_DELAY: continue TestRun.LOGGER.error( "Source IO class occupancy not changed properly!\n" f"Before: {source_occupancy_before}\n" f"After: {source_occupancy_after}") if len(files_to_reclassify): TestRun.LOGGER.info("Rereading unclassified test files...") sync() drop_caches(DropCachesMode.ALL) for file in files_to_reclassify: (Dd().input(file.full_path).output("/dev/null").block_size( Size(1, Unit.Blocks4096)).run())
def copy_file(source, target, size, direct=None): dd = Dd() \ .input(source) \ .output(target) \ .block_size(Size(1, Unit.Blocks4096)) \ .count(int(size.get_value(Unit.Blocks4096))) if direct == "oflag": dd.oflag("direct") elif direct == "iflag": dd.iflag("direct") dd.run()
def create_test_file(): from test_utils.filesystem.file import File from test_tools.dd import Dd bs = Size(512, Unit.KibiByte) cnt = int(cache_size.value / bs.value) test_file = File.create_file(test_file_path) dd = Dd().output(test_file_path) \ .input("/dev/zero") \ .block_size(bs) \ .count(cnt) dd.run() test_file.refresh_item() return test_file
def create_random_test_file(target_file_path: str, file_size: Size = Size(1, Unit.MebiByte), random: bool = True): from test_utils.filesystem.file import File bs = Size(512, Unit.KibiByte) cnt = math.ceil(file_size.value / bs.value) file = File.create_file(target_file_path) dd = Dd().output(target_file_path) \ .input("/dev/urandom" if random else "/dev/zero") \ .block_size(bs) \ .count(cnt) \ .oflag("direct") dd.run() file.refresh_item() return file
def run_io_dir_read(path): dd = Dd().output("/dev/null").input(f"{path}") output = dd.run() if output.exit_code != 0: TestRun.fail(f"Failed to execute dd.\n {output.stdout}\n{output.stderr}") sync() drop_caches(DropCachesMode.ALL)
def remove_partitions(device): if device.is_mounted(): device.unmount() for partition in device.partitions: unmount(partition) TestProperties.LOGGER.info(f"Removing partitions from device: {device.system_path}.") dd = Dd().input("/dev/zero") \ .output(device.system_path) \ .count(1) \ .block_size(Size(device.block_size.value, Unit.Byte)) dd.run() output = TestProperties.executor.execute(f"ls {device.system_path}* -1") if len(output.stdout.split('\n')) > 1: TestProperties.LOGGER.error(f"Could not remove partitions from device {device.system_path}") return False return True
def test_clean_stop_cache(cache_mode): """ title: Test of the ability to stop cache in modes with lazy writes. description: | Test if OpenCAS stops cache in modes with lazy writes without data loss. pass_criteria: - Cache stopping works properly. - Writes to exported object and core device during OpenCAS's work are equal - Data on core device is correct after cache is stopped. """ with TestRun.step("Prepare devices for cache and core."): cache_dev = TestRun.disks['cache'] cache_dev.create_partitions([Size(256, Unit.MebiByte)]) cache_part = cache_dev.partitions[0] core_dev = TestRun.disks['core'] core_dev.create_partitions([Size(512, Unit.MebiByte)]) core_part = core_dev.partitions[0] Udev.disable() with TestRun.step(f"Start cache in {cache_mode} mode."): cache = casadm.start_cache(cache_part, cache_mode) with TestRun.step("Add core to cache."): core = cache.add_core(core_part) with TestRun.step("Disable cleaning and sequential cutoff."): cache.set_cleaning_policy(CleaningPolicy.nop) cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) with TestRun.step("Read IO stats before test"): core_disk_writes_initial = check_device_write_stats(core_part) exp_obj_writes_initial = check_device_write_stats(core) with TestRun.step("Write data to the exported object."): test_file_main = create_random_test_file("/tmp/test_file_main", Size(64, Unit.MebiByte)) dd = Dd().output(core.system_path) \ .input(test_file_main.full_path) \ .block_size(bs) \ .count(int(test_file_main.size / bs)) \ .oflag("direct") dd.run() test_file_md5sum_main = test_file_main.md5sum() with TestRun.step("Read IO stats after write to the exported object."): core_disk_writes_increase = ( check_device_write_stats(core_part) - core_disk_writes_initial ) exp_obj_writes_increase = ( check_device_write_stats(core) - exp_obj_writes_initial ) with TestRun.step("Validate IO stats after write to the exported object."): if core_disk_writes_increase > 0: TestRun.LOGGER.error("Writes should occur only on the exported object.") if exp_obj_writes_increase != test_file_main.size.value: TestRun.LOGGER.error("Not all writes reached the exported object.") with TestRun.step("Read data from the exported object."): test_file_1 = File.create_file("/tmp/test_file_1") dd = Dd().output(test_file_1.full_path) \ .input(core.system_path) \ .block_size(bs) \ .count(int(test_file_main.size / bs)) \ .oflag("direct") dd.run() test_file_1.refresh_item() sync() with TestRun.step("Compare md5 sum of test files."): if test_file_md5sum_main != test_file_1.md5sum(): TestRun.LOGGER.error("Md5 sums should be equal.") with TestRun.step("Read data from the core device."): test_file_2 = File.create_file("/tmp/test_file_2") dd = Dd().output(test_file_2.full_path) \ .input(core_part.system_path) \ .block_size(bs) \ .count(int(test_file_main.size / bs)) \ .oflag("direct") dd.run() test_file_2.refresh_item() sync() with TestRun.step("Compare md5 sum of test files."): if test_file_md5sum_main == test_file_2.md5sum(): TestRun.LOGGER.error("Md5 sums should be different.") with TestRun.step("Read IO stats before stopping cache."): core_disk_writes_before_stop = check_device_write_stats(core_part) with TestRun.step("Stop cache."): cache.stop() with TestRun.step("Read IO stats after stopping cache."): core_disk_writes_increase = ( check_device_write_stats(core_part) - core_disk_writes_before_stop ) with TestRun.step("Validate IO stats after stopping cache."): if core_disk_writes_increase == 0: TestRun.LOGGER.error("Writes should occur on the core device after stopping cache.") if core_disk_writes_increase != exp_obj_writes_increase: TestRun.LOGGER.error("Write statistics for the core device should be equal " "to those from the exported object.") with TestRun.step("Read data from the core device."): test_file_3 = File.create_file("/tmp/test_file_2") dd = Dd().output(test_file_3.full_path) \ .input(core_part.system_path) \ .block_size(bs) \ .count(int(test_file_main.size / bs)) \ .oflag("direct") dd.run() test_file_3.refresh_item() sync() with TestRun.step("Compare md5 sum of test files."): if test_file_md5sum_main != test_file_3.md5sum(): TestRun.LOGGER.error("Md5 sums should be equal.") with TestRun.step("Delete test files."): test_file_main.remove(True) test_file_1.remove(True) test_file_2.remove(True) test_file_3.remove(True)
def test_clean_remove_core_with_fs(cache_mode, fs): """ title: Test of the ability to remove core from cache in lazy-write modes with filesystem. description: | Test if OpenCAS removes core from cache in modes with lazy writes and with different filesystems without data loss. pass_criteria: - Core removing works properly. - Data on core device is correct after core is removed. """ with TestRun.step("Prepare devices for cache and core."): cache_dev = TestRun.disks['cache'] cache_dev.create_partitions([Size(256, Unit.MebiByte)]) cache_part = cache_dev.partitions[0] core_dev = TestRun.disks['core'] core_dev.create_partitions([Size(512, Unit.MebiByte)]) core_part = core_dev.partitions[0] Udev.disable() with TestRun.step(f"Start cache in {cache_mode} mode."): cache = casadm.start_cache(cache_part, cache_mode) with TestRun.step(f"Add core with {fs.name} filesystem to cache and mount it."): core_part.create_filesystem(fs) core = cache.add_core(core_part) core.mount(mnt_point) with TestRun.step("Disable cleaning and sequential cutoff."): cache.set_cleaning_policy(CleaningPolicy.nop) cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) with TestRun.step("Create test file and read its md5 sum."): test_file_main = create_random_test_file("/tmp/test_file_main", Size(64, Unit.MebiByte)) test_file_md5sum_main = test_file_main.md5sum() with TestRun.step("Copy test file to the exported object."): test_file_1 = File.create_file(mnt_point + "test_file_1") dd = Dd().output(test_file_1.full_path) \ .input(test_file_main.full_path) \ .block_size(bs) \ .count(int(test_file_main.size / bs)) \ .oflag("direct") dd.run() test_file_1.refresh_item() sync() with TestRun.step("Compare md5 sum of test files."): if test_file_md5sum_main != test_file_1.md5sum(): TestRun.LOGGER.error("Md5 sums should be equal.") with TestRun.step("Unmount and remove core."): core.unmount() core.remove_core() with TestRun.step("Mount core device."): core_part.mount(mnt_point) with TestRun.step("Read data from the core device."): test_file_2 = File.create_file("/tmp/test_file_2") dd = Dd().output(test_file_2.full_path) \ .input(test_file_1.full_path) \ .block_size(bs) \ .count(int(test_file_1.size / bs)) \ .oflag("direct") dd.run() test_file_2.refresh_item() sync() with TestRun.step("Compare md5 sum of test files."): if test_file_md5sum_main != test_file_2.md5sum(): TestRun.LOGGER.error("Md5 sums should be equal.") with TestRun.step("Delete test files."): test_file_main.remove(True) test_file_1.remove(True) test_file_2.remove(True) with TestRun.step("Unmount core device."): core_part.unmount() remove(mnt_point, True, True, True)
def run_io_dir_read(path): dd = Dd().output("/dev/null").input(f"{path}") dd.run() sync() drop_caches(DropCachesMode.ALL)
def padding(self, size: Size): dd = Dd().input("/dev/zero").output(self).count(1).block_size(size) dd.run() self.refresh_item()
def test_preserve_data_for_inactive_device(): """ title: Validate preserving data for inactive CAS devices. description: Validate that cached data for inactive CAS devices is preserved. pass_criteria: - No kernel error - File md5 checksums match in every iteration. - Cache read hits increase after reads (md5 checksum) from CAS device with attached core. """ mount_dir = "/mnt/test" with TestRun.step("Prepare devices."): devices = prepare_devices([("cache", 1), ("core", 1)]) cache_dev = devices["cache"].partitions[0] core_dev = devices["core"].partitions[0] plug_device = devices["core"] with TestRun.step("Start cache and add core."): cache = casadm.start_cache(cache_dev, cache_mode=CacheMode.WB, force=True) cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) cache.set_cleaning_policy(CleaningPolicy.nop) core = cache.add_core(core_dev) with TestRun.step( "Create init config file using current CAS configuration."): InitConfig.create_init_config_from_running_configuration() with TestRun.step("Create filesystem on CAS device and mount it."): core.create_filesystem(Filesystem.ext3) core.mount(mount_dir) with TestRun.step( "Create a test file with random writes on mount point and count it's md5." ): file_path = f"{mount_dir}/test_file" test_file = File.create_file(file_path) dd = Dd().input("/dev/random") \ .output(file_path) \ .count(100) \ .block_size(Size(1, Unit.Blocks512)) dd.run() os_utils.sync() md5_after_create = test_file.md5sum() cache_stats_before_stop = cache.get_statistics() core_stats_before_stop = core.get_statistics() with TestRun.step("Unmount CAS device."): core.unmount() with TestRun.step("Stop cache without flushing dirty data."): cache.stop(no_data_flush=True) with TestRun.step("Unplug core device."): plug_device.unplug() with TestRun.step("Load cache."): cache = casadm.load_cache(cache_dev) cache_stats_after_load = cache.get_statistics() core_stats_after_load = core.get_statistics() if cache_stats_before_stop.usage_stats.clean != cache_stats_after_load.usage_stats.clean or\ cache_stats_before_stop.usage_stats.dirty != \ cache_stats_after_load.usage_stats.dirty or\ core_stats_before_stop.usage_stats.clean != \ core_stats_after_load.usage_stats.clean or\ core_stats_before_stop.usage_stats.dirty != core_stats_after_load.usage_stats.dirty: TestRun.fail( f"Statistics after counting md5 are different than after cache load.\n" f"Cache stats before: {cache_stats_before_stop}\n" f"Cache stats after: {cache_stats_after_load}\n" f"Core stats before: {core_stats_before_stop}\n" f"Core stats after: {core_stats_after_load}") with TestRun.step( "Plug core disk using sysfs and verify this change is reflected " "on the cache list."): plug_device.plug() if cache.get_status() != CacheStatus.running or core.get_status( ) != CoreStatus.active: TestRun.fail( f"Expected cache status is running (actual - {cache.get_status()}).\n" f"Expected core status is active (actual - {core.get_status()})." ) with TestRun.step("Mount CAS device"): core.mount(mount_dir) with TestRun.step( "Count md5 checksum for test file and compare it with previous value." ): cache_read_hits_before_md5 = cache.get_statistics( ).request_stats.read.hits md5_after_cache_load = test_file.md5sum() if md5_after_create != md5_after_cache_load: TestRun.fail( "Md5 checksum after cache load operation is different than before " "stopping cache.") else: TestRun.LOGGER.info( "Md5 checksum is identical before and after cache load operation " "with inactive CAS device.") with TestRun.step( "Verify that cache read hits increased after counting md5 checksum." ): cache_read_hits_after_md5 = cache.get_statistics( ).request_stats.read.hits if cache_read_hits_after_md5 - cache_read_hits_before_md5 < 0: TestRun.fail( f"Cache read hits did not increase after counting md5 checksum. " f"Before: {cache_read_hits_before_md5}. " f"After: {cache_read_hits_after_md5}.") else: TestRun.LOGGER.info("Cache read hits increased as expected.") with TestRun.step("Unmount CAS device and stop cache."): core.unmount() cache.stop()