Merge pull request #1057 from jfckm/standby-load-config

Standby load config
This commit is contained in:
Robert Baldyga 2022-02-11 10:12:02 +01:00 committed by GitHub
commit 1742841ee3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 273 additions and 108 deletions

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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 = [

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"),
],
[],
),

View File

@ -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",
},
],
)

View File

@ -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

View File

@ -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(

View File

@ -24,7 +24,7 @@ Cache device <DEVICE>
.br
Cache mode {wt|wb|wa|pt|wo}
.br
Extra fields (optional) ioclass_file=<file>,cleaning_policy=<alru,nop>,promotion_policy=<always,nhit>
Extra fields (optional) ioclass_file=<file>,cleaning_policy=<alru,nop>,promotion_policy=<always,nhit>,target_failover_state=<active,standby>
.RE
.TP
\fB[cores]\fR Cores configuration. Following columns are required:

View File

@ -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)