Add tests for dynamic cache mode switching

Signed-off-by: Rafal Stefanowski <rafal.stefanowski@intel.com>
This commit is contained in:
Rafal Stefanowski 2019-12-16 15:36:47 +01:00
parent b1e653a61c
commit d0f19af99a

View File

@ -0,0 +1,369 @@
#
# Copyright(c) 2019 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
import time
import pytest
from datetime import timedelta
from api.cas import casadm
from api.cas.cache_config import CacheMode
from storage_devices.disk import DiskType, DiskTypeSet, DiskTypeLowerThan
from core.test_run import TestRun
from test_utils.size import Size, Unit
from test_utils.os_utils import Udev, sync
from test_tools.fio.fio import Fio
from test_tools.fio.fio_param import ReadWrite, IoEngine, VerifyMethod
io_size = Size(10000, Unit.Blocks4096)
@pytest.mark.parametrize(
"cache_mode",
[
(CacheMode.WT, CacheMode.WB),
(CacheMode.WB, CacheMode.PT),
(CacheMode.WB, CacheMode.WT),
(CacheMode.PT, CacheMode.WT),
(CacheMode.WT, CacheMode.WA),
(CacheMode.WT, CacheMode.WO),
(CacheMode.WB, CacheMode.WO),
(CacheMode.PT, CacheMode.WO),
(CacheMode.WA, CacheMode.WO),
(CacheMode.WO, CacheMode.WT),
(CacheMode.WO, CacheMode.WB),
(CacheMode.WO, CacheMode.PT),
(CacheMode.WO, CacheMode.WA),
],
)
@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
def test_cache_stop_and_load(cache_mode):
"""
title: Test for stopping and loading cache back with dynamic cache mode switching.
description: |
Validate the ability of the CAS to switch cache modes at runtime and
check if all of them are working properly after switching and
after stopping and reloading cache back.
Check also other parameters consistency after reload.
pass_criteria:
- In all cache modes data reads and writes are handled properly before and after reload.
- All cache parameters preserve their values after reload.
"""
with TestRun.step("Partition cache and core devices"):
cache_dev, core_dev = storage_prepare()
with TestRun.step(f"Start cache in {cache_mode[0]} mode"):
cache = casadm.start_cache(cache_dev, cache_mode[0], force=True)
Udev.disable()
with TestRun.step("Add core to the cache"):
core = cache.add_core(core_dev)
with TestRun.step(f"Change cache mode to {cache_mode[1]}"):
cache.set_cache_mode(cache_mode[1], flush=True)
check_cache_config = cache.get_cache_config()
with TestRun.step(f"Check if {cache_mode[1]} cache mode works properly"):
check_cache_mode_operation(cache, core, cache_mode[1])
with TestRun.step("Stop and load cache back"):
cache.stop()
cache = casadm.load_cache(cache_dev)
with TestRun.step("Check parameters consistency"):
if check_cache_config != cache.get_cache_config():
failed_params = ""
if check_cache_config.cache_mode != cache.get_cache_mode():
failed_params += (
f"Cache mode is: {check_cache_config.cache_mode}, "
f"should be: {cache.get_cache_mode()}\n"
)
if check_cache_config.cleaning_policy != cache.get_cleaning_policy():
failed_params += (
f"Cleaning policy is: {check_cache_config.cleaning_policy}, "
f"should be: {cache.get_cleaning_policy()}\n"
)
if check_cache_config.eviction_policy != cache.get_eviction_policy():
failed_params += (
f"Eviction policy is: {check_cache_config.eviction_policy}, "
f"should be: {cache.get_eviction_policy()}\n"
)
if check_cache_config.cache_line_size != cache.get_cache_line_size():
failed_params += (
f"Cache line size is: {check_cache_config.cache_line_size}, "
f"should be: {cache.get_cache_line_size()}\n"
)
if check_cache_config.metadata_mode != cache.get_metadata_mode():
failed_params += (
f"Metadata mode is: {check_cache_config.metadata_mode}, "
f"should be: {cache.get_metadata_mode()}\n"
)
TestRun.fail(f"Parameters do not match after reload:\n{failed_params}")
with TestRun.step(
f"Check if {cache_mode[1]} cache mode works properly after reload"
):
if cache_mode[1] == CacheMode.WA or cache_mode[1] == CacheMode.WO:
check_separated_read_write_after_reload(cache, core, cache_mode[1], io_size)
else:
check_cache_mode_operation(cache, core, cache_mode[1])
with TestRun.step("Stop all caches"):
casadm.stop_all_caches()
Udev.enable()
@pytest.mark.parametrize(
"cache_mode_1,cache_mode_2,flush",
[
(CacheMode.WT, CacheMode.WB, False),
(CacheMode.WB, CacheMode.PT, False),
(CacheMode.WB, CacheMode.PT, True),
(CacheMode.WB, CacheMode.WT, False),
(CacheMode.WB, CacheMode.WT, True),
(CacheMode.PT, CacheMode.WT, False),
(CacheMode.WT, CacheMode.WA, False),
(CacheMode.WT, CacheMode.WO, False),
(CacheMode.WB, CacheMode.WO, False),
(CacheMode.WB, CacheMode.WO, True),
(CacheMode.PT, CacheMode.WO, False),
(CacheMode.WA, CacheMode.WO, False),
(CacheMode.WO, CacheMode.WT, False),
(CacheMode.WO, CacheMode.WT, True),
(CacheMode.WO, CacheMode.WB, False),
(CacheMode.WO, CacheMode.WB, True),
(CacheMode.WO, CacheMode.PT, False),
(CacheMode.WO, CacheMode.PT, True),
(CacheMode.WO, CacheMode.WA, False),
(CacheMode.WO, CacheMode.WA, True),
],
)
@pytest.mark.parametrize("io_mode", [ReadWrite.randwrite, ReadWrite.randrw])
@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
def test_cache_mode_switching_during_io(cache_mode_1, cache_mode_2, flush, io_mode):
"""
title: Test for dynamic cache mode switching during IO.
description: |
Validate the ability of CAS to switch cache modes
during working IO on CAS device.
pass_criteria:
- Cache mode is switched without errors.
"""
with TestRun.step("Partition cache and core devices"):
cache_dev, core_dev = storage_prepare()
with TestRun.step(f"Start cache in {cache_mode_1} mode"):
cache = casadm.start_cache(cache_dev, cache_mode_1, force=True)
with TestRun.step("Add core to the cache"):
core = cache.add_core(core_dev)
with TestRun.step("Run 'fio'"):
fio = (
fio_prepare(core, io_mode)
.verify(VerifyMethod.sha1)
.run_time(timedelta(minutes=4))
.time_based()
)
fio_pid = fio.run_in_background()
time.sleep(5)
with TestRun.step(
f"Change cache mode to {cache_mode_2} with flush cache option set to: {flush}"
):
cache_mode_switch_output = cache.set_cache_mode(cache_mode_2, flush)
if cache_mode_switch_output.exit_code != 0:
TestRun.fail("Cache mode switch failed!")
with TestRun.step(f"Check if cache mode has switched properly during IO"):
cache_mode_after_switch = cache.get_cache_mode()
if cache_mode_after_switch != cache_mode_2:
TestRun.fail(
f"Cache mode did not switch properly! "
f"Cache mode after switch is: {cache_mode_after_switch}, "
f"should be: {cache_mode_2}"
)
with TestRun.step("Stop 'fio'"):
TestRun.executor.kill_process(fio_pid)
with TestRun.step("Stop all caches"):
casadm.stop_all_caches()
def storage_prepare():
cache_dev = TestRun.disks["cache"]
cache_dev.create_partitions([Size(1, Unit.GibiByte)])
core_dev = TestRun.disks["core"]
core_dev.create_partitions([Size(2, Unit.GibiByte)])
return cache_dev.partitions[0], core_dev.partitions[0]
def check_cache_mode_operation(cache, core, cache_mode):
cache.reset_counters()
if cache_mode == CacheMode.WT:
io_mode = ReadWrite.randwrite
run_io_and_verify(cache, core, io_mode)
if cache_mode == CacheMode.WB or cache_mode == CacheMode.PT:
io_mode = ReadWrite.randrw
run_io_and_verify(cache, core, io_mode)
if cache_mode == CacheMode.WA or cache_mode == CacheMode.WO:
io_mode = ReadWrite.randread
run_io_and_verify(cache, core, io_mode)
cache.reset_counters()
io_mode = ReadWrite.randwrite
run_io_and_verify(cache, core, io_mode)
def run_io_and_verify(cache, core, io_mode):
fio_prepare(core, io_mode).run()
sync()
cache_mode = cache.get_cache_mode()
cache_stats = cache.get_statistics()
core_stats = core.get_statistics()
if cache_mode == CacheMode.WB:
if (
core_stats.block_stats.core.writes.value != 0
or core_stats.block_stats.exp_obj.writes.value <= 0
):
TestRun.fail(
"Write-Back cache mode is not working properly! "
"There should be some writes to CAS device and none to the core."
)
if cache_mode == CacheMode.PT:
if (
cache_stats.block_stats.cache.writes.value != 0
or cache_stats.block_stats.cache.reads.value != 0
):
TestRun.fail(
"Pass-Through cache mode is not working properly! "
"There should be no reads or writes from/to cache."
)
if cache_mode == CacheMode.WT:
if cache_stats.block_stats.cache != cache_stats.block_stats.core:
TestRun.fail(
"Write-Through cache mode is not working properly! "
"'cache writes' and 'core writes' counts should be the same."
)
if cache_mode == CacheMode.WA:
if io_mode == ReadWrite.randread:
if (
cache_stats.block_stats.cache.writes != io_size
or cache_stats.block_stats.core.reads != io_size
):
TestRun.fail(
"Write-Around cache mode is not working properly for data reads! "
"'cache writes' and 'core reads' should equal total data reads."
)
if io_mode == ReadWrite.randwrite:
if cache_stats.block_stats.cache.writes != io_size:
TestRun.fail(
"Write-Around cache mode is not working properly for data writes! "
"There should be no writes to cache since previous read operation."
)
if cache_mode == CacheMode.WO:
if io_mode == ReadWrite.randread:
if (
cache_stats.block_stats.cache.writes.value != 0
or cache_stats.block_stats.cache.reads.value != 0
):
TestRun.fail(
"Write-Only cache mode is not working properly for data reads! "
"There should be no reads or writes from/to cache."
)
if io_mode == ReadWrite.randwrite:
if (
core_stats.block_stats.core.writes.value != 0
or core_stats.block_stats.exp_obj.writes != io_size
):
TestRun.fail(
"Write-Only cache mode is not working properly for data writes! "
"All writes should be passed to CAS device and none to the core."
)
def check_separated_read_write_after_reload(cache, core, cache_mode, io_size):
# io_size_after_reload should be set to a greater value then global io_size value
io_size_after_reload = Size(12000, Unit.Blocks4096)
if io_size_after_reload <= io_size:
TestRun.fail(
"io_size_after_reload value is not greater then global io_size value!"
)
io_mode = ReadWrite.randread
fio_prepare(core, io_mode, io_size_after_reload).run()
sync()
cache_stats = cache.get_statistics()
io_new_data = io_size_after_reload - io_size
if cache_mode == CacheMode.WA:
if (
cache_stats.block_stats.cache.writes != io_new_data
or cache_stats.block_stats.core.reads != io_new_data
):
TestRun.fail(
"Write-Around cache mode is not working properly for data reads after reload! "
"'cache writes' and 'core reads' should equal "
"the difference from previous data reads."
)
if cache_mode == CacheMode.WO:
if (
cache_stats.block_stats.cache.writes.value != 0
or cache_stats.block_stats.cache.reads != io_size
):
TestRun.fail(
"Write-Only cache mode is not working properly for data reads after reload! "
"There should be no writes to cache and reads "
"from cache should equal previous writes to it."
)
cache.reset_counters()
io_mode = ReadWrite.randwrite
fio_prepare(core, io_mode, io_size_after_reload).run()
sync()
cache_stats = cache.get_statistics()
core_stats = core.get_statistics()
if cache_mode == CacheMode.WA:
if cache_stats.block_stats.cache.writes != io_size_after_reload:
TestRun.fail(
"Write-Around cache mode is not working properly for data writes after reload! "
"There should be no writes to cache since previous read operation."
)
if cache_mode == CacheMode.WO:
if (
core_stats.block_stats.core.writes.value != 0
or core_stats.block_stats.exp_obj.writes != io_size_after_reload
):
TestRun.fail(
"Write-Only cache mode is not working properly for data writes after reload! "
"All writes should be passed to CAS device and none to the core."
)
def fio_prepare(core, io_mode, io_size=io_size):
fio = (
Fio()
.create_command()
.io_engine(IoEngine.libaio)
.size(io_size)
.read_write(io_mode)
.target(core.system_path)
.direct(1)
)
return fio