diff --git a/casadm/cas_lib.c b/casadm/cas_lib.c index 3a4c89f..b5c64f5 100644 --- a/casadm/cas_lib.c +++ b/casadm/cas_lib.c @@ -967,9 +967,8 @@ int start_cache(uint16_t cache_id, unsigned int cache_init, { int fd = 0; struct kcas_start_cache cmd; - struct cache_device **caches; struct cache_device *cache; - int i, status, caches_count; + int i, status; double min_free_ram_gb; /* check if cache device given exists */ @@ -984,21 +983,6 @@ int start_cache(uint16_t cache_id, unsigned int cache_init, if (fd == -1) return FAILURE; - if (cache_id == 0) { - cache_id = 1; - caches = get_cache_devices(&caches_count, false); - if (caches != NULL) { - psort(caches, caches_count, sizeof(struct cache_device*), caches_compare); - for (i = 0; i < caches_count; ++i) { - if (caches[i]->id == cache_id) { - cache_id += 1; - } - } - - free_cache_devices_list(caches, caches_count); - } - } - memset(&cmd, 0, sizeof(cmd)); cmd.cache_id = cache_id; @@ -1014,9 +998,12 @@ int start_cache(uint16_t cache_id, unsigned int cache_init, cmd.line_size = line_size; cmd.force = (uint8_t)force; - if (run_ioctl_interruptible_retry(fd, KCAS_IOCTL_START_CACHE, &cmd, - "Starting cache", cache_id, OCF_CORE_ID_INVALID) < 0) { + status = run_ioctl_interruptible_retry(fd, KCAS_IOCTL_START_CACHE, &cmd, + "Starting cache", cache_id, OCF_CORE_ID_INVALID); + cache_id = cmd.cache_id; + if (status < 0) { close(fd); + if (cmd.ext_err_code == OCF_ERR_NO_FREE_RAM) { min_free_ram_gb = cmd.min_free_ram; min_free_ram_gb /= GiB; @@ -3014,7 +3001,7 @@ int standby_load(int cache_id, ocf_cache_line_size_t line_size, return start_cache(cache_id, CACHE_INIT_STANDBY_LOAD, cache_device, - ocf_cache_mode_default, + ocf_cache_mode_none, line_size, 0); } diff --git a/casadm/cas_main.c b/casadm/cas_main.c index e9eaafe..070a9fe 100644 --- a/casadm/cas_main.c +++ b/casadm/cas_main.c @@ -406,17 +406,24 @@ int handle_start() { int status; - if (command_args_values.state == CACHE_INIT_LOAD && command_args_values.force) { - cas_printf(LOG_ERR, "Use of 'load' and 'force' simultaneously is forbidden.\n"); - return FAILURE; - } + if (command_args_values.state == CACHE_INIT_LOAD) { + if (command_args_values.force || + command_args_values.line_size != ocf_cache_line_size_none || + command_args_values.cache_mode != ocf_cache_mode_none || + command_args_values.cache_id != OCF_CACHE_ID_INVALID) { + cas_printf(LOG_ERR, "Use of 'load' with 'force', 'cache-id'," + " 'cache-mode' or 'cache-line-size'" + " simultaneously is forbidden.\n"); + return FAILURE; + } + } else { + if (command_args_values.line_size == ocf_cache_line_size_none) { + command_args_values.line_size = ocf_cache_line_size_default; + } - if (command_args_values.line_size == ocf_cache_line_size_none) { - command_args_values.line_size = ocf_cache_line_size_default; - } - - if (command_args_values.cache_mode == ocf_cache_mode_none) { - command_args_values.cache_mode = ocf_cache_mode_default; + if (command_args_values.cache_mode == ocf_cache_mode_none) { + command_args_values.cache_mode = ocf_cache_mode_default; + } } if (validate_cache_path(command_args_values.cache_device) == FAILURE) @@ -1473,7 +1480,7 @@ int io_class_is_missing() { /* Option is set, check if this option is allowed */ mask = (1 << io_class_params.subcmd); if (0 == (mask & iter->priv)) { - cas_printf(LOG_INFO, "Option '%s' is not allowed\n", option_name); + cas_printf(LOG_ERR, "Option '%s' is not allowed\n", option_name); result = -1; } @@ -1481,7 +1488,7 @@ int io_class_is_missing() { /* Option is missing, check if it is required for this sub-command*/ mask = (1 << io_class_params.subcmd) | (1 << io_class_opt_flag_required); if (mask == (iter->priv & mask)) { - cas_printf(LOG_INFO, "Option '%s' is missing\n", option_name); + cas_printf(LOG_ERR, "Option '%s' is missing\n", option_name); result = -1; } } @@ -1741,12 +1748,12 @@ int script_command_is_valid() { if (option_is_set) { if (!is_option_allowed(option_id)) { - cas_printf(LOG_INFO, "Option '%s' is not allowed\n", option_name); + cas_printf(LOG_ERR, "Option '%s' is not allowed\n", option_name); result = FAILURE; } } else { if (is_option_required(option_id)) { - cas_printf(LOG_INFO, "Option '%s' is missing\n", option_name); + cas_printf(LOG_ERR, "Option '%s' is missing\n", option_name); result = FAILURE; } } @@ -1926,7 +1933,6 @@ static cli_option standby_params_options[] = { .args_count = 1, .arg = "ID", .priv = (1 << standby_opt_subcmd_init) - | (1 << standby_opt_subcmd_load) | (1 << standby_opt_subcmd_detach) | (1 << standby_opt_subcmd_activate) | (1 << standby_opt_flag_required), @@ -1941,7 +1947,6 @@ static cli_option standby_params_options[] = { .args_count = 1, .arg = "NUMBER", .priv = (1 << standby_opt_subcmd_init) - | (1 << standby_opt_subcmd_load) | (1 << standby_opt_flag_required), .flags = CLI_OPTION_DEFAULT_INT, .default_value = ocf_cache_line_size_default / KiB, @@ -2053,7 +2058,7 @@ int standby_is_missing() { /* Option is set, check if this option is allowed */ mask = (1 << standby_params.subcmd); if (0 == (mask & iter->priv)) { - cas_printf(LOG_INFO, "Option '%s' is not allowed\n", option_name); + cas_printf(LOG_ERR, "Option '%s' is not allowed\n", option_name); result = -1; } @@ -2061,7 +2066,7 @@ int standby_is_missing() { /* Option is missing, check if it is required for this sub-command*/ mask = (1 << standby_params.subcmd) | (1 << standby_opt_flag_required); if (mask == (iter->priv & mask)) { - cas_printf(LOG_INFO, "Option '%s' is missing\n", option_name); + cas_printf(LOG_ERR, "Option '%s' is missing\n", option_name); result = -1; } } @@ -2078,6 +2083,16 @@ int standby_handle() { return FAILURE; } + if (standby_params.subcmd == standby_opt_subcmd_load && + (standby_params.force || + standby_params.line_size != ocf_cache_line_size_none || + standby_params.cache_id != OCF_CACHE_ID_INVALID)) { + cas_printf(LOG_ERR, "Use of 'load' with 'force', 'cache-id'" + " or 'cache-line-size' simultaneously is" + " forbidden.\n"); + return FAILURE; + } + /* Check if all required options are set */ if (standby_is_missing()) { return FAILURE; diff --git a/modules/cas_cache/layer_cache_management.c b/modules/cas_cache/layer_cache_management.c index 4907a49..2d7322c 100644 --- a/modules/cas_cache/layer_cache_management.c +++ b/modules/cas_cache/layer_cache_management.c @@ -1839,7 +1839,6 @@ int cache_mngt_prepare_cache_device_cfg(struct ocf_mngt_cache_device_config *cfg &cfg->volume_type); } - int cache_mngt_prepare_cache_cfg(struct ocf_mngt_cache_config *cfg, struct ocf_mngt_cache_attach_config *attach_cfg, struct kcas_start_cache *cmd) @@ -1851,7 +1850,25 @@ int cache_mngt_prepare_cache_cfg(struct ocf_mngt_cache_config *cfg, if (!cmd) return -OCF_ERR_INVAL; - if (cmd->cache_id == OCF_CACHE_ID_INVALID) { + if (cmd->init_cache == CACHE_INIT_LOAD || + cmd->init_cache == CACHE_INIT_STANDBY_LOAD) { + if (cmd->cache_id != OCF_CACHE_ID_INVALID) { + printk(KERN_WARNING "Specifying cache id while loading " + "cache is forbidden\n"); + return -OCF_ERR_INVAL; + } + + if (cmd->line_size != ocf_cache_line_size_none) { + printk(KERN_WARNING "Specifying cache line size while " + "loading cache is forbidden\n"); + return -OCF_ERR_INVAL; + } + if (cmd->caching_mode != ocf_cache_mode_none) { + printk(KERN_WARNING "Specifying cache mode while " + "loading cache is forbidden\n"); + return -OCF_ERR_INVAL; + } + } else if (cmd->cache_id == OCF_CACHE_ID_INVALID) { cache_id = find_free_cache_id(cas_ctx); if (cache_id == OCF_CACHE_ID_INVALID) return -OCF_ERR_INVAL; @@ -2043,39 +2060,47 @@ static int _cache_mngt_cache_priv_init(ocf_cache_t cache) return 0; } -struct cache_mngt_check_metadata_context { +struct cache_mngt_probe_metadata_context { struct completion cmpl; char *cache_name; int *result; + + char *cache_name_meta; + ocf_cache_mode_t *cache_mode_meta; + ocf_cache_line_size_t *cache_line_size_meta; }; -static void cache_mngt_check_metadata_end(void *priv, int error, +static void cache_mngt_probe_metadata_end(void *priv, int error, struct ocf_metadata_probe_status *status) { - struct cache_mngt_check_metadata_context *context = priv; + struct cache_mngt_probe_metadata_context *context = priv; *context->result = error; if (error == -OCF_ERR_NO_METADATA) { printk(KERN_ERR "No cache metadata found!\n"); + goto err; } else if (error == -OCF_ERR_METADATA_VER) { printk(KERN_ERR "Cache metadata version mismatch\n"); + goto err; } else if (error) { printk(KERN_ERR "Failed to load cache metadata!\n"); - } else if (strncmp(status->cache_name, context->cache_name, - OCF_CACHE_NAME_SIZE)) { - *context->result = -OCF_ERR_CACHE_NAME_MISMATCH; - printk(KERN_ERR "Loaded cache name is invalid: %s!\n", - status->cache_name); + goto err; } + strlcpy(context->cache_name_meta, status->cache_name, + OCF_CACHE_NAME_SIZE); + *(context->cache_mode_meta) = status->cache_mode; + *(context->cache_line_size_meta) = status->cache_line_size; +err: complete(&context->cmpl); } -static int _cache_mngt_check_metadata(struct ocf_mngt_cache_config *cfg, - char *cache_path_name) +static int _cache_mngt_probe_metadata(char *cache_path_name, + char *cache_name_meta, ocf_cache_mode_t *cache_mode_meta, + ocf_cache_line_size_t *cache_line_size_meta) { - struct cache_mngt_check_metadata_context context; + struct cache_mngt_probe_metadata_context context; struct block_device *bdev; ocf_volume_t volume; char holder[] = "CAS CHECK METADATA\n"; @@ -2094,10 +2119,12 @@ static int _cache_mngt_check_metadata(struct ocf_mngt_cache_config *cfg, goto out_bdev; init_completion(&context.cmpl); - context.cache_name = cfg->name; context.result = &result; + context.cache_name_meta = cache_name_meta; + context.cache_mode_meta = cache_mode_meta; + context.cache_line_size_meta = cache_line_size_meta; - ocf_metadata_probe(cas_ctx, volume, cache_mngt_check_metadata_end, + ocf_metadata_probe(cas_ctx, volume, cache_mngt_probe_metadata_end, &context); wait_for_completion(&context.cmpl); @@ -2351,9 +2378,12 @@ int cache_mngt_init_instance(struct ocf_mngt_cache_config *cfg, struct kcas_start_cache *cmd) { struct _cache_mngt_attach_context *context; - ocf_cache_t cache; + ocf_cache_t cache, tmp_cache = NULL; + char cache_name_meta[OCF_CACHE_NAME_SIZE]; struct cache_priv *cache_priv; int result = 0, rollback_result = 0; + ocf_cache_mode_t cache_mode_meta; + ocf_cache_line_size_t cache_line_size_meta; if (!try_module_get(THIS_MODULE)) return -KCAS_ERR_SYSTEM; @@ -2367,11 +2397,40 @@ int cache_mngt_init_instance(struct ocf_mngt_cache_config *cfg, switch (cmd->init_cache) { case CACHE_INIT_LOAD: case CACHE_INIT_STANDBY_LOAD: - result = _cache_mngt_check_metadata(cfg, cmd->cache_path_name); + result = _cache_mngt_probe_metadata(cmd->cache_path_name, + cache_name_meta, &cache_mode_meta, + &cache_line_size_meta); if (result) { module_put(THIS_MODULE); return result; } + + /* Need to return name from metadata now for caller to properly + * communicate the error to user */ + if (cache_id_from_name(&cmd->cache_id, cache_name_meta)) { + printk(KERN_ERR "Improper cache name format on %s.\n", + cmd->cache_path_name); + + module_put(THIS_MODULE); + return -OCF_ERR_START_CACHE_FAIL; + } + + result = ocf_mngt_cache_get_by_name(cas_ctx, cache_name_meta, + OCF_CACHE_NAME_SIZE, &tmp_cache); + + if (result != -OCF_ERR_CACHE_NOT_EXIST) { + printk(KERN_ERR "Can't load %s. Cache using that name " + "already exists.\n", cache_name_meta); + + ocf_mngt_cache_put(tmp_cache); + module_put(THIS_MODULE); + return -OCF_ERR_CACHE_EXIST; + } + + result = 0; + strlcpy(cfg->name, cache_name_meta, OCF_CACHE_NAME_SIZE); + cfg->cache_mode = cache_mode_meta; + cfg->cache_line_size = cache_line_size_meta; default: break; } diff --git a/test/functional/api/cas/cli_messages.py b/test/functional/api/cas/cli_messages.py index 4ec2e85..97a4077 100644 --- a/test/functional/api/cas/cli_messages.py +++ b/test/functional/api/cas/cli_messages.py @@ -81,7 +81,8 @@ stop_cache_mounted_core = [ ] load_and_force = [ - r"Use of \'load\' and \'force\' simultaneously is forbidden\." + (r"Use of \'load\' and \'force\', \'cache-id\', \'cache-mode\' or \'cache-line-size\'", + r" simultaneously is forbidden.") ] try_add_core_sector_size_mismatch = [ diff --git a/test/smoke_test/cas_functions b/test/smoke_test/cas_functions index 0709c06..3b497e1 100644 --- a/test/smoke_test/cas_functions +++ b/test/smoke_test/cas_functions @@ -30,8 +30,11 @@ restore_config() { start_cache() { check_options ${FUNCNAME[0]} - local COMMAND="$CAS --start-cache --cache-device $CACHE_DEVICE_OPTION --cache-id $CACHE_ID_OPTION" + local COMMAND="$CAS --start-cache --cache-device $CACHE_DEVICE_OPTION" + if [ -n "$CACHE_ID_OPTION" ] ; then + COMMAND="$COMMAND --cache-id $CACHE_ID_OPTION" + fi if [ -n "$CACHE_FORCE_OPTION" ] ; then COMMAND="$COMMAND --force" fi diff --git a/test/smoke_test/cas_options b/test/smoke_test/cas_options index f376de4..d17b680 100644 --- a/test/smoke_test/cas_options +++ b/test/smoke_test/cas_options @@ -33,7 +33,7 @@ export ALL_OPTIONS=" # Specify ONLY required options here. The name of the variable should start with # uppercase function's name + "_REQUIRED_OPTIONS". -export START_CACHE_REQUIRED_OPTIONS="CACHE_ID_OPTION CACHE_DEVICE_OPTION" +export START_CACHE_REQUIRED_OPTIONS="CACHE_DEVICE_OPTION" export STOP_CACHE_REQUIRED_OPTIONS="CACHE_ID_OPTION" export ADD_CORE_REQUIRED_OPTIONS="CACHE_ID_OPTION CORE_DEVICE_OPTION" export TRY_ADD_CORE_REQUIRED_OPTIONS="CACHE_ID_OPTION CORE_ID_OPTION CORE_DEVICE_OPTION" @@ -67,7 +67,6 @@ export TURN_ON_NVME_DEVICE_REQUIRED_OPTIONS="CACHE_DEVICE_OPTION" export START_DUAL_LEVEL_CACHE_REQUIRED_OPTIONS="CACHE_ID_OPTION CACHE_DEVICE_OPTION" -export START_CACHE_REQUIRED_OPTIONS="CACHE_ID_OPTION CACHE_DEVICE_OPTION" export IO_CLASS_LIST_REQUIRED_OPTIONS="CACHE_ID_OPTION" export IO_CLASS_LOAD_REQUIRED_OPTIONS="CACHE_ID_OPTION CSV_FILE" diff --git a/test/smoke_test/incremental_load/01 b/test/smoke_test/incremental_load/01 index a94d011..70ae000 100755 --- a/test/smoke_test/incremental_load/01 +++ b/test/smoke_test/incremental_load/01 @@ -57,7 +57,7 @@ DEVICE_ID_OPTION="${CORE_DEVICE}-part3" DEMANDED_STATE_OPTION="Detached" check_d # Try to load cache device, check if it is running and if all cores status is appropirate -CACHE_ID_OPTION="1" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" CACHE_LOAD_METADATA_OPTION="y" start_cache +CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" CACHE_LOAD_METADATA_OPTION="y" start_cache DEVICE_ID_OPTION="${CACHE_DEVICE}-part1" DEMANDED_STATE_OPTION="Running" check_device_state DEVICE_ID_OPTION="${DEVICE_NAME}1-1" DEMANDED_STATE_OPTION="Active" check_device_state DEVICE_ID_OPTION="${DEVICE_NAME}1-2" DEMANDED_STATE_OPTION="Active" check_device_state diff --git a/test/smoke_test/incremental_load/02 b/test/smoke_test/incremental_load/02 index 72754f7..78ed23f 100755 --- a/test/smoke_test/incremental_load/02 +++ b/test/smoke_test/incremental_load/02 @@ -48,7 +48,7 @@ TARGET_DEVICE_OPTION="$CORE_DEVICE" PARTITION_SIZE_OPTION="4000M" PARTITION_IDS_ sleep 1 # Load cache, then add cores and check if chache is running # Try to load cache device, check its state and cores state -CACHE_ID_OPTION="1" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" CACHE_LOAD_METADATA_OPTION="y" start_cache +CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" CACHE_LOAD_METADATA_OPTION="y" start_cache DEVICE_ID_OPTION="${CACHE_DEVICE}-part1" DEMANDED_STATE_OPTION="Incomplete" check_device_state DEVICE_ID_OPTION="${DEVICE_NAME}1-1" DEMANDED_STATE_OPTION="Active" check_device_state DEVICE_ID_OPTION="${DEVICE_NAME}1-2" DEMANDED_STATE_OPTION="Active" check_device_state @@ -66,7 +66,7 @@ DEVICE_ID_OPTION="${CORE_DEVICE}-part1" DEMANDED_STATE_OPTION="Detached" check_d DEVICE_ID_OPTION="${CORE_DEVICE}-part2" DEMANDED_STATE_OPTION="Detached" check_device_state DEVICE_ID_OPTION="${CORE_DEVICE}-part3" DEMANDED_STATE_OPTION="Detached" check_device_state -CACHE_ID_OPTION="1" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" CACHE_LOAD_METADATA_OPTION="y" start_cache +CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" CACHE_LOAD_METADATA_OPTION="y" start_cache DEVICE_ID_OPTION="${CACHE_DEVICE}-part1" DEMANDED_STATE_OPTION="Running" check_device_state DEVICE_ID_OPTION="${DEVICE_NAME}1-1" DEMANDED_STATE_OPTION="Active" check_device_state diff --git a/test/smoke_test/metadata_corrupt/01 b/test/smoke_test/metadata_corrupt/01 index f30e5c5..73eb91c 100755 --- a/test/smoke_test/metadata_corrupt/01 +++ b/test/smoke_test/metadata_corrupt/01 @@ -114,7 +114,7 @@ do fi # Start again with load option, this should fail, metadata is corrupted. - NEGATIVE_TEST_OPTION="1" CACHE_ID_OPTION="1" CACHE_LOAD_METADATA_OPTION="1" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" start_cache + NEGATIVE_TEST_OPTION="1" CACHE_LOAD_METADATA_OPTION="1" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" start_cache done diff --git a/test/smoke_test/metadata_corrupt/02 b/test/smoke_test/metadata_corrupt/02 index 8c10796..2b7236d 100755 --- a/test/smoke_test/metadata_corrupt/02 +++ b/test/smoke_test/metadata_corrupt/02 @@ -91,7 +91,7 @@ do fi # Start again with load option, this should fail, metadata is corrupted. - NEGATIVE_TEST_OPTION="1" CACHE_ID_OPTION="1" CACHE_LOAD_METADATA_OPTION="1" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" start_cache + NEGATIVE_TEST_OPTION="1" CACHE_LOAD_METADATA_OPTION="1" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" start_cache done test_log_stop diff --git a/test/smoke_test/promotion/01 b/test/smoke_test/promotion/01 index 90cf8af..b97ca47 100755 --- a/test/smoke_test/promotion/01 +++ b/test/smoke_test/promotion/01 @@ -108,7 +108,7 @@ CACHE_ID_OPTION="1" PROMO_POL_NS_OPTION="promotion-nhit" THRESHOLD_OPTION="451" CACHE_ID_OPTION="1" PROMO_POL_NS_OPTION="promotion-nhit" THRESHOLD_OPTION="812" TRIGGER_OPTION="49" set_promotion_params CACHE_ID_OPTION="1" PROMO_POL_OPTION="nhit" set_promotion_policy CACHE_ID_OPTION="1" stop_cache -CACHE_MODE_OPTION="wt" CACHE_ID_OPTION="1" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" CACHE_LOAD_METADATA_OPTION="1" start_cache +CACHE_MODE_OPTION="wt" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part1" CACHE_LOAD_METADATA_OPTION="1" start_cache CACHE_ID_OPTION="1" PROMO_POL_OPTION="nhit" check_promotion_policy CACHE_ID_OPTION="1" PROMO_POL_NS_OPTION="promotion-nhit" THRESHOLD_OPTION="812" TRIGGER_OPTION="49" check_promotion_params diff --git a/test/smoke_test/recovery/01 b/test/smoke_test/recovery/01 index 4ab49dd..68b46b4 100755 --- a/test/smoke_test/recovery/01 +++ b/test/smoke_test/recovery/01 @@ -72,7 +72,7 @@ done CACHE_DEVICE_OPTION="${SHORT_LINK}" turn_on_device for ID in 1 2 3 ; do - CACHE_ID_OPTION="$ID" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part${ID}" CACHE_LOAD_METADATA_OPTION="y" CACHE_MODE_OPTION="wb" start_cache + CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part${ID}" CACHE_LOAD_METADATA_OPTION="y" start_cache CACHE_ID_OPTION="$ID" stop_cache run_cmd "mount ${CORE_DEVICE}-part${ID} ${MOUNTPOINT}-${ID}-1" done diff --git a/test/smoke_test/recovery/02 b/test/smoke_test/recovery/02 index 4423750..7c512f6 100755 --- a/test/smoke_test/recovery/02 +++ b/test/smoke_test/recovery/02 @@ -72,7 +72,7 @@ done CACHE_DEVICE_OPTION="${SHORT_LINK}" turn_on_device for ID in 1 2 3 ; do - CACHE_ID_OPTION="$ID" CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part${ID}" CACHE_LOAD_METADATA_OPTION="y" CACHE_MODE_OPTION="wb" start_cache + CACHE_DEVICE_OPTION="${CACHE_DEVICE}-part${ID}" CACHE_LOAD_METADATA_OPTION="y" start_cache CACHE_ID_OPTION="$ID" CORE_ID_OPTION="1" mount_cache done diff --git a/test/utils_tests/opencas-py-tests/test_cas_config_01.py b/test/utils_tests/opencas-py-tests/test_cas_config_01.py index efa4c67..b52ebe3 100644 --- a/test/utils_tests/opencas-py-tests/test_cas_config_01.py +++ b/test/utils_tests/opencas-py-tests/test_cas_config_01.py @@ -316,7 +316,7 @@ def test_cas_config_get_by_id_path_not_found(mock_listdir, mock_realpath): "2 /dev/dummy0n2 pt ioclass_file=mango.csv", "3 /dev/dummy0n3 WA cache_line_size=16", ("4 /dev/dummyc wb cache_line_size=16," - "ioclass_file=mango.csv,cleaning_policy=nop"), + "ioclass_file=mango.csv,cleaning_policy=nop,target_failover_state=standby"), ], [], ), diff --git a/test/utils_tests/opencas-py-tests/test_cas_config_cache_01.py b/test/utils_tests/opencas-py-tests/test_cas_config_cache_01.py index a2a9fec..897c668 100644 --- a/test/utils_tests/opencas-py-tests/test_cas_config_cache_01.py +++ b/test/utils_tests/opencas-py-tests/test_cas_config_cache_01.py @@ -187,8 +187,10 @@ def test_cache_config_from_line_missing_ioclass_file( "ioclass_file=ioclass.csv,ioclass_file=ioclass.csv", "cleaning_policy=nop,cleaning_policy=acp", "cleaning_policy=", + "clining_polisi=nop", "cleaning_policy=INVALID", "ioclass_file=ioclass.csv, cleaning_policy=nop", + "ioclas_file=ioclass.csv", "cache_line_size=4,cache_line_size=8", "cache_line_size=", "cache_line_size=0", @@ -198,14 +200,20 @@ def test_cache_config_from_line_missing_ioclass_file( "cache_line_size=-1", "cache_line_size=four", "cache_line_size=128", + "cach_lin_siz=4", "promotion_policy=111111", "promotion_policy=", "promotion_policy=dinosaurs", "promotion_policy=Robert'); DROP TABLE Students;--", "promotion_policy=awlays", "promotion_policy=nnhit", + "demolition_policy=nhit", "lazy_startup=yes", "lazy_startup=absolutely", + "hasty_startup=true", + "target_failover_state=no", + "target_failover_state=maybe", + "target_failrover_state=standby", ], ) @mock.patch("os.path.exists") @@ -242,10 +250,12 @@ def test_cache_config_from_line_parameter_validation_01( "ioclass_file=ioclass.csv,cache_line_size=4,cleaning_policy=nop", "promotion_policy=nhit", "promotion_policy=always", + "target_failover_state=standby", + "target_failover_state=active", "lazy_startup=true", "lazy_startup=false", ("ioclass_file=ioclass.csv,cache_line_size=4,cleaning_policy=nop,promotion_policy=always," - "lazy_startup=true"), + "lazy_startup=true,target_failover_state=active"), ], ) @mock.patch("os.path.exists") @@ -416,6 +426,7 @@ def test_cache_config_from_line_cache_id_validation_02( "cache_mode": "wo", "promotion_policy": "always", "cache_line_size": "16", + "lazy_startup": "true" }, { "cache_id": "1", @@ -423,6 +434,7 @@ def test_cache_config_from_line_cache_id_validation_02( "cache_mode": "wo", "promotion_policy": "nhit", "cache_line_size": "16", + "target_failover_state": "active", }, ], ) diff --git a/test/utils_tests/opencas-py-tests/test_helper_functions_01.py b/test/utils_tests/opencas-py-tests/test_helper_functions_01.py index 3518a04..c71dcee 100644 --- a/test/utils_tests/opencas-py-tests/test_helper_functions_01.py +++ b/test/utils_tests/opencas-py-tests/test_helper_functions_01.py @@ -6,6 +6,7 @@ import pytest from unittest.mock import patch, Mock import time +import subprocess import opencas @@ -74,7 +75,7 @@ def test_cas_settle_cores_didnt_start_02(mock_add, mock_exists, mock_run, mock_l "type": "cache", "id": "1", "disk": "/dev/dummy_cache", - "status": "Active", + "status": "Standby", "write policy": "wt", "device": "-", } @@ -303,7 +304,11 @@ def test_cas_settle_caches_didnt_start_01( mock_config.return_value = Mock( spec_set=opencas.cas_config(), cores=[], - caches={42: opencas.cas_config.cache_config(42, "/dev/dummy", "wt")}, + caches={ + 42: opencas.cas_config.cache_config( + 42, "/dev/dummy", "wt", target_failover_state="standby" + ) + }, ) result = opencas.wait_for_startup(timeout=0, interval=0) @@ -345,7 +350,7 @@ def test_cas_settle_caches_didnt_start_02( result = opencas.wait_for_startup(timeout=0, interval=0) - assert len(result) == 1, "didn't return uninitialized core" + assert len(result) == 1, "didn't return uninitialized cache" @patch("opencas.cas_config.from_file") @@ -416,7 +421,7 @@ def test_cas_settle_caches_didnt_start_03( result = opencas.wait_for_startup(timeout=0, interval=0) - assert len(result) == 2, "didn't return uninitialized cores" + assert len(result) == 2, "didn't return uninitialized caches" @patch("opencas.cas_config.from_file") @@ -802,10 +807,10 @@ def test_last_resort_add_02(mock_start, mock_add, mock_exists, mock_run, mock_li result = opencas.wait_for_startup(timeout=0, interval=0) - mock_start.assert_any_call(config.caches[1], True) - mock_start.assert_any_call(config.caches[2], True) - mock_add.assert_any_call(config.cores[0], True) - mock_add.assert_any_call(config.cores[1], True) + mock_start.assert_any_call(config.caches[1], load=True) + mock_start.assert_any_call(config.caches[2], load=True) + mock_add.assert_any_call(config.cores[0], try_add=True) + mock_add.assert_any_call(config.cores[1], try_add=True) mock_run.assert_called_with(["udevadm", "settle"]) @@ -883,10 +888,10 @@ def test_last_resort_add_04(mock_start, mock_add, mock_exists, mock_run, mock_li result = opencas.wait_for_startup(timeout=2, interval=0.1) - mock_start.assert_any_call(config.caches[1], True) - mock_start.assert_any_call(config.caches[2], True) - mock_add.assert_any_call(config.cores[0], True) - mock_add.assert_any_call(config.cores[1], True) + mock_start.assert_any_call(config.caches[1], load=True) + mock_start.assert_any_call(config.caches[2], load=True) + mock_add.assert_any_call(config.cores[0], try_add=True) + mock_add.assert_any_call(config.cores[1], try_add=True) mock_run.assert_called_with(["udevadm", "settle"]) @@ -896,7 +901,7 @@ def test_last_resort_add_04(mock_start, mock_add, mock_exists, mock_run, mock_li @patch("os.path.exists") @patch("opencas.add_core") @patch("opencas.start_cache") -def test_last_resort_add_04(mock_start, mock_add, mock_exists, mock_run, mock_list, mock_config): +def test_last_resort_add_05(mock_start, mock_add, mock_exists, mock_run, mock_list, mock_config): """ Check if adding cores/starting caches is attempted while waiting for startup for lazy_startup devices once before returning. @@ -919,11 +924,11 @@ def test_last_resort_add_04(mock_start, mock_add, mock_exists, mock_run, mock_li result = opencas.wait_for_startup(timeout=0.5, interval=0.1) - mock_start.assert_any_call(config.caches[1], True) - mock_start.assert_any_call(config.caches[2], True) + mock_start.assert_any_call(config.caches[1], load=True) + mock_start.assert_any_call(config.caches[2], load=True) assert mock_start.call_count == 2, "start cache was called more than once per device" - mock_add.assert_any_call(config.cores[0], True) - mock_add.assert_any_call(config.cores[1], True) + mock_add.assert_any_call(config.cores[0], try_add=True) + mock_add.assert_any_call(config.cores[1], try_add=True) assert mock_add.call_count == 2, "add core was called more than once per device" mock_run.assert_called_with(["udevadm", "settle"]) @@ -934,7 +939,7 @@ def test_last_resort_add_04(mock_start, mock_add, mock_exists, mock_run, mock_li @patch("os.path.exists") @patch("opencas.add_core") @patch("opencas.start_cache") -def test_last_resort_add_05(mock_start, mock_add, mock_exists, mock_run, mock_list, mock_config): +def test_last_resort_add_06(mock_start, mock_add, mock_exists, mock_run, mock_list, mock_config): """ Check if adding cores/starting caches is not attempted while waiting for startup for lazy startup devices if paths show up after half of the startup timeout expires. @@ -960,3 +965,53 @@ def test_last_resort_add_05(mock_start, mock_add, mock_exists, mock_run, mock_li mock_start.assert_not_called() mock_add.assert_not_called() mock_run.assert_called_with(["udevadm", "settle"]) + + +def assert_option_value(call, option, value): + try: + index = call.index(option) + except ValueError as e: + raise AssertionError(f"{option} not found in call ({call})") from e + + assert call[index + 1] == value + + +@pytest.mark.parametrize("failover", ["standby", "active"]) +@pytest.mark.parametrize("force", [True, False]) +@pytest.mark.parametrize("load", [True, False]) +@patch("subprocess.run") +def test_start_cache(mock_run, load, force, failover): + cache_config = opencas.cas_config.cache_config( + 1, + "/dev/lizards", + "wt", + lazy_startup="true", + cache_line_size="64", + target_failover_state=failover, + ) + mock_run.return_value = Mock( + returncode=0, + stderr="", + stdout="", + ) + + opencas.start_cache(cache_config, load, force) + + casadm_call = mock_run.call_args[0][0] + assert "/sbin/casadm" in casadm_call + assert_option_value(casadm_call, "--cache-device", "/dev/lizards") + + if not load: + assert_option_value(casadm_call, "--cache-id", "1") + assert_option_value(casadm_call, "--cache-line-size", "64") + if failover == "active": + assert "--start-cache" in casadm_call + assert_option_value(casadm_call, "--cache-mode", "wt") + else: + assert "--standby" in casadm_call + assert "--init" in casadm_call + else: + assert "--load" in casadm_call + assert "--cache-id" not in casadm_call + assert "--cache-mode" not in casadm_call + assert "--cache-line-size" not in casadm_call diff --git a/utils/casctl b/utils/casctl index dad0d3a..d5bdd5e 100755 --- a/utils/casctl +++ b/utils/casctl @@ -44,7 +44,7 @@ def start(): for cache in config.caches.values(): try: - opencas.start_cache(cache, True) + opencas.start_cache(cache, load=True) except opencas.casadm.CasadmError as e: eprint( "Unable to load cache {0} ({1}). Reason:\n{2}".format( @@ -116,7 +116,7 @@ def init(force): for cache in config.caches.values(): try: - opencas.start_cache(cache, False, force) + opencas.start_cache(cache, load=False, force=force) except opencas.casadm.CasadmError as e: eprint( "Unable to start cache {0} ({1}). Reason:\n{2}".format( diff --git a/utils/opencas.conf.5 b/utils/opencas.conf.5 index c70091e..73a9f24 100644 --- a/utils/opencas.conf.5 +++ b/utils/opencas.conf.5 @@ -24,7 +24,7 @@ Cache device .br Cache mode {wt|wb|wa|pt|wo} .br -Extra fields (optional) ioclass_file=,cleaning_policy=,promotion_policy= +Extra fields (optional) ioclass_file=,cleaning_policy=,promotion_policy=,target_failover_state= .RE .TP \fB[cores]\fR Cores configuration. Following columns are required: diff --git a/utils/opencas.py b/utils/opencas.py index a2c6f55..f3ae112 100644 --- a/utils/opencas.py +++ b/utils/opencas.py @@ -60,8 +60,9 @@ class casadm: return cls.run_cmd(cmd) @classmethod - def start_cache(cls, device, cache_id=None, cache_mode=None, - cache_line_size=None, load=False, force=False): + def start_cache( + cls, device, cache_id=None, cache_mode=None, cache_line_size=None, load=False, force=False + ): cmd = [cls.casadm_path, '--start-cache', '--cache-device', device] @@ -77,6 +78,22 @@ class casadm: cmd += ['--force'] return cls.run_cmd(cmd) + @classmethod + def start_standby_cache( + cls, device, cache_id=None, cache_line_size=None, load=False, force=False + ): + cmd = [cls.casadm_path, + '--standby', + '--init' if not load else '--load', + '--cache-device', device] + if cache_id: + cmd += ['--cache-id', str(cache_id)] + if cache_line_size: + cmd += ['--cache-line-size', str(cache_line_size)] + if force: + cmd += ['--force'] + return cls.run_cmd(cmd) + @classmethod def add_core(cls, device, cache_id, core_id=None, try_add=False): cmd = [cls.casadm_path, @@ -197,7 +214,7 @@ class cas_config(object): def __init__(self, cache_id, device, cache_mode, **params): self.cache_id = int(cache_id) self.device = device - self.cache_mode = cache_mode + self.cache_mode = cache_mode.lower() self.params = params self.cores = dict() @@ -215,7 +232,7 @@ class cas_config(object): params = dict() if len(values) > 3: - for param in values[3].split(','): + for param in values[3].lower().split(','): param_name, param_value = param.split('=') if param_name in params: raise ValueError('Invalid cache configuration (repeated parameter') @@ -250,6 +267,8 @@ class cas_config(object): self.check_cache_line_size_valid(param_value) elif param_name == "lazy_startup": self.check_lazy_startup_valid(param_value) + elif param_name == "target_failover_state": + self.check_failover_state_valid(param_value) else: raise ValueError(f'{param_name} is invalid parameter name') @@ -273,19 +292,23 @@ class cas_config(object): ) def check_cache_mode_valid(self, cache_mode): - if cache_mode.lower() not in ['wt', 'pt', 'wa', 'wb', 'wo']: + if cache_mode not in ['wt', 'pt', 'wa', 'wb', 'wo']: raise ValueError(f'Invalid cache mode {cache_mode}') def check_cleaning_policy_valid(self, cleaning_policy): - if cleaning_policy.lower() not in ['acp', 'alru', 'nop']: + if cleaning_policy not in ['acp', 'alru', 'nop']: raise ValueError(f'{cleaning_policy} is invalid cleaning policy name') def check_lazy_startup_valid(self, lazy_startup): - if lazy_startup.lower() not in ["true", "false"]: + if lazy_startup not in ["true", "false"]: raise ValueError('{0} is invalid lazy_startup value'.format(lazy_startup)) + def check_failover_state_valid(self, failover_state): + if failover_state not in ["active", "standby"]: + raise ValueError(f"{failover_state} is invalid target_failover_state value") + def check_promotion_policy_valid(self, promotion_policy): - if promotion_policy.lower() not in ['always', 'nhit']: + if promotion_policy not in ['always', 'nhit']: raise ValueError(f'{promotion_policy} is invalid promotion policy name') def check_cache_line_size_valid(self, cache_line_size): @@ -319,7 +342,7 @@ class cas_config(object): return ret def is_lazy(self): - return self.params.get("lazy_startup", "false").lower() == "true" + return self.params.get("lazy_startup", "false") == "true" class core_config(object): def __init__(self, cache_id, core_id, path, **params): @@ -369,7 +392,7 @@ class cas_config(object): def validate_parameter(self, param_name, param_value): if param_name == "lazy_startup": - if param_value.lower() not in ["true", "false"]: + if param_value not in ["true", "false"]: raise ValueError( f"{param_value} is invalid value for '{param_name}' core param" ) @@ -401,7 +424,7 @@ class cas_config(object): return ret def is_lazy(self): - return self.params.get("lazy_startup", "false").lower() == "true" + return self.params.get("lazy_startup", "false") == "true" def __init__(self, caches=None, cores=None, version_tag=None): self.caches = caches if caches else dict() @@ -545,13 +568,24 @@ class cas_config(object): def start_cache(cache, load, force=False): - casadm.start_cache( + target_state = cache.params.get("target_failover_state") + if target_state is not None and target_state == "standby": + casadm.start_standby_cache( device=cache.device, - cache_id=cache.cache_id, - cache_mode=cache.cache_mode, - cache_line_size=cache.params.get('cache_line_size'), + cache_id=cache.cache_id if not load else None, + cache_line_size=cache.params.get("cache_line_size") if not load else None, load=load, - force=force) + force=force + ) + else: + casadm.start_cache( + device=cache.device, + cache_id=cache.cache_id if not load else None, + cache_mode=cache.cache_mode if not load else None, + cache_line_size=cache.params.get('cache_line_size') if not load else None, + load=load, + force=force + ) def configure_cache(cache): @@ -820,9 +854,9 @@ def wait_for_startup(timeout=300, interval=5): def start_device(dev): if os.path.exists(dev.device): if type(dev) is cas_config.core_config: - add_core(dev, True) + add_core(dev, try_add=True) elif type(dev) is cas_config.cache_config: - start_cache(dev, True) + start_cache(dev, load=True) stop_time = time.time() + int(timeout)