Add IO class test for loading wrong IO class configuration

Signed-off-by: Katarzyna Lapinska <katarzyna.lapinska@intel.com>
This commit is contained in:
Katarzyna Lapinska 2022-04-29 09:54:02 +02:00
parent 83224dd81b
commit a495181adb
3 changed files with 266 additions and 3 deletions

View File

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

View File

@ -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)
@ -94,10 +107,10 @@ class IoClass:
@staticmethod
def generate_random_ioclass_list(count: int, max_priority: int = MAX_IO_CLASS_PRIORITY):
random_list = [IoClass.default(priority=random.randint(0, max_priority),
allocation=f"{random.randint(0,100)/100:0.2f}")]
allocation=f"{random.randint(0, 100) / 100:0.2f}")]
for i in range(1, count):
random_list.append(IoClass(i, priority=random.randint(0, max_priority),
allocation=f"{random.randint(0,100)/100:0.2f}")
allocation=f"{random.randint(0, 100) / 100:0.2f}")
.set_random_rule())
return random_list

View File

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