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)
Beispiel #2
0
def get_io_class_list(cache_id: int):
    ret = []
    casadm_output = casadm.list_io_classes(
        cache_id, OutputFormat.csv).stdout.splitlines()
    casadm_output.pop(0)  # Remove header
    for line in casadm_output:
        values = line.split(",")
        ioclass = IoClass(int(values[0]), values[1], int(values[2]), values[3])
        ret.append(ioclass)
    return ret
Beispiel #3
0
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")
Beispiel #4
0
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_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_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}")
Beispiel #7
0
def test_standby_neg_cli_management():
    """
    title: Blocking management commands in standby state
    description: |
      Try executing management commands for a cache in standby state
    pass_criteria:
      - The execution is unsuccessful for blocked management commands
      - The execution is successful for allowed management commands
      - A proper error message is displayed for unsuccessful executions
    """
    with TestRun.step("Prepare the device for the cache."):
        device = TestRun.disks["cache"]
        device.create_partitions(
            [Size(500, Unit.MebiByte),
             Size(500, Unit.MebiByte)])
        cache_device = device.partitions[0]
        core_device = device.partitions[1]

    with TestRun.step("Prepare the standby instance"):
        cache_id = 1
        cache = casadm.standby_init(cache_dev=cache_device,
                                    cache_id=cache_id,
                                    cache_line_size=32,
                                    force=True)

        ioclass_config_path = "/tmp/standby_cli_neg_mngt_test_ioclass_config_file.csv"
        TestRun.executor.run(f"rm -rf {ioclass_config_path}")
        random_ioclass_config = IoClass.generate_random_ioclass_list(5)
        IoClass.save_list_to_config_file(
            random_ioclass_config, ioclass_config_path=ioclass_config_path)

        blocked_mngt_commands = [
            cli.get_param_cutoff_cmd(str(cache_id), "1"),
            cli.get_param_cleaning_cmd(str(cache_id)),
            cli.get_param_cleaning_alru_cmd(str(cache_id)),
            cli.get_param_cleaning_acp_cmd(str(cache_id)),
            cli.set_param_cutoff_cmd(str(cache_id), "1", threshold="1"),
            cli.set_param_cutoff_cmd(str(cache_id), policy="never"),
            cli.set_param_cleaning_cmd(str(cache_id), policy="nop"),
            cli.set_param_cleaning_alru_cmd(str(cache_id), wake_up="30"),
            cli.set_param_cleaning_acp_cmd(str(cache_id), wake_up="100"),
            cli.set_param_promotion_cmd(str(cache_id), policy="nhit"),
            cli.set_param_promotion_nhit_cmd(str(cache_id), threshold="5"),
            cli.set_cache_mode_cmd("wb", str(cache_id)),
            cli.add_core_cmd(str(cache_id), core_device.path),
            cli.remove_core_cmd(str(cache_id), "1"),
            cli.remove_inactive_cmd(str(cache_id), "1"),
            cli.reset_counters_cmd(str(cache_id)),
            cli.flush_cache_cmd(str(cache_id)),
            cli.flush_core_cmd(str(cache_id), "1"),
            cli.load_io_classes_cmd(str(cache_id), ioclass_config_path),
            cli.list_io_classes_cmd(str(cache_id), output_format="csv"),
            cli.script_try_add_cmd(str(cache_id), core_device.path, core_id=1),
            cli.script_purge_cache_cmd(str(cache_id)),
            cli.script_purge_core_cmd(str(cache_id), "1"),
            cli.script_detach_core_cmd(str(cache_id), "1"),
            cli.script_remove_core_cmd(str(cache_id), "1"),
        ]

    with TestRun.step(
            "Try to execute forbidden management commands in standby mode"):
        for cmd in blocked_mngt_commands:

            TestRun.LOGGER.info(f"Verify {cmd}")
            output = TestRun.executor.run_expect_fail(cmd)
            if not check_stderr_msg(output, operation_forbiden_in_standby):
                TestRun.LOGGER.error(
                    f'Expected the following error message "{operation_forbiden_in_standby[0]}" '
                    f'Got "{output.stderr}" instead.')

    with TestRun.step("Stop the standby instance"):
        TestRun.executor.run(f"rm -rf {ioclass_config_path}")
        cache.stop()
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}")
Beispiel #9
0
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]}"
            )
Beispiel #10
0
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}"
                )