diff --git a/casadm/cas_main.c b/casadm/cas_main.c index 7bab2be..7f6423c 100644 --- a/casadm/cas_main.c +++ b/casadm/cas_main.c @@ -895,6 +895,8 @@ int set_param_namespace_handle_option(char *namespace, char *opt, const char **a int handle_set_param() { + int err = 0; + if (command_args_values.params_count == 0) { cas_printf(LOG_ERR, "Error: No parameters specified!\n"); return FAILURE; @@ -902,15 +904,20 @@ int handle_set_param() switch (command_args_values.params_type) { case PARAM_TYPE_CORE: - return core_params_set(command_args_values.cache_id, + err = core_params_set(command_args_values.cache_id, command_args_values.core_id, cas_core_params); case PARAM_TYPE_CACHE: - return cache_params_set(command_args_values.cache_id, + err = cache_params_set(command_args_values.cache_id, cas_cache_params); default: - return FAILURE; + err = FAILURE; } + + if (err) + cas_printf(LOG_ERR, "Setting runtime parameter failed!\n"); + + return err; } /***************************************************************************** @@ -985,6 +992,7 @@ int get_param_namespace_handle_option(char *namespace, char *opt, const char **a int handle_get_param() { int format = TEXT; + int err = 0; if (OUTPUT_FORMAT_CSV == command_args_values.output_format) { format = RAW_CSV; @@ -992,15 +1000,20 @@ int handle_get_param() switch (command_args_values.params_type) { case PARAM_TYPE_CORE: - return core_params_get(command_args_values.cache_id, + err = core_params_get(command_args_values.cache_id, command_args_values.core_id, cas_core_params, format); case PARAM_TYPE_CACHE: - return cache_params_get(command_args_values.cache_id, + err = cache_params_get(command_args_values.cache_id, cas_cache_params, format); default: - return FAILURE; + err = FAILURE; } + + if (err) + cas_printf(LOG_ERR, "Getting runtime parameter failed!\n"); + + return err; } static cli_option set_state_cache_mode_options[] = { diff --git a/test/functional/GUIDELINES.md b/test/functional/GUIDELINES.md index f8c8777..11b0342 100644 --- a/test/functional/GUIDELINES.md +++ b/test/functional/GUIDELINES.md @@ -1,6 +1,6 @@ # Preface -This document contains guidlines and BKMs for writing tests. Following these +This document contains guidelines and BKMs for writing tests. Following these will significantly reduce number of comments you will receive on the code review and will speed up the process of merging your patch. diff --git a/test/functional/api/cas/cache.py b/test/functional/api/cas/cache.py index acb89aa..f9b27e2 100644 --- a/test/functional/api/cas/cache.py +++ b/test/functional/api/cas/cache.py @@ -123,8 +123,18 @@ class Cache: def set_seq_cutoff_parameters(self, seq_cutoff_param: SeqCutOffParameters): return casadm.set_param_cutoff(self.cache_id, - seq_cutoff_param.threshold, - seq_cutoff_param.policy) + threshold=seq_cutoff_param.threshold, + policy=seq_cutoff_param.policy) + + def set_seq_cutoff_threshold(self, threshold: Size): + return casadm.set_param_cutoff(self.cache_id, + threshold=threshold, + policy=None) + + def set_seq_cutoff_policy(self, policy: SeqCutOffPolicy): + return casadm.set_param_cutoff(self.cache_id, + threshold=None, + policy=policy) def set_cleaning_policy(self, cleaning_policy: CleaningPolicy): return casadm.set_param_cleaning(self.cache_id, cleaning_policy) diff --git a/test/functional/api/cas/cache_config.py b/test/functional/api/cas/cache_config.py index 8d028f0..d97e5ce 100644 --- a/test/functional/api/cas/cache_config.py +++ b/test/functional/api/cas/cache_config.py @@ -32,6 +32,14 @@ class SeqCutOffPolicy(Enum): never = 2 DEFAULT = full + @classmethod + def from_name(cls, name): + for policy_name, policy in SeqCutOffPolicy.__members__.items(): + if name == policy_name: + return policy + + raise ValueError(f"{name} is not a valid sequential cut off name") + class EvictionPolicy(Enum): lru = 0 @@ -106,6 +114,7 @@ class SeqCutOffParameters: seq_cut_off_params = SeqCutOffParameters() seq_cut_off_params.policy = SeqCutOffPolicy.full seq_cut_off_params.threshold = Size(1024, Unit.KibiByte) + return seq_cut_off_params # TODO: Use case for this will be to iterate over configurations (kernel params such as diff --git a/test/functional/api/cas/casadm.py b/test/functional/api/cas/casadm.py index cd70ac8..720cc0a 100644 --- a/test/functional/api/cas/casadm.py +++ b/test/functional/api/cas/casadm.py @@ -254,15 +254,10 @@ def get_param_cleaning_acp(cache_id: int, output_format: OutputFormat = None, def set_param_cutoff(cache_id: int, core_id: int = None, threshold: Size = None, policy: SeqCutOffPolicy = None): - _threshold = None if threshold is None else threshold.get_value(Unit.KibiByte) - if core_id is None: - command = set_param_cutoff_cmd( - cache_id=str(cache_id), threshold=_threshold, - policy=policy.name) - else: - command = set_param_cutoff_cmd( - cache_id=str(cache_id), core_id=str(core_id), - threshold=_threshold, policy=policy.name) + _threshold = None if threshold is None else int(threshold.get_value(Unit.KibiByte)) + command = set_param_cutoff_cmd( + cache_id=cache_id, core_id=core_id, + threshold=_threshold, policy=policy) output = TestRun.executor.run(command) if output.exit_code != 0: raise Exception( diff --git a/test/functional/api/cas/casadm_parser.py b/test/functional/api/cas/casadm_parser.py index ca9a3b2..03f86d9 100644 --- a/test/functional/api/cas/casadm_parser.py +++ b/test/functional/api/cas/casadm_parser.py @@ -198,9 +198,10 @@ def get_seq_cut_off_parameters(cache_id: int, core_id: int): seq_cut_off_params = SeqCutOffParameters() for line in casadm_output: if 'threshold' in line: - seq_cut_off_params.threshold = line.split(',')[1] + seq_cut_off_params.threshold = Size(int(line.split(',')[1]), Unit.KibiByte) if 'policy' in line: - seq_cut_off_params.policy = SeqCutOffPolicy(line.split(',')[1]) + seq_cut_off_params.policy = SeqCutOffPolicy.from_name(line.split(',')[1]) + return seq_cut_off_params def get_casadm_version(): diff --git a/test/functional/api/cas/cli.py b/test/functional/api/cas/cli.py index 8abf909..a29838d 100644 --- a/test/functional/api/cas/cli.py +++ b/test/functional/api/cas/cli.py @@ -190,11 +190,11 @@ def set_param_cutoff_cmd(cache_id: str, core_id: str = None, threshold: str = No policy: str = None, shortcut: bool = False): add_params = "" if core_id is not None: - add_params += (" -j " if shortcut else " --core-id ") + core_id + add_params += (" -j " if shortcut else " --core-id ") + str(core_id) if threshold is not None: - add_params += (" -t " if shortcut else " --threshold ") + threshold + add_params += (" -t " if shortcut else " --threshold ") + str(threshold) if policy is not None: - add_params += (" -p " if shortcut else " --policy ") + policy + add_params += (" -p " if shortcut else " --policy ") + policy.name return _set_param_cmd(namespace="seq-cutoff", cache_id=cache_id, additional_params=add_params, shortcut=shortcut) diff --git a/test/functional/api/cas/core.py b/test/functional/api/cas/core.py index bb08324..891a289 100644 --- a/test/functional/api/cas/core.py +++ b/test/functional/api/cas/core.py @@ -17,6 +17,10 @@ class CoreStatus(Enum): detached = 3 +SEQ_CUTOFF_THRESHOLD_MAX = Size(4194181, Unit.KibiByte) +SEQ_CUT_OFF_THRESHOLD_DEFAULT = Size(1, Unit.MebiByte) + + class Core(Device): def __init__(self, core_device: str, cache_id: int): self.core_device = Device(core_device) @@ -54,6 +58,12 @@ class Core(Device): def get_seq_cut_off_parameters(self): return get_seq_cut_off_parameters(self.cache_id, self.core_id) + def get_seq_cut_off_policy(self): + return get_seq_cut_off_parameters(self.cache_id, self.core_id).policy + + def get_seq_cut_off_threshold(self): + return get_seq_cut_off_parameters(self.cache_id, self.core_id).threshold + def get_dirty_blocks(self): return self.get_core_statistics()["dirty"] @@ -77,5 +87,15 @@ class Core(Device): assert self.get_dirty_blocks().get_value(Unit.Blocks4096) == 0 def set_seq_cutoff_parameters(self, seq_cutoff_param: SeqCutOffParameters): - casadm.set_param_cutoff(self.cache_id, self.core_id, - seq_cutoff_param.threshold, seq_cutoff_param.policy) + return casadm.set_param_cutoff(self.cache_id, self.core_id, + seq_cutoff_param.threshold, seq_cutoff_param.policy) + + def set_seq_cutoff_threshold(self, threshold: Size): + return casadm.set_param_cutoff(self.cache_id, self.core_id, + threshold=threshold, + policy=None) + + def set_seq_cutoff_policy(self, policy: SeqCutOffPolicy): + return casadm.set_param_cutoff(self.cache_id, self.core_id, + threshold=None, + policy=policy) diff --git a/test/functional/tests/cli/test_seq_cutoff.py b/test/functional/tests/cli/test_seq_cutoff.py new file mode 100644 index 0000000..69ea98a --- /dev/null +++ b/test/functional/tests/cli/test_seq_cutoff.py @@ -0,0 +1,280 @@ +# +# Copyright(c) 2019 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause-Clear +# + + +import pytest +import random +from ctypes import c_uint32 +from api.cas import casadm +from api.cas.cache_config import SeqCutOffPolicy +from api.cas.core import SEQ_CUTOFF_THRESHOLD_MAX, SEQ_CUT_OFF_THRESHOLD_DEFAULT +from api.cas.casadm import set_param_cutoff_cmd +from core.test_run import TestRun + +from storage_devices.disk import DiskType, DiskTypeSet, DiskTypeLowerThan +from test_utils.size import Size, Unit + + +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +def test_seq_cutoff_default_params(): + """ + title: Default sequential cut-off threshold & policy test + description: Test if proper default threshold and policy is set after cache start + pass_criteria: + - "Full" shall be default sequential cut-off policy + - There shall be default 1MiB (1024kiB) value for sequential cut-off threshold + """ + with TestRun.step("Test prepare (start cache and add core)"): + cache, cores = prepare() + + with TestRun.step("Getting sequential cut-off parameters"): + params = cores[0].get_seq_cut_off_parameters() + + with TestRun.step("Check if proper sequential cut off policy is set as a default"): + if params.policy != SeqCutOffPolicy.DEFAULT: + TestRun.fail(f"Wrong sequential cut off policy set: {params.policy} " + f"should be {SeqCutOffPolicy.DEFAULT}") + + with TestRun.step("Check if proper sequential cut off threshold is set as a default"): + if params.threshold != SEQ_CUT_OFF_THRESHOLD_DEFAULT: + TestRun.fail(f"Wrong sequential cut off threshold set: {params.threshold} " + f"should be {SEQ_CUT_OFF_THRESHOLD_DEFAULT}") + + +@pytest.mark.parametrize("policy", SeqCutOffPolicy) +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +def test_seq_cutoff_set_get_policy_core(policy): + """ + title: Sequential cut-off policy set/get test for core + description: | + Test if CAS is setting proper sequential cut-off policy for core and + returns previously set value + pass_criteria: + - Sequential cut-off policy obtained from get-param command for the first core must be + the same as the one used in set-param command + - Sequential cut-off policy obtained from get-param command for the second core must be + proper default value + """ + with TestRun.step("Test prepare (start cache and add 2 cores)"): + cache, cores = prepare(cores_count=2) + + with TestRun.step(f"Setting core sequential cut off policy mode to {policy}"): + cores[0].set_seq_cutoff_policy(policy) + + with TestRun.step("Check if proper sequential cut off policy was set for the first core"): + if cores[0].get_seq_cut_off_policy() != policy: + TestRun.fail(f"Wrong sequential cut off policy set: " + f"{cores[0].get_seq_cut_off_policy()} " + f"should be {policy}") + + with TestRun.step("Check if proper default sequential cut off policy was set for the " + "second core"): + if cores[1].get_seq_cut_off_policy() != SeqCutOffPolicy.DEFAULT: + TestRun.fail(f"Wrong default sequential cut off policy: " + f"{cores[1].get_seq_cut_off_policy()} " + f"should be {SeqCutOffPolicy.DEFAULT}") + + +@pytest.mark.parametrize("policy", SeqCutOffPolicy) +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +def test_seq_cutoff_set_get_policy_cache(policy): + """ + title: Sequential cut-off policy set/get test for cache + description: | + Test if CAS is setting proper sequential cut-off policy for whole cache and + returns previously set value + pass_criteria: + - Sequential cut-off policy obtained from get-param command for each of 3 cores must be the + same as the one used in set-param command for cache + """ + with TestRun.step("Test prepare (start cache and add 3 cores)"): + cache, cores = prepare(cores_count=3) + + with TestRun.step(f"Setting sequential cut off policy mode {policy} for cache"): + cache.set_seq_cutoff_policy(policy) + + for i in TestRun.iteration(range(0, len(cores)), "Verifying if proper policy was set"): + with TestRun.step(f"Check if proper sequential cut off policy was set for core"): + if cores[i].get_seq_cut_off_policy() != policy: + TestRun.fail(f"Wrong core sequential cut off policy: " + f"{cores[i].get_seq_cut_off_policy()} " + f"should be {policy}") + + +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +def test_seq_cutoff_policy_load(): + """ + title: Sequential cut-off policy set/get test with cache load between + description: | + Set each possible policy for different core, stop cache, test if after cache load + sequential cut-off policy value previously set is being loaded correctly for each core. + pass_criteria: + - Sequential cut-off policy obtained from get-param command after cache load + must be the same as the one used in set-param command before cache stop + - Sequential cut-off policy loaded for the last core should be the default one +""" + with TestRun.step(f"Test prepare (start cache and add {len(SeqCutOffPolicy) + 1} cores)"): + # Create as many cores as many possible policies including default one + cache, cores = prepare(cores_count=len(SeqCutOffPolicy) + 1) + policies = [policy for policy in SeqCutOffPolicy] + + for i, core in TestRun.iteration(enumerate(cores[:-1]), "Set all possible policies " + "except the default one"): + with TestRun.step(f"Setting cache sequential cut off policy mode to " + f"{policies[i]}"): + cores[i].set_seq_cutoff_policy(policies[i]) + + with TestRun.step("Stopping cache"): + cache.stop() + + with TestRun.step("Loading cache"): + loaded_cache = casadm.load_cache(cache.cache_device) + + with TestRun.step("Getting cores from loaded cache"): + cores = loaded_cache.get_core_devices() + + for i, core in TestRun.iteration(enumerate(cores[:-1]), "Check if proper policies have " + "been loaded"): + with TestRun.step(f"Check if proper sequential cut off policy was loaded"): + if cores[i].get_seq_cut_off_policy() != policies[i]: + TestRun.fail(f"Wrong sequential cut off policy loaded: " + f"{cores[i].get_seq_cut_off_policy()} " + f"should be {policies[i]}") + + with TestRun.step(f"Check if proper (default) sequential cut off policy was loaded for " + f"last core"): + if cores[len(SeqCutOffPolicy)].get_seq_cut_off_policy() != SeqCutOffPolicy.DEFAULT: + TestRun.fail(f"Wrong sequential cut off policy loaded: " + f"{cores[len(SeqCutOffPolicy)].get_seq_cut_off_policy()} " + f"should be {SeqCutOffPolicy.DEFAULT}") + + +@pytest.mark.parametrize("threshold", random.sample( + range((int(SEQ_CUTOFF_THRESHOLD_MAX.get_value(Unit.KibiByte)) + 1), + c_uint32(-1).value), 3)) +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +def test_seq_cutoff_set_invalid_threshold(threshold): + """ + title: Invalid sequential cut-off threshold test + description: Test if CAS is allowing setting invalid sequential cut-off threshold + pass_criteria: + - Setting invalid sequential cut-off threshold should be blocked + """ + with TestRun.step("Test prepare (start cache and add core)"): + cache, cores = prepare() + _threshold = Size(threshold, Unit.KibiByte) + + with TestRun.step(f"Setting cache sequential cut off threshold to out of range value: " + f"{_threshold}"): + command = set_param_cutoff_cmd( + cache_id=str(cache.cache_id), core_id=str(cores[0].core_id), + threshold=str(int(_threshold.get_value()))) + output = TestRun.executor.run_expect_fail(command) + if "Invalid sequential cutoff threshold, must be in the range 1-4194181"\ + not in output.stderr: + TestRun.fail("Command succeeded (should fail)!") + + with TestRun.step(f"Setting cache sequential cut off threshold " + f"to value passed as a float"): + command = set_param_cutoff_cmd( + cache_id=str(cache.cache_id), core_id=str(cores[0].core_id), + threshold=str(_threshold.get_value())) + output = TestRun.executor.run_expect_fail(command) + if "Invalid sequential cutoff threshold, must be a correct unsigned decimal integer"\ + not in output.stderr: + TestRun.fail("Command succeeded (should fail)!") + + +@pytest.mark.parametrize("threshold", random.sample( + range(1, int(SEQ_CUTOFF_THRESHOLD_MAX.get_value(Unit.KibiByte))), 3)) +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +def test_seq_cutoff_set_get_threshold(threshold): + """ + title: Sequential cut-off threshold set/get test + description: | + Test if CAS is setting proper sequential cut-off threshold and returns + previously set value + pass_criteria: + - Sequential cut-off threshold obtained from get-param command must be the same as + the one used in set-param command + """ + with TestRun.step("Test prepare (start cache and add core)"): + cache, cores = prepare() + _threshold = Size(threshold, Unit.KibiByte) + + with TestRun.step(f"Setting cache sequential cut off threshold to " + f"{_threshold}"): + cores[0].set_seq_cutoff_threshold(_threshold) + + with TestRun.step("Check if proper sequential cut off threshold was set"): + if cores[0].get_seq_cut_off_threshold() != _threshold: + TestRun.fail(f"Wrong sequential cut off threshold set: " + f"{cores[0].get_seq_cut_off_threshold()} " + f"should be {_threshold}") + + +@pytest.mark.parametrize("threshold", random.sample( + range(1, int(SEQ_CUTOFF_THRESHOLD_MAX.get_value(Unit.KibiByte))), 3)) +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +def test_seq_cutoff_threshold_load(threshold): + """ + title: Sequential cut-off threshold set/get test with cache load between + description: | + Test if after cache load sequential cut-off threshold + value previously set is being loaded correctly. Each of possible sequential cut-off + policies is set for different core. + pass_criteria: + - Sequential cut-off threshold obtained from get-param command after cache load + must be the same as the one used in set-param command before cache stop + """ + with TestRun.step("Test prepare (start cache and add core)"): + cache, cores = prepare() + _threshold = Size(threshold, Unit.KibiByte) + + with TestRun.step(f"Setting cache sequential cut off threshold to " + f"{_threshold}"): + cores[0].set_seq_cutoff_threshold(_threshold) + + with TestRun.step("Stopping cache"): + cache.stop() + + with TestRun.step("Loading cache"): + loaded_cache = casadm.load_cache(cache.cache_device) + + with TestRun.step("Getting core from loaded cache"): + cores_load = loaded_cache.get_core_devices() + + with TestRun.step("Check if proper sequential cut off policy was loaded"): + if cores_load[0].get_seq_cut_off_threshold() != _threshold: + TestRun.fail(f"Wrong sequential cut off threshold set: " + f"{cores_load[0].get_seq_cut_off_threshold()} " + f"should be {_threshold}") + + +def prepare(cores_count=1): + cache_device = TestRun.disks['cache'] + core_device = TestRun.disks['core'] + cache_device.create_partitions([Size(500, Unit.MebiByte)]) + partitions = [] + for x in range(cores_count): + partitions.append(Size(1, Unit.GibiByte)) + + core_device.create_partitions(partitions) + cache_part = cache_device.partitions[0] + core_parts = core_device.partitions + TestRun.LOGGER.info("Staring cache") + cache = casadm.start_cache(cache_part, force=True) + TestRun.LOGGER.info("Adding core devices") + core_list = [] + for core_part in core_parts: + core_list.append(cache.add_core(core_dev=core_part)) + return cache, core_list