Add tests for opencas.py
Signed-off-by: Jan Musial <jan.musial@intel.com>
This commit is contained in:
parent
d773a81fa7
commit
aa0f3c078a
3
.gitignore
vendored
3
.gitignore
vendored
@ -13,3 +13,6 @@ Module.symvers
|
|||||||
Module.markers
|
Module.markers
|
||||||
*.mod.c
|
*.mod.c
|
||||||
modules.order
|
modules.order
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
0
test/utils_tests/opencas-py-tests/__init__.py
Normal file
0
test/utils_tests/opencas-py-tests/__init__.py
Normal file
15
test/utils_tests/opencas-py-tests/conftest.py
Normal file
15
test/utils_tests/opencas-py-tests/conftest.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2012-2019 Intel Corporation
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_configure(config):
|
||||||
|
try:
|
||||||
|
import helpers
|
||||||
|
except ImportError:
|
||||||
|
raise Exception("Couldn't import helpers")
|
||||||
|
|
||||||
|
sys.path.append(helpers.find_repo_root() + "/utils")
|
122
test/utils_tests/opencas-py-tests/helpers.py
Normal file
122
test/utils_tests/opencas-py-tests/helpers.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2012-2019 Intel Corporation
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
#
|
||||||
|
|
||||||
|
import mock
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
from io import StringIO
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
|
||||||
|
def find_repo_root():
|
||||||
|
path = os.getcwd()
|
||||||
|
|
||||||
|
while os.path.realpath(path) != "/":
|
||||||
|
if ".git" in os.listdir(path):
|
||||||
|
return path
|
||||||
|
|
||||||
|
path = os.path.dirname(path)
|
||||||
|
|
||||||
|
raise Exception(
|
||||||
|
"Couldn't find repository root - unable to locate opencas.py"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_process_mock(return_value, stdout, stderr):
|
||||||
|
process_mock = mock.Mock()
|
||||||
|
attrs = {
|
||||||
|
"wait.return_value": return_value,
|
||||||
|
"communicate.return_value": (stdout, stderr),
|
||||||
|
}
|
||||||
|
process_mock.configure_mock(**attrs)
|
||||||
|
|
||||||
|
return process_mock
|
||||||
|
|
||||||
|
|
||||||
|
def get_mock_os_exists(existing_files):
|
||||||
|
return lambda x: x in existing_files
|
||||||
|
|
||||||
|
|
||||||
|
def get_hashed_config_list(conf):
|
||||||
|
"""
|
||||||
|
Convert list of config lines to list of config lines hashes,
|
||||||
|
drop empty lines
|
||||||
|
"""
|
||||||
|
hashed_conf = [get_conf_line_hash(x) for x in conf]
|
||||||
|
|
||||||
|
return [x for x in hashed_conf if x]
|
||||||
|
|
||||||
|
|
||||||
|
def get_conf_line_hash(line):
|
||||||
|
"""
|
||||||
|
Removes whitespace, lowercases, comments and sorts cache params if present.
|
||||||
|
Returns empty line for comment-only lines
|
||||||
|
|
||||||
|
We don't care about order of params and kinds of whitespace in config lines
|
||||||
|
so normalize it to compare. We do care about case in paths, but to simplify
|
||||||
|
testing we pretend we don't.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def sort_cache_params(params):
|
||||||
|
return ",".join(sorted(params.split(",")))
|
||||||
|
|
||||||
|
line = line.split("#")[0]
|
||||||
|
|
||||||
|
cache_params_pattern = re.compile(r"(.*?\s)(\S+=\S+)")
|
||||||
|
match = cache_params_pattern.search(line)
|
||||||
|
if match:
|
||||||
|
sorted_params = sort_cache_params(match.group(2))
|
||||||
|
line = match.group(1) + sorted_params
|
||||||
|
|
||||||
|
return "".join(line.lower().split())
|
||||||
|
|
||||||
|
|
||||||
|
class MockConfigFile(object):
|
||||||
|
def __init__(self, buffer=""):
|
||||||
|
self.set_contents(buffer)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self.buffer
|
||||||
|
|
||||||
|
def __exit__(self, *args, **kwargs):
|
||||||
|
self.set_contents(self.buffer.getvalue())
|
||||||
|
|
||||||
|
def __call__(self, path, mode):
|
||||||
|
if mode == "w":
|
||||||
|
self.buffer = StringIO()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return self.buffer.read()
|
||||||
|
|
||||||
|
def write(self, str):
|
||||||
|
return self.buffer.write(str)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.set_contents(self.buffer.getvalue())
|
||||||
|
|
||||||
|
def readline(self):
|
||||||
|
return self.buffer.readline()
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
return self.buffer.__next__()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_contents(self, buffer):
|
||||||
|
self.buffer = StringIO(dedent(buffer).strip())
|
||||||
|
|
||||||
|
|
||||||
|
class CopyableMock(mock.Mock):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CopyableMock, self).__init__(*args, **kwargs)
|
||||||
|
self.copies = []
|
||||||
|
|
||||||
|
def __deepcopy__(self, memo):
|
||||||
|
copy = mock.Mock(spec=self)
|
||||||
|
self.copies += [copy]
|
||||||
|
return copy
|
489
test/utils_tests/opencas-py-tests/test_cas_config_01.py
Normal file
489
test/utils_tests/opencas-py-tests/test_cas_config_01.py
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2012-2019 Intel Corporation
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
#
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from mock import patch, mock_open
|
||||||
|
from textwrap import dedent
|
||||||
|
import helpers as h
|
||||||
|
|
||||||
|
import opencas
|
||||||
|
|
||||||
|
|
||||||
|
@patch("builtins.open", new_callable=mock_open)
|
||||||
|
def test_cas_config_from_file_exception(mock_file):
|
||||||
|
mock_file.raises = ValueError()
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
opencas.cas_config.from_file("/dummy/file.conf")
|
||||||
|
|
||||||
|
mock_file.assert_called_once_with("/dummy/file.conf", "r")
|
||||||
|
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
"builtins.open",
|
||||||
|
new_callable=h.MockConfigFile,
|
||||||
|
buffer="""
|
||||||
|
[caches]
|
||||||
|
1 /dev/nvme0n1 WT
|
||||||
|
[cores]
|
||||||
|
1 1 /dev/sdc
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
def test_cas_config_from_file_no_vertag(mock_file):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.from_file("/dummy/file.conf")
|
||||||
|
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
"builtins.open",
|
||||||
|
new_callable=h.MockConfigFile,
|
||||||
|
buffer="""
|
||||||
|
version=03.08.00
|
||||||
|
#[caches]
|
||||||
|
#1 /dev/nvme0n1 WT
|
||||||
|
#[cores]
|
||||||
|
#1 1 /dev/sdc
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
@patch("opencas.cas_config.core_config.from_line")
|
||||||
|
@patch("opencas.cas_config.cache_config.from_line")
|
||||||
|
@patch("opencas.cas_config.insert_core")
|
||||||
|
@patch("opencas.cas_config.insert_cache")
|
||||||
|
def test_cas_config_from_file_comments_only(
|
||||||
|
mock_insert_cache,
|
||||||
|
mock_insert_core,
|
||||||
|
mock_cache_from_line,
|
||||||
|
mock_core_from_line,
|
||||||
|
mock_file,
|
||||||
|
):
|
||||||
|
|
||||||
|
config = opencas.cas_config.from_file("/dummy/file.conf")
|
||||||
|
|
||||||
|
mock_cache_from_line.assert_not_called()
|
||||||
|
mock_core_from_line.assert_not_called()
|
||||||
|
mock_insert_cache.assert_not_called()
|
||||||
|
mock_insert_core.assert_not_called()
|
||||||
|
|
||||||
|
assert config.is_empty()
|
||||||
|
|
||||||
|
|
||||||
|
ConflictingConfigException = opencas.cas_config.ConflictingConfigException
|
||||||
|
AlreadyConfiguredException = opencas.cas_config.AlreadyConfiguredException
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"caches_config,cores_config,exception",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"1 /dev/dummy0n1 WT",
|
||||||
|
"2 /dev/dummy0n1 WT",
|
||||||
|
"3 /dev/dummy0n1 WT",
|
||||||
|
],
|
||||||
|
["1 1 /dev/dummyc"],
|
||||||
|
ConflictingConfigException,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["1 /dev/dummyc WT"],
|
||||||
|
["1 1 /dev/dummyc"],
|
||||||
|
ConflictingConfigException,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["1 /dev/dummya WT", "1 /dev/dummy0n1 WT"],
|
||||||
|
["1 1 /dev/dummyc"],
|
||||||
|
ConflictingConfigException,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["1 /dev/dummya WT", "1 /dev/dummya WT"],
|
||||||
|
["1 1 /dev/dummyc"],
|
||||||
|
AlreadyConfiguredException,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["1 /dev/dummya WT"],
|
||||||
|
["1 1 /dev/dummyc", "1 1 /dev/dummyc"],
|
||||||
|
AlreadyConfiguredException,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["2 /dev/dummya WT"],
|
||||||
|
["1 1 /dev/dummyc", "2 1 /dev/dummyb"],
|
||||||
|
KeyError,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["1 /dev/dummya WT", "2 /dev/dummy0n1 WT"],
|
||||||
|
["1 1 /dev/dummyc", "2 1 /dev/dummyc"],
|
||||||
|
ConflictingConfigException,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@patch("builtins.open", new_callable=h.MockConfigFile)
|
||||||
|
@patch("opencas.cas_config.cache_config.validate_config")
|
||||||
|
@patch("opencas.cas_config.core_config.validate_config")
|
||||||
|
def test_cas_config_from_file_inconsistent_configs(
|
||||||
|
mock_validate_core,
|
||||||
|
mock_validate_cache,
|
||||||
|
mock_file,
|
||||||
|
caches_config,
|
||||||
|
cores_config,
|
||||||
|
exception,
|
||||||
|
):
|
||||||
|
|
||||||
|
mock_file.set_contents(
|
||||||
|
dedent(
|
||||||
|
"""
|
||||||
|
version=3.8.0
|
||||||
|
[caches]
|
||||||
|
{0}
|
||||||
|
[cores]
|
||||||
|
{1}
|
||||||
|
"""
|
||||||
|
).format("\n".join(caches_config), "\n".join(cores_config))
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(exception):
|
||||||
|
opencas.cas_config.from_file("/dummy/file.conf")
|
||||||
|
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
"builtins.open",
|
||||||
|
new_callable=h.MockConfigFile,
|
||||||
|
buffer="""
|
||||||
|
version=3.8.0
|
||||||
|
[caches]
|
||||||
|
1 /dev/dummy0n1 WT
|
||||||
|
[cores]
|
||||||
|
1 1 /dev/dummyc
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
@patch("opencas.cas_config.cache_config.validate_config")
|
||||||
|
@patch("opencas.cas_config.core_config.validate_config")
|
||||||
|
def test_cas_config_is_empty_non_empty(
|
||||||
|
mock_validate_core, mock_validate_cache, mock_file
|
||||||
|
):
|
||||||
|
|
||||||
|
config = opencas.cas_config.from_file("/dummy/file.conf")
|
||||||
|
|
||||||
|
assert not config.is_empty()
|
||||||
|
|
||||||
|
|
||||||
|
def test_cas_config_double_add_cache():
|
||||||
|
config = opencas.cas_config()
|
||||||
|
|
||||||
|
cache = opencas.cas_config.cache_config(1, "/dev/dummy", "WT")
|
||||||
|
config.insert_cache(cache)
|
||||||
|
|
||||||
|
with pytest.raises(AlreadyConfiguredException):
|
||||||
|
config.insert_cache(cache)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cas_config_double_add_core():
|
||||||
|
config = opencas.cas_config()
|
||||||
|
cache = opencas.cas_config.cache_config(1, "/dev/dummy1", "WT")
|
||||||
|
config.insert_cache(cache)
|
||||||
|
|
||||||
|
core = opencas.cas_config.core_config(1, 1, "/dev/dummy")
|
||||||
|
config.insert_core(core)
|
||||||
|
|
||||||
|
with pytest.raises(AlreadyConfiguredException):
|
||||||
|
config.insert_core(core)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cas_config_insert_core_no_cache():
|
||||||
|
config = opencas.cas_config()
|
||||||
|
|
||||||
|
core = opencas.cas_config.core_config(1, 1, "/dev/dummy")
|
||||||
|
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
config.insert_core(core)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("os.path.realpath")
|
||||||
|
def test_cas_config_add_same_cache_symlinked_01(mock_realpath):
|
||||||
|
mock_realpath.side_effect = (
|
||||||
|
lambda x: "/dev/dummy1" if x == "/dev/dummy_link" else x
|
||||||
|
)
|
||||||
|
|
||||||
|
config = opencas.cas_config()
|
||||||
|
cache = opencas.cas_config.cache_config(1, "/dev/dummy1", "WT")
|
||||||
|
config.insert_cache(cache)
|
||||||
|
|
||||||
|
cache_symlinked = opencas.cas_config.cache_config(
|
||||||
|
2, "/dev/dummy_link", "WB"
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ConflictingConfigException):
|
||||||
|
config.insert_cache(cache_symlinked)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("os.path.realpath")
|
||||||
|
def test_cas_config_add_same_cache_symlinked_02(mock_realpath):
|
||||||
|
mock_realpath.side_effect = (
|
||||||
|
lambda x: "/dev/dummy1" if x == "/dev/dummy_link" else x
|
||||||
|
)
|
||||||
|
|
||||||
|
config = opencas.cas_config()
|
||||||
|
cache = opencas.cas_config.cache_config(1, "/dev/dummy1", "WT")
|
||||||
|
config.insert_cache(cache)
|
||||||
|
|
||||||
|
cache_symlinked = opencas.cas_config.cache_config(
|
||||||
|
1, "/dev/dummy_link", "WB"
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(AlreadyConfiguredException):
|
||||||
|
config.insert_cache(cache_symlinked)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("os.path.realpath")
|
||||||
|
def test_cas_config_add_same_core_symlinked_01(mock_realpath):
|
||||||
|
mock_realpath.side_effect = (
|
||||||
|
lambda x: "/dev/dummy1" if x == "/dev/dummy_link" else x
|
||||||
|
)
|
||||||
|
|
||||||
|
config = opencas.cas_config()
|
||||||
|
config.insert_cache(
|
||||||
|
opencas.cas_config.cache_config(1, "/dev/dummy_cache", "WB")
|
||||||
|
)
|
||||||
|
core = opencas.cas_config.core_config(1, 1, "/dev/dummy1")
|
||||||
|
config.insert_core(core)
|
||||||
|
|
||||||
|
core_symlinked = opencas.cas_config.core_config(1, 2, "/dev/dummy_link")
|
||||||
|
|
||||||
|
with pytest.raises(ConflictingConfigException):
|
||||||
|
config.insert_core(core_symlinked)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("os.path.realpath")
|
||||||
|
def test_cas_config_add_same_core_symlinked_02(mock_realpath):
|
||||||
|
mock_realpath.side_effect = (
|
||||||
|
lambda x: "/dev/dummy1" if x == "/dev/dummy_link" else x
|
||||||
|
)
|
||||||
|
|
||||||
|
config = opencas.cas_config()
|
||||||
|
config.insert_cache(
|
||||||
|
opencas.cas_config.cache_config(1, "/dev/dummy_cache", "WB")
|
||||||
|
)
|
||||||
|
core = opencas.cas_config.core_config(1, 1, "/dev/dummy1")
|
||||||
|
config.insert_core(core)
|
||||||
|
|
||||||
|
core_symlinked = opencas.cas_config.core_config(1, 1, "/dev/dummy_link")
|
||||||
|
|
||||||
|
with pytest.raises(AlreadyConfiguredException):
|
||||||
|
config.insert_core(core_symlinked)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("os.path.realpath")
|
||||||
|
@patch("os.listdir")
|
||||||
|
def test_cas_config_get_by_id_path(mock_listdir, mock_realpath):
|
||||||
|
mock_listdir.return_value = [
|
||||||
|
"wwn-1337deadbeef-x0x0",
|
||||||
|
"wwn-1337deadbeef-x0x0-part1",
|
||||||
|
"nvme-INTEL_SSDAAAABBBBBCCC_0984547ASDDJHHHFH",
|
||||||
|
]
|
||||||
|
mock_realpath.side_effect = (
|
||||||
|
lambda x: "/dev/dummy1"
|
||||||
|
if x == "/dev/disk/by-id/wwn-1337deadbeef-x0x0-part1"
|
||||||
|
else x
|
||||||
|
)
|
||||||
|
|
||||||
|
path = opencas.cas_config.get_by_id_path("/dev/dummy1")
|
||||||
|
|
||||||
|
assert path == "/dev/disk/by-id/wwn-1337deadbeef-x0x0-part1"
|
||||||
|
|
||||||
|
|
||||||
|
@patch("os.path.realpath")
|
||||||
|
@patch("os.listdir")
|
||||||
|
def test_cas_config_get_by_id_path_not_found(mock_listdir, mock_realpath):
|
||||||
|
mock_listdir.return_value = [
|
||||||
|
"wwn-1337deadbeef-x0x0",
|
||||||
|
"wwn-1337deadbeef-x0x0-part1",
|
||||||
|
"nvme-INTEL_SSDAAAABBBBBCCC_0984547ASDDJHHHFH",
|
||||||
|
]
|
||||||
|
mock_realpath.side_effect = lambda x: x
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
path = opencas.cas_config.get_by_id_path("/dev/dummy1")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"caches_config,cores_config",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"1 /dev/dummy0n1 WT",
|
||||||
|
"2 /dev/dummy0n2 WT",
|
||||||
|
"3 /dev/dummy0n3 WT",
|
||||||
|
],
|
||||||
|
["1 1 /dev/dummyc"],
|
||||||
|
),
|
||||||
|
([], []),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"1 /dev/dummy0n1 WT",
|
||||||
|
"2 /dev/dummy0n2 WT",
|
||||||
|
"3 /dev/dummy0n3 WT",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"1 1 /dev/dummyc1",
|
||||||
|
"2 200 /dev/dummyc2",
|
||||||
|
"3 100 /dev/dummyc3",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"1 /dev/dummy0n1 WT cleaning_policy=acp",
|
||||||
|
"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"),
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@patch("builtins.open", new_callable=h.MockConfigFile)
|
||||||
|
@patch("opencas.cas_config.cache_config.validate_config")
|
||||||
|
@patch("opencas.cas_config.core_config.validate_config")
|
||||||
|
def test_cas_config_from_file_to_file(
|
||||||
|
mock_validate_core,
|
||||||
|
mock_validate_cache,
|
||||||
|
mock_file,
|
||||||
|
caches_config,
|
||||||
|
cores_config,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
1. Read config from mocked file with parametrized caches and cores section
|
||||||
|
2. Serialize config back to mocked file
|
||||||
|
3. Check if serialized file is proper config file and the same content-wise
|
||||||
|
as the initial file. Specifically check:
|
||||||
|
* Version tag is present in first line
|
||||||
|
* There is only one of each [caches] and [cores] sections marking
|
||||||
|
* [cores] section comes after [caches]
|
||||||
|
* sets of caches and cores are equal before and after test
|
||||||
|
"""
|
||||||
|
|
||||||
|
mock_file.set_contents(
|
||||||
|
dedent(
|
||||||
|
"""
|
||||||
|
version=3.8.0
|
||||||
|
[caches]
|
||||||
|
{0}
|
||||||
|
[cores]
|
||||||
|
{1}
|
||||||
|
"""
|
||||||
|
).format("\n".join(caches_config), "\n".join(cores_config))
|
||||||
|
)
|
||||||
|
|
||||||
|
config = opencas.cas_config.from_file("/dummy/file.conf")
|
||||||
|
|
||||||
|
config.write("/dummy/file.conf")
|
||||||
|
|
||||||
|
f = mock_file("/dummy/file.conf", "r")
|
||||||
|
contents_hashed = h.get_hashed_config_list(f)
|
||||||
|
|
||||||
|
assert contents_hashed[0] == "version=3.8.0"
|
||||||
|
assert contents_hashed.count("[caches]") == 1
|
||||||
|
assert contents_hashed.count("[cores]") == 1
|
||||||
|
|
||||||
|
caches_index = contents_hashed.index("[caches]")
|
||||||
|
cores_index = contents_hashed.index("[cores]")
|
||||||
|
|
||||||
|
assert cores_index > caches_index
|
||||||
|
|
||||||
|
caches_hashed = h.get_hashed_config_list(caches_config)
|
||||||
|
cores_hashed = h.get_hashed_config_list(cores_config)
|
||||||
|
|
||||||
|
assert set(caches_hashed) == set(
|
||||||
|
contents_hashed[caches_index + 1 : cores_index]
|
||||||
|
)
|
||||||
|
assert set(cores_hashed) == set(contents_hashed[cores_index + 1 :])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"caches_config,cores_config",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"1 /dev/dummy0n1 WT",
|
||||||
|
"2 /dev/dummy0n2 WT",
|
||||||
|
"3 /dev/dummy0n3 WT",
|
||||||
|
],
|
||||||
|
["1 1 /dev/dummyc"],
|
||||||
|
),
|
||||||
|
([], []),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"1 /dev/dummy0n1 WT",
|
||||||
|
"2 /dev/dummy0n2 WT",
|
||||||
|
"3 /dev/dummy0n3 WT",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"1 1 /dev/dummyc1",
|
||||||
|
"2 200 /dev/dummyc2",
|
||||||
|
"3 100 /dev/dummyc3",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"1 /dev/dummy0n1 WT cleaning_policy=acp",
|
||||||
|
"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"),
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@patch("builtins.open", new_callable=h.MockConfigFile)
|
||||||
|
@patch("opencas.cas_config.cache_config.validate_config")
|
||||||
|
@patch("opencas.cas_config.core_config.validate_config")
|
||||||
|
def test_cas_config_from_file_insert_cache_insert_core_to_file(
|
||||||
|
mock_validate_core,
|
||||||
|
mock_validate_cache,
|
||||||
|
mock_file,
|
||||||
|
caches_config,
|
||||||
|
cores_config,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
1. Read config from mocked file with parametrized caches and cores section
|
||||||
|
2. Add one core and one cache to config
|
||||||
|
3. Serialize config back to mocked file
|
||||||
|
4. Compare that config file after serialization is same content-wise with
|
||||||
|
initial + added core and cache
|
||||||
|
"""
|
||||||
|
|
||||||
|
mock_file.set_contents(
|
||||||
|
dedent(
|
||||||
|
"""
|
||||||
|
version=3.8.0
|
||||||
|
[caches]
|
||||||
|
{0}
|
||||||
|
[cores]
|
||||||
|
{1}
|
||||||
|
"""
|
||||||
|
).format("\n".join(caches_config), "\n".join(cores_config))
|
||||||
|
)
|
||||||
|
|
||||||
|
config = opencas.cas_config.from_file("/dummy/file.conf")
|
||||||
|
|
||||||
|
config.insert_cache(opencas.cas_config.cache_config(5, "/dev/mango", "WT"))
|
||||||
|
config.insert_core(opencas.cas_config.core_config(5, 1, "/dev/mango_core"))
|
||||||
|
|
||||||
|
config.write("/dummy/file.conf")
|
||||||
|
|
||||||
|
f = mock_file("/dummy/file.conf", "r")
|
||||||
|
contents_hashed = h.get_hashed_config_list(f)
|
||||||
|
|
||||||
|
caches_index = contents_hashed.index("[caches]")
|
||||||
|
cores_index = contents_hashed.index("[cores]")
|
||||||
|
|
||||||
|
caches_hashed = h.get_hashed_config_list(caches_config)
|
||||||
|
cores_hashed = h.get_hashed_config_list(cores_config)
|
||||||
|
|
||||||
|
assert set(contents_hashed[caches_index + 1 : cores_index]) - set(
|
||||||
|
caches_hashed
|
||||||
|
) == set(["5/dev/mangowt"])
|
||||||
|
assert set(contents_hashed[cores_index + 1 :]) - set(cores_hashed) == set(
|
||||||
|
["51/dev/mango_core"]
|
||||||
|
)
|
405
test/utils_tests/opencas-py-tests/test_cas_config_cache_01.py
Normal file
405
test/utils_tests/opencas-py-tests/test_cas_config_cache_01.py
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2012-2019 Intel Corporation
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
#
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import mock
|
||||||
|
import stat
|
||||||
|
|
||||||
|
import helpers as h
|
||||||
|
import opencas
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"line",
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
" ",
|
||||||
|
"#",
|
||||||
|
" # ",
|
||||||
|
("TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2Npbmcg"
|
||||||
|
"ZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlI"
|
||||||
|
"GV0IGRvbG9yZSBtYWduYSBhbGlxdWEu"),
|
||||||
|
" # ? } { ! ",
|
||||||
|
"1 /dev/nvme0n1 WT 1 2 3",
|
||||||
|
"1 /dev/nvme0n1 WT ioclass_file=ioclass.csv ,cache_line_size=4",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("opencas.cas_config.cache_config.validate_config")
|
||||||
|
def test_cache_config_from_line_parsing_checks_01(mock_validate, line):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.cache_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"line",
|
||||||
|
[
|
||||||
|
"1 /dev/nvme0n1 WT",
|
||||||
|
"1 /dev/nvme0n1 WT ioclass_file=ioclass.csv,cache_line_size=4",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("opencas.cas_config.cache_config.validate_config")
|
||||||
|
def test_cache_config_from_line_parsing_checks_02(mock_validate, line):
|
||||||
|
opencas.cas_config.cache_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
def test_cache_config_from_line_device_is_directory(
|
||||||
|
mock_stat, mock_path_exists
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(
|
||||||
|
["/home/user/catpictures"]
|
||||||
|
)
|
||||||
|
mock_stat.return_value = mock.Mock(st_mode=stat.S_IFDIR)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.cache_config.from_line(
|
||||||
|
"1 /home/user/catpictures WT"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
def test_cache_config_from_line_device_not_present(
|
||||||
|
mock_stat, mock_path_exists
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists([])
|
||||||
|
mock_stat.side_effect = OSError()
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.cache_config.from_line("1 /dev/nvme0n1 WT")
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
@mock.patch("subprocess.Popen")
|
||||||
|
def test_cache_config_from_line_device_with_partitions(
|
||||||
|
mock_popen, mock_stat, mock_path_exists
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(["/dev/sda"])
|
||||||
|
mock_stat.return_value = mock.Mock(st_mode=stat.S_IFBLK)
|
||||||
|
mock_popen.return_value = h.get_process_mock(0, "sda\nsda1\nsda2", "")
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.cache_config.from_line("1 /dev/sda WT")
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
@mock.patch("subprocess.Popen")
|
||||||
|
def test_cache_config_validate_device_with_partitions(
|
||||||
|
mock_popen, mock_stat, mock_path_exists
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(["/dev/sda"])
|
||||||
|
mock_stat.return_value = mock.Mock(st_mode=stat.S_IFBLK)
|
||||||
|
mock_popen.return_value = h.get_process_mock(0, "sda\nsda1\nsda2", "")
|
||||||
|
|
||||||
|
cache = opencas.cas_config.cache_config(
|
||||||
|
cache_id="1", device="/dev/sda", cache_mode="WT", params=dict()
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
cache.validate_config(False)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
@mock.patch("subprocess.Popen")
|
||||||
|
def test_cache_config_validate_force_device_with_partitions(
|
||||||
|
mock_popen, mock_stat, mock_path_exists
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(["/dev/sda"])
|
||||||
|
mock_stat.return_value = mock.Mock(st_mode=stat.S_IFBLK)
|
||||||
|
mock_popen.return_value = h.get_process_mock(0, "sda\nsda1\nsda2", "")
|
||||||
|
|
||||||
|
cache = opencas.cas_config.cache_config(
|
||||||
|
cache_id="1", device="/dev/sda", cache_mode="WT", params=dict()
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
cache.validate_config(True)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
@mock.patch("subprocess.Popen")
|
||||||
|
def test_cache_config_from_line_device_without_partitions(
|
||||||
|
mock_popen, mock_stat, mock_path_exists
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(["/dev/sda"])
|
||||||
|
mock_stat.return_value = mock.Mock(st_mode=stat.S_IFBLK)
|
||||||
|
mock_popen.return_value = h.get_process_mock(0, "sda\n", "")
|
||||||
|
|
||||||
|
opencas.cas_config.cache_config.from_line("1 /dev/sda WT")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("device", ["/dev/cas1-1", "/dev/cas1-300"])
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
def test_cache_config_from_line_recursive_multilevel(
|
||||||
|
mock_stat, mock_path_exists, device
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists([])
|
||||||
|
mock_stat.raises = OSError()
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.cache_config.from_line(
|
||||||
|
"1 {0} WT".format(device)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
def test_cache_config_from_line_multilevel(mock_stat, mock_path_exists):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists([])
|
||||||
|
mock_stat.raises = OSError()
|
||||||
|
|
||||||
|
opencas.cas_config.cache_config.from_line("2 /dev/cas1-1 WT")
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_cache_config_from_line_allow_incomplete(mock_check_block,):
|
||||||
|
opencas.cas_config.cache_config.from_line(
|
||||||
|
"1 /dev/sda WT", allow_incomplete=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not mock_check_block.called
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_cache_config_from_line_missing_ioclass_file(
|
||||||
|
mock_check_block, mock_path_exists
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(["/dev/nvme0n1"])
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.cache_config.from_line(
|
||||||
|
("11 /dev/nvme0n1 WT ioclass_file=ioclass.csv,"
|
||||||
|
"cleaning_policy=nop,cache_line_size=4")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"params",
|
||||||
|
[
|
||||||
|
"ioclass_file=",
|
||||||
|
"ioclass_file=asdf",
|
||||||
|
"ioclass_file=ioclass.csv,ioclass_file=ioclass.csv",
|
||||||
|
"cleaning_policy=nop,cleaning_policy=acp",
|
||||||
|
"cleaning_policy=",
|
||||||
|
"cleaning_policy=INVALID",
|
||||||
|
"ioclass_file=ioclass.csv, cleaning_policy=nop",
|
||||||
|
"cache_line_size=4,cache_line_size=8",
|
||||||
|
"cache_line_size=",
|
||||||
|
"cache_line_size=0",
|
||||||
|
"cache_line_size=4k",
|
||||||
|
"cache_line_size=4kb",
|
||||||
|
"cache_line_size=256",
|
||||||
|
"cache_line_size=-1",
|
||||||
|
"cache_line_size=four",
|
||||||
|
"cache_line_size=128",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("opencas.cas_config.cache_config.check_cache_device_empty")
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_cache_config_from_line_parameter_validation_01(
|
||||||
|
mock_check_block, mock_device_empty, mock_path_exists, params
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(
|
||||||
|
["/dev/sda", "ioclass.csv"]
|
||||||
|
)
|
||||||
|
|
||||||
|
line = "1 /dev/sda WT {0}".format(params)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.cache_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"params",
|
||||||
|
[
|
||||||
|
"ioclass_file=ioclass.csv",
|
||||||
|
"cleaning_policy=acp",
|
||||||
|
"cleaning_policy=nop",
|
||||||
|
"cleaning_policy=alru",
|
||||||
|
"cleaning_policy=AlRu",
|
||||||
|
"ioclass_file=ioclass.csv,cleaning_policy=nop",
|
||||||
|
"cache_line_size=4",
|
||||||
|
"cache_line_size=8",
|
||||||
|
"cache_line_size=16",
|
||||||
|
"cache_line_size=32",
|
||||||
|
"cache_line_size=64",
|
||||||
|
"cache_line_size=4,cleaning_policy=nop",
|
||||||
|
"ioclass_file=ioclass.csv,cache_line_size=4,cleaning_policy=nop",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("opencas.cas_config.cache_config.check_cache_device_empty")
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_cache_config_from_line_parameter_validation_02(
|
||||||
|
mock_check_block, mock_device_empty, mock_path_exists, params
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(
|
||||||
|
["/dev/sda", "ioclass.csv"]
|
||||||
|
)
|
||||||
|
|
||||||
|
line = "1 /dev/sda WT {0}".format(params)
|
||||||
|
|
||||||
|
opencas.cas_config.cache_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"mode",
|
||||||
|
[
|
||||||
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
"ioclass_file=ioclass.csv,cache_line_size=4,cleaning_policy=nop",
|
||||||
|
" ",
|
||||||
|
" $$# ",
|
||||||
|
"PT$$# ",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("opencas.cas_config.cache_config.check_cache_device_empty")
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_cache_config_from_line_cache_mode_validation_01(
|
||||||
|
mock_check_block, mock_device_empty, mock_path_exists, mode
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(
|
||||||
|
["/dev/sda", "ioclass.csv"]
|
||||||
|
)
|
||||||
|
|
||||||
|
line = "1 /dev/sda {0}".format(mode)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.cache_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"mode", ["wt", "WT", "pt", "PT", "wb", "WB", "wa", "WA", "wA", "Wa"]
|
||||||
|
)
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("opencas.cas_config.cache_config.check_cache_device_empty")
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_cache_config_from_line_cache_mode_validation_02(
|
||||||
|
mock_check_block, mock_device_empty, mock_path_exists, mode
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(
|
||||||
|
["/dev/sda", "ioclass.csv"]
|
||||||
|
)
|
||||||
|
|
||||||
|
line = "1 /dev/sda {0}".format(mode)
|
||||||
|
|
||||||
|
opencas.cas_config.cache_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cache_id",
|
||||||
|
[
|
||||||
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
"lizard",
|
||||||
|
"",
|
||||||
|
"#",
|
||||||
|
"-1",
|
||||||
|
"3.14",
|
||||||
|
"3,14",
|
||||||
|
"3 14",
|
||||||
|
"0",
|
||||||
|
"16385",
|
||||||
|
"99999999999",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("opencas.cas_config.cache_config.check_cache_device_empty")
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_cache_config_from_line_cache_id_validation_01(
|
||||||
|
mock_check_block, mock_device_empty, mock_path_exists, cache_id
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(
|
||||||
|
["/dev/sda", "ioclass.csv"]
|
||||||
|
)
|
||||||
|
|
||||||
|
line = "{0} /dev/sda WT".format(cache_id)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.cache_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("cache_id", ["1", "16384", "123"])
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("opencas.cas_config.cache_config.check_cache_device_empty")
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_cache_config_from_line_cache_id_validation_02(
|
||||||
|
mock_check_block, mock_device_empty, mock_path_exists, cache_id
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(
|
||||||
|
["/dev/sda", "ioclass.csv"]
|
||||||
|
)
|
||||||
|
|
||||||
|
line = "{0} /dev/sda WT".format(cache_id)
|
||||||
|
|
||||||
|
opencas.cas_config.cache_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"params",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"cache_id": "1",
|
||||||
|
"device": "/dev/nvme0n1",
|
||||||
|
"cache_mode": "WT",
|
||||||
|
"ioclass_file": "ioclass.csv",
|
||||||
|
"cleaning_policy": "acp",
|
||||||
|
"cache_line_size": "4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cache_id": "16384",
|
||||||
|
"device": "/dev/nvme0n1p1",
|
||||||
|
"cache_mode": "wb",
|
||||||
|
"ioclass_file": "ioclass.csv",
|
||||||
|
"cleaning_policy": "nop",
|
||||||
|
"cache_line_size": "64",
|
||||||
|
},
|
||||||
|
{"cache_id": "100", "device": "/dev/sda", "cache_mode": "wb"},
|
||||||
|
{
|
||||||
|
"cache_id": "2",
|
||||||
|
"device": "/dev/dm-1",
|
||||||
|
"cache_mode": "wb",
|
||||||
|
"cleaning_policy": "nop",
|
||||||
|
"cache_line_size": "64",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cache_id": "1",
|
||||||
|
"device": "/dev/nvme0n1",
|
||||||
|
"cache_mode": "WT",
|
||||||
|
"cache_line_size": "4",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("opencas.cas_config.cache_config.check_cache_device_empty")
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_cache_config_to_line_from_line(
|
||||||
|
mock_check_block, mock_device_empty, mock_path_exists, params
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(
|
||||||
|
[params["device"], "ioclass.csv"]
|
||||||
|
)
|
||||||
|
|
||||||
|
cache_reference = opencas.cas_config.cache_config(**params)
|
||||||
|
|
||||||
|
cache_reference.validate_config(False)
|
||||||
|
|
||||||
|
cache_after = opencas.cas_config.cache_config.from_line(
|
||||||
|
cache_reference.to_line()
|
||||||
|
)
|
||||||
|
|
||||||
|
assert cache_after.cache_id == cache_reference.cache_id
|
||||||
|
assert cache_after.device == cache_reference.device
|
||||||
|
assert str.lower(cache_after.cache_mode) == str.lower(
|
||||||
|
cache_reference.cache_mode
|
||||||
|
)
|
||||||
|
assert cache_after.params == cache_reference.params
|
156
test/utils_tests/opencas-py-tests/test_cas_config_core_01.py
Normal file
156
test/utils_tests/opencas-py-tests/test_cas_config_core_01.py
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2012-2019 Intel Corporation
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
#
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import mock
|
||||||
|
import stat
|
||||||
|
|
||||||
|
import helpers as h
|
||||||
|
import opencas
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"line",
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
" ",
|
||||||
|
"#",
|
||||||
|
" # ",
|
||||||
|
("TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2Npbmcg"
|
||||||
|
"ZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlI"
|
||||||
|
"GV0IGRvbG9yZSBtYWduYSBhbGlxdWEu"),
|
||||||
|
" # ? } { ! ",
|
||||||
|
"1 1 /dev/sda /dev/sdb",
|
||||||
|
"1 2 1 /dev/sda ",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("opencas.cas_config.core_config.validate_config")
|
||||||
|
def test_core_config_from_line_parsing_checks_01(mock_validate, line):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.core_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("line", ["1 1 /dev/sda", "1 1 /dev/sda "])
|
||||||
|
@mock.patch("opencas.cas_config.core_config.validate_config")
|
||||||
|
def test_core_config_from_line_parsing_checks_02(mock_validate, line):
|
||||||
|
opencas.cas_config.core_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
def test_core_config_from_line_device_is_directory(
|
||||||
|
mock_stat, mock_path_exists
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(["/home/user/stuff"])
|
||||||
|
mock_stat.return_value = mock.Mock(st_mode=stat.S_IFDIR)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.core_config.from_line("1 1 /home/user/stuff")
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
def test_core_config_from_line_device_not_present(mock_stat, mock_path_exists):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists([])
|
||||||
|
mock_stat.side_effect = ValueError()
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.core_config.from_line("1 1 /dev/sda")
|
||||||
|
|
||||||
|
|
||||||
|
def test_core_config_from_line_recursive_multilevel():
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.core_config.from_line("1 1 /dev/cas1-1")
|
||||||
|
|
||||||
|
|
||||||
|
def test_core_config_from_line_multilevel():
|
||||||
|
opencas.cas_config.core_config.from_line("1 1 /dev/cas2-1")
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("opencas.cas_config.check_block_device")
|
||||||
|
def test_core_config_from_line_allow_incomplete(mock_check_block,):
|
||||||
|
opencas.cas_config.core_config.from_line(
|
||||||
|
"1 1 /dev/sda", allow_incomplete=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not mock_check_block.called
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cache_id,core_id",
|
||||||
|
[
|
||||||
|
("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "bbbbbbb"),
|
||||||
|
("lizard", "chicken"),
|
||||||
|
("0", "0"),
|
||||||
|
("0", "100"),
|
||||||
|
("0", "-1"),
|
||||||
|
("-1", "0"),
|
||||||
|
("-1", "1"),
|
||||||
|
("-1", "-1"),
|
||||||
|
("16385", "4095"),
|
||||||
|
("16384", "4096"),
|
||||||
|
("0", "0"),
|
||||||
|
("1", "-1"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
def test_core_config_from_line_cache_id_validation_01(
|
||||||
|
mock_stat, mock_path_exists, cache_id, core_id
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(["/dev/sda"])
|
||||||
|
mock_stat.return_value = mock.Mock(st_mode=stat.S_IFBLK)
|
||||||
|
|
||||||
|
line = "{0} {1} /dev/sda".format(cache_id, core_id)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
opencas.cas_config.core_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cache_id,core_id", [("16384", "4095"), ("1", "0"), ("1", "10")]
|
||||||
|
)
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
def test_core_config_from_line_cache_id_validation_02(
|
||||||
|
mock_stat, mock_path_exists, cache_id, core_id
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists(["/dev/sda"])
|
||||||
|
mock_stat.return_value = mock.Mock(st_mode=stat.S_IFBLK)
|
||||||
|
|
||||||
|
line = "{0} {1} /dev/sda".format(cache_id, core_id)
|
||||||
|
|
||||||
|
opencas.cas_config.core_config.from_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cache_id,core_id,device",
|
||||||
|
[
|
||||||
|
("1", "1", "/dev/sda"),
|
||||||
|
("16384", "4095", "/dev/sda1"),
|
||||||
|
("16384", "0", "/dev/nvme0n1p"),
|
||||||
|
("100", "5", "/dev/dm-10"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock.patch("os.path.exists")
|
||||||
|
@mock.patch("os.stat")
|
||||||
|
def test_core_config_from_line_cache_id_validation(
|
||||||
|
mock_stat, mock_path_exists, cache_id, core_id, device
|
||||||
|
):
|
||||||
|
mock_path_exists.side_effect = h.get_mock_os_exists([device])
|
||||||
|
mock_stat.return_value = mock.Mock(st_mode=stat.S_IFBLK)
|
||||||
|
|
||||||
|
core_reference = opencas.cas_config.core_config(
|
||||||
|
cache_id=cache_id, core_id=core_id, path=device
|
||||||
|
)
|
||||||
|
|
||||||
|
core_reference.validate_config()
|
||||||
|
|
||||||
|
core_after = opencas.cas_config.core_config.from_line(
|
||||||
|
core_reference.to_line()
|
||||||
|
)
|
||||||
|
assert core_after.cache_id == core_reference.cache_id
|
||||||
|
assert core_after.core_id == core_reference.core_id
|
||||||
|
assert core_after.device == core_reference.device
|
53
test/utils_tests/opencas-py-tests/test_casadm_01.py
Normal file
53
test/utils_tests/opencas-py-tests/test_casadm_01.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2012-2019 Intel Corporation
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
#
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import subprocess
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from opencas import casadm
|
||||||
|
from helpers import get_process_mock
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("subprocess.Popen")
|
||||||
|
def test_run_cmd_01(mock_popen):
|
||||||
|
mock_popen.return_value = get_process_mock(0, "successes", "errors")
|
||||||
|
result = casadm.run_cmd(["casadm", "-L"])
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert result.stdout == "successes"
|
||||||
|
assert result.stderr == "errors"
|
||||||
|
mock_popen.assert_called_once_with(
|
||||||
|
["casadm", "-L"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("subprocess.Popen")
|
||||||
|
def test_run_cmd_02(mock_popen):
|
||||||
|
mock_popen.return_value = get_process_mock(4, "successes", "errors")
|
||||||
|
with pytest.raises(casadm.CasadmError):
|
||||||
|
casadm.run_cmd(["casadm", "-L"])
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("subprocess.Popen")
|
||||||
|
def test_get_version_01(mock_popen):
|
||||||
|
mock_popen.return_value = get_process_mock(0, "0.0.1", "errors")
|
||||||
|
result = casadm.get_version()
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert result.stdout == "0.0.1"
|
||||||
|
assert result.stderr == "errors"
|
||||||
|
mock_popen.assert_called_once_with(
|
||||||
|
[casadm.casadm_path, "--version", "--output-format", "csv"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("subprocess.Popen")
|
||||||
|
def test_get_version_02(mock_popen):
|
||||||
|
mock_popen.return_value = get_process_mock(4, "successes", "errors")
|
||||||
|
with pytest.raises(casadm.CasadmError):
|
||||||
|
casadm.get_version()
|
@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python2
|
|
||||||
#
|
#
|
||||||
# Copyright(c) 2012-2019 Intel Corporation
|
# Copyright(c) 2012-2019 Intel Corporation
|
||||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
@ -223,7 +222,7 @@ class cas_config(object):
|
|||||||
type(self).check_cache_id_valid(self.cache_id)
|
type(self).check_cache_id_valid(self.cache_id)
|
||||||
self.check_recursive()
|
self.check_recursive()
|
||||||
self.check_cache_mode_valid(self.cache_mode)
|
self.check_cache_mode_valid(self.cache_mode)
|
||||||
for param_name, param_value in self.params.iteritems():
|
for param_name, param_value in self.params.items():
|
||||||
self.validate_parameter(param_name, param_value)
|
self.validate_parameter(param_name, param_value)
|
||||||
|
|
||||||
if not allow_incomplete:
|
if not allow_incomplete:
|
||||||
@ -289,7 +288,7 @@ class cas_config(object):
|
|||||||
ret = '{0}\t{1}\t{2}'.format(self.cache_id, self.device, self.cache_mode)
|
ret = '{0}\t{1}\t{2}'.format(self.cache_id, self.device, self.cache_mode)
|
||||||
if len(self.params) > 0:
|
if len(self.params) > 0:
|
||||||
i = 0
|
i = 0
|
||||||
for param, value in self.params.iteritems():
|
for param, value in self.params.items():
|
||||||
if i > 0:
|
if i > 0:
|
||||||
ret += ','
|
ret += ','
|
||||||
else:
|
else:
|
||||||
@ -408,14 +407,14 @@ class cas_config(object):
|
|||||||
raise cas_config.AlreadyConfiguredException(
|
raise cas_config.AlreadyConfiguredException(
|
||||||
'Cache already configured')
|
'Cache already configured')
|
||||||
|
|
||||||
for cache_id, cache in self.caches.iteritems():
|
for cache_id, cache in self.caches.items():
|
||||||
if cache_id != new_cache_config.cache_id:
|
if cache_id != new_cache_config.cache_id:
|
||||||
if (os.path.realpath(new_cache_config.device)
|
if (os.path.realpath(new_cache_config.device)
|
||||||
== os.path.realpath(cache.device)):
|
== os.path.realpath(cache.device)):
|
||||||
raise cas_config.ConflictingConfigException(
|
raise cas_config.ConflictingConfigException(
|
||||||
'This cache device is already configured as a cache')
|
'This cache device is already configured as a cache')
|
||||||
|
|
||||||
for _, core in cache.cores.iteritems():
|
for _, core in cache.cores.items():
|
||||||
if (os.path.realpath(core.device)
|
if (os.path.realpath(core.device)
|
||||||
== os.path.realpath(new_cache_config.device)):
|
== os.path.realpath(new_cache_config.device)):
|
||||||
raise cas_config.ConflictingConfigException(
|
raise cas_config.ConflictingConfigException(
|
||||||
@ -433,13 +432,13 @@ class cas_config(object):
|
|||||||
raise KeyError('Cache id {0} doesn\'t exist'.format(new_core_config.cache_id))
|
raise KeyError('Cache id {0} doesn\'t exist'.format(new_core_config.cache_id))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for cache_id, cache in self.caches.iteritems():
|
for cache_id, cache in self.caches.items():
|
||||||
if (os.path.realpath(cache.device)
|
if (os.path.realpath(cache.device)
|
||||||
== os.path.realpath(new_core_config.device)):
|
== os.path.realpath(new_core_config.device)):
|
||||||
raise cas_config.ConflictingConfigException(
|
raise cas_config.ConflictingConfigException(
|
||||||
'Core device already configured as a cache')
|
'Core device already configured as a cache')
|
||||||
|
|
||||||
for core_id, core in cache.cores.iteritems():
|
for core_id, core in cache.cores.items():
|
||||||
if (cache_id == new_core_config.cache_id
|
if (cache_id == new_core_config.cache_id
|
||||||
and core_id == new_core_config.core_id):
|
and core_id == new_core_config.core_id):
|
||||||
if (os.path.realpath(core.device)
|
if (os.path.realpath(core.device)
|
||||||
@ -478,7 +477,7 @@ class cas_config(object):
|
|||||||
conf.write('# This config was automatically generated\n')
|
conf.write('# This config was automatically generated\n')
|
||||||
|
|
||||||
conf.write('[caches]\n')
|
conf.write('[caches]\n')
|
||||||
for _, cache in self.caches.iteritems():
|
for _, cache in self.caches.items():
|
||||||
conf.write(cache.to_line())
|
conf.write(cache.to_line())
|
||||||
|
|
||||||
conf.write('\n[cores]\n')
|
conf.write('\n[cores]\n')
|
||||||
|
Loading…
Reference in New Issue
Block a user