Add IO class test for loading wrong IO class configuration
Signed-off-by: Katarzyna Lapinska <katarzyna.lapinska@intel.com>
This commit is contained in:
parent
83224dd81b
commit
a495181adb
@ -169,6 +169,68 @@ cache_line_size_mismatch = [
|
||||
r"Cache line size mismatch"
|
||||
]
|
||||
|
||||
headerless_io_class_config = [
|
||||
r'Cannot parse configuration file - unknown column "1"\.\n'
|
||||
r'Failed to parse I/O classes configuration file header\. It is either malformed or missing\.\n'
|
||||
r'Please consult Admin Guide to check how columns in configuration file should be named\.'
|
||||
]
|
||||
|
||||
illegal_io_class_config_L2C1 = [
|
||||
r"Cannot parse configuration file - error in line 2 in column 1 \(IO class id\)\."
|
||||
]
|
||||
|
||||
illegal_io_class_config_L2C2 = [
|
||||
r"Empty or too long IO class name\n"
|
||||
r"Cannot parse configuration file - error in line 2 in column 2 \(IO class name\)\."
|
||||
]
|
||||
|
||||
illegal_io_class_config_L2C4 = [
|
||||
r"Cannot parse configuration file - error in line 2 in column 4 \(Allocation\)\."
|
||||
]
|
||||
|
||||
illegal_io_class_config_L2 = [
|
||||
r"Cannot parse configuration file - error in line 2\."
|
||||
]
|
||||
|
||||
double_io_class_config = [
|
||||
r"Double configuration for IO class id \d+\n"
|
||||
r"Cannot parse configuration file - error in line \d+ in column \d+ \(IO class id\)\."
|
||||
]
|
||||
|
||||
illegal_io_class_invalid_id = [
|
||||
r"Invalid id, must be a correct unsigned decimal integer\.\n"
|
||||
r"Cannot parse configuration file - error in line 2 in column 1 \(IO class id\)\."
|
||||
]
|
||||
|
||||
illegal_io_class_invalid_id_number = [
|
||||
r"Invalid id, must be in the range 0-32\.\n"
|
||||
r"Cannot parse configuration file - error in line 2 in column 1 \(IO class id\)\."
|
||||
]
|
||||
|
||||
illegal_io_class_invalid_priority = [
|
||||
r"Invalid prio, must be a correct unsigned decimal integer\.\n"
|
||||
r"Cannot parse configuration file - error in line 2 in column 3 \(Eviction priority\)"
|
||||
]
|
||||
|
||||
illegal_io_class_invalid_priority_number = [
|
||||
r"Invalid prio, must be in the range 0-255\.\n"
|
||||
r"Cannot parse configuration file - error in line 2 in column 3 \(Eviction priority\)"
|
||||
]
|
||||
|
||||
illegal_io_class_invalid_allocation = [
|
||||
r"Cannot parse configuration file - error in line 2 in column 4 \(Allocation\)\."
|
||||
]
|
||||
|
||||
illegal_io_class_invalid_allocation_number = [
|
||||
r"Cannot parse configuration file - error in line 2 in column 4 \(Allocation\)\."
|
||||
]
|
||||
|
||||
malformed_io_class_header = [
|
||||
r'Cannot parse configuration file - unknown column \"value_template\"\.\n'
|
||||
r'Failed to parse I/O classes configuration file header\. It is either malformed or missing\.\n'
|
||||
r'Please consult Admin Guide to check how columns in configuration file should be named\.'
|
||||
]
|
||||
|
||||
|
||||
def check_stderr_msg(output: Output, expected_messages):
|
||||
return __check_string_msg(output.stderr, expected_messages)
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright(c) 2019-2021 Intel Corporation
|
||||
# Copyright(c) 2019-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
@ -87,6 +87,19 @@ class IoClass:
|
||||
def default(priority=DEFAULT_IO_CLASS_PRIORITY, allocation="1.00"):
|
||||
return IoClass(DEFAULT_IO_CLASS_ID, DEFAULT_IO_CLASS_RULE, priority, allocation)
|
||||
|
||||
@staticmethod
|
||||
def default_header_dict():
|
||||
return {
|
||||
"id": "IO class id",
|
||||
"name": "IO class name",
|
||||
"eviction_prio": "Eviction priority",
|
||||
"allocation": "Allocation"
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def default_header():
|
||||
return ','.join(IoClass.default_header_dict().values())
|
||||
|
||||
@staticmethod
|
||||
def compare_ioclass_lists(list1: [], list2: []):
|
||||
return sorted(list1) == sorted(list2)
|
||||
|
@ -0,0 +1,188 @@
|
||||
#
|
||||
# Copyright(c) 2022 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import pytest
|
||||
from api.cas import ioclass_config, cli_messages
|
||||
from core.test_run import TestRun
|
||||
from test_utils.output import CmdException
|
||||
from test_utils.size import Unit, Size
|
||||
from tests.io_class.io_class_common import prepare, ioclass_config_path
|
||||
from api.cas.ioclass_config import IoClass
|
||||
from storage_devices.disk import DiskType, DiskTypeSet, DiskTypeLowerThan
|
||||
from test_tools import fs_utils
|
||||
|
||||
headerless_configuration = "1,unclassified,22,1.00"
|
||||
double_io_class_configuration = "2,file_size:le:4096,1,1.00\n2,file_size:le:4096,1,1.00"
|
||||
malformed_io_class_allocation = "malformed allocation"
|
||||
malformed_io_class_eviction_priority = "malformed eviction priority"
|
||||
malformed_io_class_id = "malformed IO class id"
|
||||
malformed_io_class_name = "malformed IO class name"
|
||||
|
||||
# Illegal configurations and expected error messages:
|
||||
illegal_io_class_configurations = {
|
||||
# Use only 0, 1, 2, 3 and 5 parameters number in one line as integer values
|
||||
# 1 parameter
|
||||
",,,1": cli_messages.illegal_io_class_config_L2C1,
|
||||
",,1,": cli_messages.illegal_io_class_config_L2C1,
|
||||
",1,,": cli_messages.illegal_io_class_config_L2C1,
|
||||
"1,,,": cli_messages.illegal_io_class_config_L2C2,
|
||||
|
||||
# 2 parameters
|
||||
",,1,1": cli_messages.illegal_io_class_config_L2C1,
|
||||
",1,,1": cli_messages.illegal_io_class_config_L2C1,
|
||||
",1,1,": cli_messages.illegal_io_class_config_L2C1,
|
||||
"1,,1,": cli_messages.illegal_io_class_config_L2C2,
|
||||
"1,,,1": cli_messages.illegal_io_class_config_L2C2,
|
||||
"1,1,,": cli_messages.illegal_io_class_config_L2C4,
|
||||
|
||||
# 3 parameters
|
||||
",1,1,1": cli_messages.illegal_io_class_config_L2C1,
|
||||
"1,,1,1": cli_messages.illegal_io_class_config_L2C2,
|
||||
"1,1,1,": cli_messages.illegal_io_class_config_L2C4,
|
||||
|
||||
# 5 parameters
|
||||
"1,1,1,1,1": cli_messages.illegal_io_class_config_L2,
|
||||
|
||||
# Try to configure IO class ID as: string, negative value or 33
|
||||
"IllegalInput,Superblock,22,1": cli_messages.illegal_io_class_invalid_id,
|
||||
"-2,Superblock,22,1": cli_messages.illegal_io_class_invalid_id_number,
|
||||
"33,Superblock,22,1": cli_messages.illegal_io_class_invalid_id_number,
|
||||
|
||||
# Try to use semicolon, dots or new line as csv delimiters
|
||||
"1;1;1;1": cli_messages.illegal_io_class_config_L2,
|
||||
"1.1.1.1": cli_messages.illegal_io_class_config_L2,
|
||||
"1\n1\n1\n1": cli_messages.illegal_io_class_config_L2,
|
||||
|
||||
# Try to configure eviction priority as: string, negative value or 256
|
||||
"1,Superblock,IllegalInput,1": cli_messages.illegal_io_class_invalid_priority,
|
||||
"1,Superblock,-2,1": cli_messages.illegal_io_class_invalid_priority_number,
|
||||
"1,Superblock,256,1": cli_messages.illegal_io_class_invalid_priority_number,
|
||||
|
||||
# Try to configure allocation as: string, negative value or 2
|
||||
"1,Superblock,22,IllegalInput": cli_messages.illegal_io_class_invalid_allocation,
|
||||
"1,Superblock,255,-2": cli_messages.illegal_io_class_invalid_allocation_number,
|
||||
"1,Superblock,255,2": cli_messages.illegal_io_class_invalid_allocation_number
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
|
||||
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
|
||||
def test_io_class_prevent_wrong_configuration():
|
||||
"""
|
||||
title: Open CAS ability to prevent loading wrong configuration.
|
||||
description: |
|
||||
Check Open CAS ability to prevent loading configuration from wrong configuration file
|
||||
like: illegal number of parameters in line, IO class values, using semicolon
|
||||
instead of comma, wrong value of eviction, and priority.
|
||||
pass_criteria:
|
||||
- Wrong configuration must not be loaded
|
||||
- There is an appropriate message about wrong io class configuration
|
||||
"""
|
||||
with TestRun.step("Prepare CAS device"):
|
||||
cache, core = prepare(cache_size=Size(150, Unit.MiB), core_size=Size(300, Unit.MiB))
|
||||
|
||||
with TestRun.step("Display IO class configuration – shall be default"):
|
||||
create_and_load_default_io_class_config(cache)
|
||||
loaded_io_classes = cache.list_io_classes()
|
||||
loaded_io_classes_str = '\n'.join(str(i) for i in loaded_io_classes)
|
||||
TestRun.LOGGER.info(f"Loaded IO class configuration is:\n"
|
||||
f"{IoClass.default_header()}\n{loaded_io_classes_str}")
|
||||
|
||||
config_io_classes = IoClass.csv_to_list(fs_utils.read_file(ioclass_config_path))
|
||||
if not IoClass.compare_ioclass_lists(config_io_classes, loaded_io_classes):
|
||||
TestRun.fail("Default IO class configuration not loaded correctly.")
|
||||
|
||||
with TestRun.step("Create illegal configuration file containing IO configuration "
|
||||
"without header and check if it can not be loaded."):
|
||||
TestRun.LOGGER.info(f"Preparing headerless configuration file with following content:\n"
|
||||
f"{headerless_configuration}")
|
||||
fs_utils.write_file(ioclass_config_path, headerless_configuration)
|
||||
try_load_malformed_config(cache, config_io_classes,
|
||||
expected_err_msg=cli_messages.headerless_io_class_config)
|
||||
|
||||
with TestRun.step("Create illegal configuration file containing IO configuration with "
|
||||
"malformed header and check if it can not be loaded."):
|
||||
for header, err_message in setup_headers().items():
|
||||
config_content = f"{header}\n{IoClass.default()}"
|
||||
TestRun.LOGGER.info(f"Testing following header with default IO class:\n"
|
||||
f"{config_content}")
|
||||
fs_utils.write_file(ioclass_config_path, config_content)
|
||||
try_load_malformed_config(cache, config_io_classes,
|
||||
expected_err_msg=err_message)
|
||||
|
||||
with TestRun.step("Create illegal configuration file containing double IO class configuration "
|
||||
"and check if it can not be loaded."):
|
||||
config_content = f"{IoClass.default_header()}\n{double_io_class_configuration}"
|
||||
TestRun.LOGGER.info(f"Testing following configuration file:\n{config_content}")
|
||||
fs_utils.write_file(ioclass_config_path, config_content)
|
||||
try_load_malformed_config(cache, config_io_classes,
|
||||
expected_err_msg=cli_messages.double_io_class_config)
|
||||
|
||||
with TestRun.step("Create illegal configuration file containing malformed IO configuration "
|
||||
"with correct header and check if it can not be loaded."):
|
||||
for io_config, err_message in illegal_io_class_configurations.items():
|
||||
config_content = f"{IoClass.default_header()}\n{io_config}"
|
||||
TestRun.LOGGER.info(
|
||||
f"Testing following header with default IO class:\n{config_content}")
|
||||
fs_utils.write_file(ioclass_config_path, config_content)
|
||||
try_load_malformed_config(cache, config_io_classes,
|
||||
expected_err_msg=err_message)
|
||||
|
||||
|
||||
def try_load_malformed_config(cache, config_io_classes, expected_err_msg):
|
||||
try:
|
||||
cache.load_io_class(ioclass_config_path)
|
||||
TestRun.LOGGER.error("Open CAS accepts malformed configuration.")
|
||||
create_and_load_default_io_class_config(cache)
|
||||
except CmdException as e:
|
||||
TestRun.LOGGER.info(f"Open CAS did not load malformed config file as expected.")
|
||||
cli_messages.check_stderr_msg(e.output, expected_err_msg)
|
||||
output_io_classes = cache.list_io_classes()
|
||||
if not IoClass.compare_ioclass_lists(output_io_classes, config_io_classes):
|
||||
output_str = '\n'.join(str(i) for i in output_io_classes)
|
||||
TestRun.LOGGER.error(
|
||||
f"Loaded IO class config should be default but it is different:\n{output_str}")
|
||||
|
||||
|
||||
def create_and_load_default_io_class_config(cache):
|
||||
ioclass_config.create_ioclass_config(ioclass_config_path=ioclass_config_path)
|
||||
cache.load_io_class(ioclass_config_path)
|
||||
|
||||
|
||||
def setup_headers():
|
||||
default_header = IoClass.default_header_dict()
|
||||
correct_id_header = default_header['id']
|
||||
correct_name_header = default_header['name']
|
||||
correct_eviction_priority_header = default_header['eviction_prio']
|
||||
correct_allocation_header = default_header['allocation']
|
||||
|
||||
malformed_io_class_id_header = f"{malformed_io_class_id}," \
|
||||
f"{correct_name_header}," \
|
||||
f"{correct_eviction_priority_header}," \
|
||||
f"{correct_allocation_header}"
|
||||
malformed_io_class_name_header = f"{correct_id_header}," \
|
||||
f"{malformed_io_class_name}," \
|
||||
f"{correct_eviction_priority_header}," \
|
||||
f"{correct_allocation_header}"
|
||||
malformed_eviction_priority_header = f"{correct_id_header}," \
|
||||
f"{correct_name_header}," \
|
||||
f"{malformed_io_class_eviction_priority}," \
|
||||
f"{correct_allocation_header}"
|
||||
malformed_allocation_header = f"{correct_id_header}," \
|
||||
f"{correct_name_header}," \
|
||||
f"{correct_eviction_priority_header}," \
|
||||
f"{malformed_io_class_allocation}"
|
||||
|
||||
return {
|
||||
malformed_io_class_id_header: [m.replace("value_template", malformed_io_class_id)
|
||||
for m in cli_messages.malformed_io_class_header],
|
||||
malformed_io_class_name_header: [m.replace("value_template", malformed_io_class_name)
|
||||
for m in cli_messages.malformed_io_class_header],
|
||||
malformed_eviction_priority_header: [m.replace("value_template",
|
||||
malformed_io_class_eviction_priority)
|
||||
for m in cli_messages.malformed_io_class_header],
|
||||
malformed_allocation_header: [m.replace("value_template", malformed_io_class_allocation)
|
||||
for m in cli_messages.malformed_io_class_header]
|
||||
}
|
Loading…
Reference in New Issue
Block a user