def load_io_classes_in_permutation_order(rules, permutation, cache): ioclass_config.remove_ioclass_config(ioclass_config_path=ioclass_config_path) ioclass_config.create_ioclass_config( add_default_rule=False, ioclass_config_path=ioclass_config_path ) # To make test more precise all workload except of tested ioclass should be # put in pass-through mode ioclass_list = [IoClass.default(allocation=False)] for n in range(len(rules)): ioclass_list.append(IoClass(class_id=permutation[n], rule=rules[n])) IoClass.save_list_to_config_file(ioclass_list, add_default_rule=False, ioclass_config_path=ioclass_config_path) casadm.load_io_classes(cache.cache_id, file=ioclass_config_path)
def test_ioclass_eviction_priority(cache_line_size): """ title: Check whether eviction priorites are respected. description: | Create ioclass for 4 different directories, each with different eviction priority configured. Saturate 3 of them and check if the partitions are evicted in a good order during IO to the fourth pass_criteria: - Partitions are evicted in specified order """ with TestRun.step("Prepare CAS device"): cache, core = prepare(cache_mode=CacheMode.WT, cache_line_size=cache_line_size) cache_size = cache.get_statistics().config_stats.cache_size with TestRun.step("Disable udev"): Udev.disable() with TestRun.step( f"Preparing filesystem and mounting {core.path} at {mountpoint}"): filesystem = Filesystem.xfs core.create_filesystem(filesystem) core.mount(mountpoint) sync() with TestRun.step("Prepare test dirs"): IoclassConfig = namedtuple("IoclassConfig", "id eviction_prio max_occupancy dir_path") io_classes = [ IoclassConfig(1, 3, 0.30, f"{mountpoint}/A"), IoclassConfig(2, 4, 0.30, f"{mountpoint}/B"), IoclassConfig(3, 5, 0.40, f"{mountpoint}/C"), IoclassConfig(4, 1, 1.00, f"{mountpoint}/D"), ] for io_class in io_classes: fs_utils.create_directory(io_class.dir_path, parents=True) with TestRun.step("Remove old ioclass config"): ioclass_config.remove_ioclass_config() ioclass_config.create_ioclass_config(False) with TestRun.step("Adding default ioclasses"): ioclass_config.add_ioclass(*str(IoClass.default( allocation="0.00")).split(",")) with TestRun.step("Adding ioclasses for all dirs"): for io_class in io_classes: ioclass_config.add_ioclass( io_class.id, f"directory:{io_class.dir_path}&done", io_class.eviction_prio, f"{io_class.max_occupancy:0.2f}", ) casadm.load_io_classes(cache_id=cache.cache_id, file=ioclass_config_path) with TestRun.step("Resetting cache stats"): cache.purge_cache() cache.reset_counters() with TestRun.step("Checking initial occupancy"): for io_class in io_classes: occupancy = get_io_class_occupancy(cache, io_class.id) if occupancy.get_value() != 0: TestRun.LOGGER.error( f"Incorrect inital occupancy for ioclass id: {io_class.id}." f" Expected 0, got: {occupancy}") with TestRun.step( f"To A, B and C directories perform IO with size of max io_class occupancy" ): for io_class in io_classes[0:3]: run_io_dir( f"{io_class.dir_path}/tmp_file", int((io_class.max_occupancy * cache_size) / Unit.Blocks4096), ) with TestRun.step("Check if each ioclass reached it's occupancy limit"): for io_class in io_classes[0:3]: actuall_occupancy = get_io_class_occupancy(cache, io_class.id) occupancy_limit = ((io_class.max_occupancy * cache_size).align_down( Unit.Blocks4096.get_value()).set_unit( Unit.Blocks4096)) if not isclose(actuall_occupancy.value, occupancy_limit.value, rel_tol=0.1): TestRun.LOGGER.error( f"Occupancy for ioclass {io_class.id} does not match. " f"Limit: {occupancy_limit}, actuall: {actuall_occupancy}") if get_io_class_occupancy(cache, io_classes[3].id).value != 0: TestRun.LOGGER.error( f"Occupancy for ioclass {io_classes[3].id} should be 0. " f"Actuall: {actuall_occupancy}") with TestRun.step("Perform IO to the fourth directory and check " "if other partitions are evicted in a good order"): target_io_class = io_classes[3] io_classes_to_evict = io_classes[: 3][:: -1] # List is ordered by eviction priority io_classes_evicted = [] io_offset = 0 for io_class in io_classes_to_evict: io_size = int( (io_class.max_occupancy * cache_size) / Unit.Blocks4096) run_io_dir(f"{target_io_class.dir_path}/tmp_file_{io_class.id}", io_size, io_offset) io_offset += io_size part_to_evict_end_occupancy = get_io_class_occupancy(cache, io_class.id, percent=True) # Since number of evicted cachelines is always >= 128, occupancy is checked # with approximation if not isclose(part_to_evict_end_occupancy, 0, abs_tol=4): TestRun.LOGGER.error( f"Wrong percent of cache lines evicted from part {io_class.id}. " f"Meant to be evicted {io_class.max_occupancy*100}%, actaully evicted " f"{io_class.max_occupancy*100-part_to_evict_end_occupancy}%" ) io_classes_evicted.append(io_class) for i in io_classes_to_evict: if i in io_classes_evicted: continue occupancy = get_io_class_occupancy(cache, i.id, percent=True) if not isclose(occupancy, i.max_occupancy * 100, abs_tol=4): TestRun.LOGGER.error(f"Ioclass {i.id} evicted incorrectly")
def test_ioclass_resize(cache_line_size, new_occupancy): """ title: Resize ioclass description: | Add ioclass, fill it with data, change it's size and check if new limit is respected pass_criteria: - Occupancy threshold is respected """ with TestRun.step("Prepare CAS device"): cache, core = prepare(cache_mode=CacheMode.WT, cache_line_size=cache_line_size) cache_size = cache.get_statistics().config_stats.cache_size with TestRun.step("Disable udev"): Udev.disable() with TestRun.step( f"Prepare filesystem and mount {core.path} at {mountpoint}"): filesystem = Filesystem.xfs core.create_filesystem(filesystem) core.mount(mountpoint) sync() with TestRun.step("Prepare test dirs"): IoclassConfig = recordclass("IoclassConfig", "id eviction_prio max_occupancy dir_path") io_class = IoclassConfig(2, 3, 0.10, f"{mountpoint}/A") fs_utils.create_directory(io_class.dir_path, parents=True) with TestRun.step("Remove old ioclass config"): ioclass_config.remove_ioclass_config() ioclass_config.create_ioclass_config(False) with TestRun.step("Add default ioclasses"): ioclass_config.add_ioclass(ioclass_id=1, rule="metadata&done", eviction_priority=1, allocation="1.00", ioclass_config_path=ioclass_config_path) ioclass_config.add_ioclass(*str(IoClass.default( allocation="0.00")).split(",")) with TestRun.step("Add directory for ioclass"): ioclass_config.add_ioclass( io_class.id, f"directory:{io_class.dir_path}&done", io_class.eviction_prio, f"{io_class.max_occupancy:0.2f}", ) casadm.load_io_classes(cache_id=cache.cache_id, file=ioclass_config_path) with TestRun.step("Reset cache stats"): cache.purge_cache() cache.reset_counters() with TestRun.step("Check initial occupancy"): occupancy = get_io_class_occupancy(cache, io_class.id) if occupancy.get_value() != 0: TestRun.LOGGER.error( f"Incorrect inital occupancy for ioclass id: {io_class.id}." f" Expected 0, got: {occupancy}") with TestRun.step(f"Perform IO with size equal to cache size"): run_io_dir(f"{io_class.dir_path}/tmp_file", int((cache_size) / Unit.Blocks4096)) with TestRun.step( "Check if the ioclass did not exceed specified occupancy"): actuall_occupancy = get_io_class_occupancy(cache, io_class.id) occupancy_limit = ((io_class.max_occupancy * cache_size).align_up( Unit.Blocks4096.get_value()).set_unit(Unit.Blocks4096)) # Divergency may be casued be rounding max occupancy if actuall_occupancy > occupancy_limit + Size(100, Unit.Blocks4096): TestRun.LOGGER.error( f"Occupancy for ioclass id exceeded: {io_class.id}. " f"Limit: {occupancy_limit}, actuall: {actuall_occupancy}") with TestRun.step( f"Resize ioclass from {io_class.max_occupancy*100}% to {new_occupancy}%" " cache occupancy"): io_class.max_occupancy = new_occupancy / 100 ioclass_config.remove_ioclass_config() ioclass_config.create_ioclass_config(False) ioclass_config.add_ioclass(*str(IoClass.default( allocation="0.00")).split(",")) ioclass_config.add_ioclass(ioclass_id=1, rule="metadata&done", eviction_priority=1, allocation="1.00", ioclass_config_path=ioclass_config_path) ioclass_config.add_ioclass( io_class.id, f"directory:{io_class.dir_path}&done", io_class.eviction_prio, f"{io_class.max_occupancy:0.2f}", ) casadm.load_io_classes(cache_id=cache.cache_id, file=ioclass_config_path) with TestRun.step(f"Perform IO with size equal to cache size"): run_io_dir(f"{io_class.dir_path}/tmp_file", int((cache_size) / Unit.Blocks4096)) with TestRun.step( "Check if the ioclass did not exceed specified occupancy"): actuall_occupancy = get_io_class_occupancy(cache, io_class.id) occupancy_limit = ((io_class.max_occupancy * cache_size).align_up( Unit.Blocks4096.get_value()).set_unit(Unit.Blocks4096)) # Divergency may be casued be rounding max occupancy if actuall_occupancy > occupancy_limit + Size(100, Unit.Blocks4096): TestRun.LOGGER.error( f"Occupancy for ioclass id exceeded: {io_class.id}. " f"Limit: {occupancy_limit}, actuall: {actuall_occupancy}")
def test_ioclass_export_configuration(cache_mode): """ title: Export IO class configuration to a file description: | Test CAS ability to create a properly formatted file with current IO class configuration pass_criteria: - CAS default IO class configuration contains unclassified class only - CAS properly imports previously exported configuration """ with TestRun.LOGGER.step(f"Test prepare"): cache, core = prepare(cache_mode) saved_config_path = "/tmp/opencas_saved.conf" default_list = [IoClass.default()] with TestRun.LOGGER.step( f"Check IO class configuration (should contain only default class)" ): csv = casadm.list_io_classes(cache.cache_id, OutputFormat.csv).stdout if not IoClass.compare_ioclass_lists(IoClass.csv_to_list(csv), default_list): TestRun.LOGGER.error( "Default configuration does not match expected\n" f"Current:\n{csv}\n" f"Expected:{IoClass.list_to_csv(default_list)}") with TestRun.LOGGER.step( "Create and load configuration file for 33 IO classes " "with random names, allocation and priority values"): random_list = IoClass.generate_random_ioclass_list(33) IoClass.save_list_to_config_file( random_list, ioclass_config_path=ioclass_config_path) casadm.load_io_classes(cache.cache_id, ioclass_config_path) with TestRun.LOGGER.step( "Display and export IO class configuration - displayed configuration " "should be the same as created"): TestRun.executor.run( f"{casadm.list_io_classes_cmd(str(cache.cache_id), OutputFormat.csv.name)}" f" > {saved_config_path}") csv = fs_utils.read_file(saved_config_path) if not IoClass.compare_ioclass_lists(IoClass.csv_to_list(csv), random_list): TestRun.LOGGER.error( "Exported configuration does not match expected\n" f"Current:\n{csv}\n" f"Expected:{IoClass.list_to_csv(random_list)}") with TestRun.LOGGER.step("Stop Intel CAS"): casadm.stop_cache(cache.cache_id) with TestRun.LOGGER.step("Start cache and add core"): cache = casadm.start_cache(cache.cache_device, force=True) casadm.add_core(cache, core.core_device) with TestRun.LOGGER.step( "Check IO class configuration (should contain only default class)" ): csv = casadm.list_io_classes(cache.cache_id, OutputFormat.csv).stdout if not IoClass.compare_ioclass_lists(IoClass.csv_to_list(csv), default_list): TestRun.LOGGER.error( "Default configuration does not match expected\n" f"Current:\n{csv}\n" f"Expected:{IoClass.list_to_csv(default_list)}") with TestRun.LOGGER.step( "Load exported configuration file for 33 IO classes"): casadm.load_io_classes(cache.cache_id, saved_config_path) with TestRun.LOGGER.step( "Display IO class configuration - should be the same as created"): csv = casadm.list_io_classes(cache.cache_id, OutputFormat.csv).stdout if not IoClass.compare_ioclass_lists(IoClass.csv_to_list(csv), random_list): TestRun.LOGGER.error( "Exported configuration does not match expected\n" f"Current:\n{csv}\n" f"Expected:{IoClass.list_to_csv(random_list)}") with TestRun.LOGGER.step(f"Test cleanup"): fs_utils.remove(saved_config_path)
def test_ioclass_occuppancy_load(cache_line_size): """ title: Load cache with occupancy limit specified description: | Load cache and verify if occupancy limits are loaded correctly and if each part has assigned apropriate number of dirty blocks. pass_criteria: - Occupancy thresholds have correct values for each ioclass after load """ with TestRun.step("Prepare CAS device"): cache, core = prepare(cache_mode=CacheMode.WB, cache_line_size=cache_line_size) cache_size = cache.get_statistics().config_stats.cache_size with TestRun.step("Disable udev"): Udev.disable() with TestRun.step( f"Prepare filesystem and mount {core.path} at {mountpoint}"): filesystem = Filesystem.xfs core.create_filesystem(filesystem) core.mount(mountpoint) sync() with TestRun.step("Prepare test dirs"): IoclassConfig = namedtuple("IoclassConfig", "id eviction_prio max_occupancy dir_path") io_classes = [ IoclassConfig(1, 3, 0.30, f"{mountpoint}/A"), IoclassConfig(2, 3, 0.30, f"{mountpoint}/B"), IoclassConfig(3, 3, 0.30, f"{mountpoint}/C"), ] for io_class in io_classes: fs_utils.create_directory(io_class.dir_path, parents=True) with TestRun.step("Remove old ioclass config"): ioclass_config.remove_ioclass_config() ioclass_config.create_ioclass_config(False) with TestRun.step("Add default ioclasses"): ioclass_config.add_ioclass(*str(IoClass.default( allocation="0.00")).split(",")) with TestRun.step("Add ioclasses for all dirs"): for io_class in io_classes: ioclass_config.add_ioclass( io_class.id, f"directory:{io_class.dir_path}&done", io_class.eviction_prio, f"{io_class.max_occupancy:0.2f}", ) casadm.load_io_classes(cache_id=cache.cache_id, file=ioclass_config_path) with TestRun.step("Reset cache stats"): cache.purge_cache() cache.reset_counters() with TestRun.step("Check initial occupancy"): for io_class in io_classes: occupancy = get_io_class_occupancy(cache, io_class.id) if occupancy.get_value() != 0: TestRun.LOGGER.error( f"Incorrect inital occupancy for ioclass id: {io_class.id}." f" Expected 0, got: {occupancy}") with TestRun.step(f"Perform IO with size equal to cache size"): for io_class in io_classes: run_io_dir(f"{io_class.dir_path}/tmp_file", int((cache_size) / Unit.Blocks4096)) with TestRun.step( "Check if the ioclass did not exceed specified occupancy"): for io_class in io_classes: actuall_dirty = get_io_class_dirty(cache, io_class.id) dirty_limit = ((io_class.max_occupancy * cache_size).align_down( Unit.Blocks4096.get_value()).set_unit(Unit.Blocks4096)) if not isclose(actuall_dirty.get_value(), dirty_limit.get_value(), rel_tol=0.1): TestRun.LOGGER.error( f"Dirty for ioclass id: {io_class.id} doesn't match expected." f"Expected: {dirty_limit}, actuall: {actuall_dirty}") with TestRun.step("Stop cache without flushing the data"): original_usage_stats = {} for io_class in io_classes: original_usage_stats[io_class.id] = get_io_class_usage( cache, io_class.id) original_ioclass_list = cache.list_io_classes() cache_disk_path = cache.cache_device.path core.unmount() cache.stop(no_data_flush=True) with TestRun.step("Load cache"): cache = casadm.start_cache(Device(cache_disk_path), load=True) with TestRun.step( "Check if the ioclass did not exceed specified occupancy"): for io_class in io_classes: actuall_dirty = get_io_class_dirty(cache, io_class.id) dirty_limit = ((io_class.max_occupancy * cache_size).align_down( Unit.Blocks4096.get_value()).set_unit(Unit.Blocks4096)) if not isclose(actuall_dirty.get_value(), dirty_limit.get_value(), rel_tol=0.1): TestRun.LOGGER.error( f"Dirty for ioclass id: {io_class.id} doesn't match expected." f"Expected: {dirty_limit}, actuall: {actuall_dirty}") with TestRun.step("Compare ioclass configs"): ioclass_list_after_load = cache.list_io_classes() if len(ioclass_list_after_load) != len(original_ioclass_list): TestRun.LOGGER.error( f"Ioclass occupancy limit doesn't match. Original list size: " f"{len(original_ioclass_list)}, loaded list size: " f"{len(ioclass_list_after_load)}") original_sorted = sorted(original_ioclass_list, key=lambda k: k["id"]) loaded_sorted = sorted(ioclass_list_after_load, key=lambda k: k["id"]) for original, loaded in zip(original_sorted, loaded_sorted): original_allocation = original["allocation"] loaded_allocation = loaded["allocation"] ioclass_id = original["id"] if original_allocation != loaded_allocation: TestRun.LOGGER.error( f"Occupancy limit doesn't match for ioclass {ioclass_id}: " f"Original: {original_allocation}, loaded: {loaded_allocation}" ) with TestRun.step("Compare usage stats before and after the load"): for io_class in io_classes: actuall_usage_stats = get_io_class_usage(cache, io_class.id) if original_usage_stats[io_class.id] != actuall_usage_stats: TestRun.LOGGER.error( f"Usage stats doesn't match for ioclass {io_class.id}. " f"Original: {original_usage_stats[io_class.id]}, " f"loaded: {actuall_usage_stats}")
def test_ioclass_repart(cache_mode, cache_line_size, ioclass_size_multiplicatior): """ title: Check whether occupancy limit is respected during repart description: | Create ioclass for 3 different directories, each with different max occupancy threshold. Create 3 files classified on default ioclass. Move files to directories created earlier and force repart by reading theirs contents. pass_criteria: - Partitions are evicted in specified order """ with TestRun.step("Prepare CAS device"): cache, core = prepare(cache_mode=cache_mode, cache_line_size=cache_line_size) cache_size = cache.get_statistics().config_stats.cache_size with TestRun.step("Disable udev"): Udev.disable() with TestRun.step( f"Prepare filesystem and mount {core.path} at {mountpoint}"): filesystem = Filesystem.xfs core.create_filesystem(filesystem) core.mount(mountpoint) sync() with TestRun.step("Prepare test dirs"): IoclassConfig = namedtuple("IoclassConfig", "id eviction_prio max_occupancy dir_path") io_classes = [ IoclassConfig(1, 3, 0.40, f"{mountpoint}/A"), IoclassConfig(2, 4, 0.30, f"{mountpoint}/B"), IoclassConfig(3, 5, 0.30, f"{mountpoint}/C"), ] for io_class in io_classes: fs_utils.create_directory(io_class.dir_path, parents=True) with TestRun.step("Remove old ioclass config"): ioclass_config.remove_ioclass_config() ioclass_config.create_ioclass_config(False) with TestRun.step("Add default ioclasses"): ioclass_config.add_ioclass(*str(IoClass.default( allocation="1.00")).split(",")) ioclass_config.add_ioclass(ioclass_id=5, rule="metadata", eviction_priority=1, allocation="1.00", ioclass_config_path=ioclass_config_path) with TestRun.step("Add ioclasses for all dirs"): for io_class in io_classes: ioclass_config.add_ioclass( io_class.id, f"directory:{io_class.dir_path}&done", io_class.eviction_prio, f"{io_class.max_occupancy*ioclass_size_multiplicatior:0.2f}", ) casadm.load_io_classes(cache_id=cache.cache_id, file=ioclass_config_path) with TestRun.step("Reset cache stats"): cache.purge_cache() cache.reset_counters() with TestRun.step(f"Create 3 files classified in default ioclass"): for i, io_class in enumerate(io_classes[0:3]): run_io_dir( f"{mountpoint}/{i}", int((io_class.max_occupancy * cache_size) / Unit.Blocks4096)) if not isclose( get_io_class_occupancy( cache, ioclass_config.DEFAULT_IO_CLASS_ID).value, cache_size.value, rel_tol=0.1, ): TestRun.fail(f"Failed to populte default ioclass") with TestRun.step("Check initial occupancy"): for io_class in io_classes: occupancy = get_io_class_occupancy(cache, io_class.id) if occupancy.get_value() != 0: TestRun.LOGGER.error( f"Incorrect inital occupancy for ioclass id: {io_class.id}." f" Expected 0, got: {occupancy}") with TestRun.step( "Force repart - move files to created directories and read theirs contents" ): for i, io_class in enumerate(io_classes): fs_utils.move(source=f"{mountpoint}/{i}", destination=io_class.dir_path) run_io_dir_read(f"{io_class.dir_path}/{i}") with TestRun.step("Check if each ioclass reached it's occupancy limit"): for io_class in io_classes[0:3]: actuall_occupancy = get_io_class_occupancy(cache, io_class.id) occupancy_limit = ((io_class.max_occupancy * cache_size).align_down( Unit.Blocks4096.get_value()).set_unit( Unit.Blocks4096)) if not isclose(actuall_occupancy.value, occupancy_limit.value, rel_tol=0.1): TestRun.LOGGER.error( f"Occupancy for ioclass {io_class.id} does not match. " f"Limit: {occupancy_limit}, actuall: {actuall_occupancy}")
def test_ioclass_occupancy_sum_cache(): """ title: Test for ioclasses occupancy sum description: | Create ioclass for 3 different directories, each with different max cache occupancy configured. Trigger IO to each ioclass and check if sum of their Usage stats is equal to cache Usage stats. pass_criteria: - Max occupancy is set correctly for each ioclass - Sum of ioclassess stats is equal to cache stats """ with TestRun.step("Prepare CAS device"): cache, core = prepare() cache_size = cache.get_statistics().config_stats.cache_size with TestRun.step("Disable udev"): Udev.disable() with TestRun.step( f"Prepare filesystem and mount {core.path} at {mountpoint}"): filesystem = Filesystem.xfs core.create_filesystem(filesystem) core.mount(mountpoint) sync() with TestRun.step("Prepare test dirs"): default_ioclass_id = 0 IoclassConfig = namedtuple("IoclassConfig", "id eviction_prio max_occupancy dir_path") io_classes = [ IoclassConfig(1, 3, 0.10, f"{mountpoint}/A"), IoclassConfig(2, 4, 0.20, f"{mountpoint}/B"), IoclassConfig(3, 5, 0.30, f"{mountpoint}/C"), ] for io_class in io_classes: fs_utils.create_directory(io_class.dir_path, parents=True) with TestRun.step("Remove old ioclass config"): ioclass_config.remove_ioclass_config() ioclass_config.create_ioclass_config(False) with TestRun.step("Add default ioclasses"): ioclass_config.add_ioclass(*str(IoClass.default( allocation="0.00")).split(",")) with TestRun.step("Add ioclasses for all dirs"): for io_class in io_classes: ioclass_config.add_ioclass( io_class.id, f"directory:{io_class.dir_path}&done", io_class.eviction_prio, f"{io_class.max_occupancy:0.2f}", ) casadm.load_io_classes(cache_id=cache.cache_id, file=ioclass_config_path) with TestRun.step("Purge cache"): cache.purge_cache() with TestRun.step("Verify stats before IO"): usage_stats_sum = IoClassUsageStats(Size(0), Size(0), Size(0)) for i in io_classes: usage_stats_sum += get_io_class_usage(cache, i.id) usage_stats_sum += get_io_class_usage(cache, default_ioclass_id) cache_stats = cache.get_statistics().usage_stats cache_stats.free = Size(0) if (cache_stats.occupancy != usage_stats_sum.occupancy or cache_stats.clean != usage_stats_sum.clean or cache_stats.dirty != usage_stats_sum.dirty): TestRun.LOGGER.error( "Initial cache usage stats doesn't match sum of ioclasses stats\n" f"cache stats: {cache_stats}, sumed up stats {usage_stats_sum}\n" f"particular stats {[get_io_class_usage(cache, i.id) for i in io_classes]}" ) with TestRun.step(f"Trigger IO to each directory"): for io_class in io_classes: run_io_dir( f"{io_class.dir_path}/tmp_file", int((io_class.max_occupancy * cache_size) / Unit.Blocks4096), ) with TestRun.step("Verify stats after IO"): usage_stats_sum = IoClassUsageStats(Size(0), Size(0), Size(0)) for i in io_classes: usage_stats_sum += get_io_class_usage(cache, i.id) usage_stats_sum += get_io_class_usage(cache, default_ioclass_id) cache_stats = cache.get_statistics().usage_stats cache_stats.free = Size(0) if (cache_stats.occupancy != usage_stats_sum.occupancy or cache_stats.clean != usage_stats_sum.clean or cache_stats.dirty != usage_stats_sum.dirty): TestRun.LOGGER.error( "Cache usage stats doesn't match sum of ioclasses stats\n" f"cache stats: {cache_stats}, sumed up stats {usage_stats_sum}\n" f"particular stats {[get_io_class_usage(cache, i.id) for i in io_classes]}" )
def test_ioclass_occupancy_directory_write(io_size_multiplication, cache_mode, cache_line_size): """ title: Test for max occupancy set for ioclass based on directory description: | Create ioclass for 3 different directories, each with different max cache occupancy configured. Run IO against each directory and see if occupancy limit is repected. pass_criteria: - Max occupancy is set correctly for each ioclass - Each ioclass does not exceed max occupancy """ with TestRun.step("Prepare CAS device"): cache, core = prepare(cache_mode=cache_mode, cache_line_size=cache_line_size) cache_size = cache.get_statistics().config_stats.cache_size with TestRun.step("Disable udev"): Udev.disable() with TestRun.step( f"Prepare filesystem and mount {core.path} at {mountpoint}"): filesystem = Filesystem.xfs core.create_filesystem(filesystem) core.mount(mountpoint) sync() with TestRun.step("Prepare test dirs"): IoclassConfig = namedtuple("IoclassConfig", "id eviction_prio max_occupancy dir_path") io_classes = [ IoclassConfig(1, 3, 0.10, f"{mountpoint}/A"), IoclassConfig(2, 4, 0.20, f"{mountpoint}/B"), IoclassConfig(3, 5, 0.30, f"{mountpoint}/C"), ] for io_class in io_classes: fs_utils.create_directory(io_class.dir_path, parents=True) with TestRun.step("Remove old ioclass config"): ioclass_config.remove_ioclass_config() ioclass_config.create_ioclass_config(False) with TestRun.step("Add default ioclasses"): ioclass_config.add_ioclass(*str(IoClass.default( allocation="0.00")).split(",")) with TestRun.step("Add ioclasses for all dirs"): for io_class in io_classes: ioclass_config.add_ioclass( io_class.id, f"directory:{io_class.dir_path}&done", io_class.eviction_prio, f"{io_class.max_occupancy:0.2f}", ) casadm.load_io_classes(cache_id=cache.cache_id, file=ioclass_config_path) with TestRun.step("Reset cache stats"): cache.purge_cache() cache.reset_counters() with TestRun.step("Check initial occupancy"): for io_class in io_classes: occupancy = get_io_class_occupancy(cache, io_class.id) if occupancy.get_value() != 0: TestRun.LOGGER.error( f"Incorrect inital occupancy for ioclass id: {io_class.id}." f" Expected 0, got: {occupancy}") with TestRun.step( f"To each directory perform IO with size of {io_size_multiplication} max io_class occupancy" ): for io_class in io_classes: original_occupancies = {} tmp_io_class_list = [i for i in io_classes if i != io_class] for i in tmp_io_class_list: original_occupancies[i.id] = get_io_class_occupancy( cache, i.id) io_count = get_io_count(io_class, cache_size, cache_line_size, io_size_multiplication) run_io_dir(f"{io_class.dir_path}/tmp_file", io_count) actual_occupancy = get_io_class_occupancy(cache, io_class.id) expected_occupancy = io_class.max_occupancy * cache_size if io_size_multiplication < 1: expected_occupancy *= io_size_multiplication expected_occupancy = expected_occupancy.align_down( cache_line_size.value.value) expected_occupancy.set_unit(Unit.Blocks4096) if not isclose(expected_occupancy.value, actual_occupancy.value, rel_tol=0.1): TestRun.LOGGER.error( f"Occupancy for ioclass {io_class.id} should be equal {expected_occupancy} " f"but is {actual_occupancy} instead!") for i in tmp_io_class_list: actual_occupancy = get_io_class_occupancy(cache, i.id) io_count = get_io_count(i, cache_size, cache_line_size, io_size_multiplication) if (original_occupancies[i.id] != actual_occupancy and io_count * Unit.Blocks4096.value < actual_occupancy.value): TestRun.LOGGER.error( f"Occupancy for ioclass {i.id} should not change " f"during IO to ioclass {io_class.id}. Original value: " f"{original_occupancies[i.id]}, actual: {actual_occupancy}" ) with TestRun.step( "Check if none of ioclasses did not exceed specified occupancy"): for io_class in io_classes: actual_occupancy = get_io_class_occupancy(cache, io_class.id) occupancy_limit = ((io_class.max_occupancy * cache_size).align_up( Unit.Blocks4096.get_value()).set_unit(Unit.Blocks4096)) # Divergency may be caused by rounding max occupancy if actual_occupancy > occupancy_limit * 1.01: TestRun.LOGGER.error( f"Occupancy for ioclass id exceeded: {io_class.id}. " f"Limit: {occupancy_limit}, actual: {actual_occupancy}")
def test_ioclass_occupancy_directory_read(io_size_multiplication, cache_line_size, cache_mode): """ title: Test for max occupancy set for ioclass based on directory - read description: | Set cache mode to pass-through and create files on mounted core device. Swtich cache to write through, and load ioclasses applaying to different files. Read files and check if occupancy threshold is respected. pass_criteria: - Max occupancy is set correctly for each ioclass - Each ioclass does not exceed max occupancy """ with TestRun.step("Prepare CAS device"): cache, core = prepare(cache_mode=cache_mode, cache_line_size=cache_line_size) cache_size = cache.get_statistics().config_stats.cache_size with TestRun.step("Disable udev"): Udev.disable() with TestRun.step(f"Prepare filesystem and mount {core.system_path} at {mountpoint}"): filesystem = Filesystem.xfs core.create_filesystem(filesystem) core.mount(mountpoint) sync() with TestRun.step("Prepare test dirs"): IoclassConfig = namedtuple("IoclassConfig", "id eviction_prio max_occupancy dir_path") io_classes = [ IoclassConfig(1, 3, 0.10, f"{mountpoint}/A"), IoclassConfig(2, 4, 0.20, f"{mountpoint}/B"), IoclassConfig(3, 5, 0.30, f"{mountpoint}/C"), ] for io_class in io_classes: fs_utils.create_directory(io_class.dir_path, parents=True) with TestRun.step( f"In each directory create file with size of {io_size_multiplication} " f"max io_class occupancy for future read" ): for io_class in io_classes: run_io_dir( f"{io_class.dir_path}/tmp_file", int( (io_class.max_occupancy * cache_size) / Unit.Blocks4096 * io_size_multiplication ), ) with TestRun.step("Remove old ioclass config"): ioclass_config.remove_ioclass_config() ioclass_config.create_ioclass_config(False) with TestRun.step("Add default ioclasses"): ioclass_config.add_ioclass(*str(IoClass.default(allocation="0.00")).split(",")) with TestRun.step("Add ioclasses for all dirs"): for io_class in io_classes: ioclass_config.add_ioclass( io_class.id, f"directory:{io_class.dir_path}&done", io_class.eviction_prio, f"{io_class.max_occupancy:0.2f}", ) casadm.load_io_classes(cache_id=cache.cache_id, file=ioclass_config_path) with TestRun.step("Reset cache stats"): cache.purge_cache() cache.reset_counters() with TestRun.step("Check initial occupancy"): for io_class in io_classes: occupancy = get_io_class_occupancy(cache, io_class.id) if occupancy.get_value() != 0: TestRun.LOGGER.error( f"Incorrect inital occupancy for ioclass id: {io_class.id}." f" Expected 0, got: {occupancy}" ) with TestRun.step(f"Read each file and check if data was inserted to appropriate ioclass"): for io_class in io_classes: original_occupacies = {} tmp_io_class_list = [i for i in io_classes if i != io_class] for i in tmp_io_class_list: original_occupacies[i.id] = get_io_class_occupancy(cache, i.id) run_io_dir_read(f"{io_class.dir_path}/tmp_file") actuall_occupancy = get_io_class_occupancy(cache, io_class.id) io_size = io_class.max_occupancy * cache_size if io_size_multiplication < 1: io_size *= io_size_multiplication io_size.set_unit(Unit.Blocks4096) if not isclose(io_size.value, actuall_occupancy.value, rel_tol=0.1): TestRun.LOGGER.error( f"Occupancy for ioclass {i.id} should be equal {io_size} " f"but is {actuall_occupancy} instead!" ) for i in tmp_io_class_list: actuall_occupancy = get_io_class_occupancy(cache, i.id) if original_occupacies[i.id] != actuall_occupancy: TestRun.LOGGER.error( f"Occupancy for ioclass {i.id} should not change " f"during IO to ioclass {io_class.id}. Original value: " f"{original_occupacies[i.id]}, actuall: {actuall_occupancy}" ) with TestRun.step("Check if none of ioclasses did not exceed specified occupancy"): for io_class in io_classes: actuall_occupancy = get_io_class_occupancy(cache, io_class.id) occupancy_limit = ( (io_class.max_occupancy * cache_size) .align_up(Unit.Blocks4096.get_value()) .set_unit(Unit.Blocks4096) ) # Divergency may be casued be rounding max occupancy if actuall_occupancy > occupancy_limit + Size(100, Unit.Blocks4096): TestRun.LOGGER.error( f"Occupancy for ioclass id exceeded: {io_class.id}. " f"Limit: {occupancy_limit}, actuall: {actuall_occupancy}" )