open-cas-linux/test/functional/tests/stats/test_block_stats.py
Slawomir Jankowski 17f440de10 Update TF and functional tests API
Signed-off-by: Slawomir Jankowski <slawomir.jankowski@intel.com>
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
2020-12-22 16:29:33 +01:00

323 lines
12 KiB
Python

#
# Copyright(c) 2019-2020 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
import pytest
from api.cas import casadm
from api.cas import ioclass_config
from api.cas.cache_config import CacheMode, CleaningPolicy
from api.cas.casadm import StatsFilter
from core.test_run import TestRun
from storage_devices.disk import DiskType, DiskTypeSet, DiskTypeLowerThan
from test_tools.dd import Dd
from test_utils.os_utils import Udev
from test_utils.size import Size, Unit
ioclass_config_path = "/tmp/opencas_ioclass.conf"
mountpoint = "/tmp/cas1-1"
exported_obj_path_prefix = "/dev/cas1-"
cache_id = 1
# lists of cache and core block stats, that should have zero value for particular cache modes
write_wb_zero_stats = [
"reads from core(s)",
"writes to core(s)",
"total to/from core(s)",
"reads from cache",
"reads from exported object(s)",
"reads from core",
"writes to core",
"total to/from core",
"reads from cache",
"reads from exported object",
]
write_wt_zero_stats = [
"reads from core(s)",
"reads from cache",
"reads from exported object(s)",
"reads from core",
"reads from exported object",
]
write_pt_zero_stats = [
"reads from core(s)",
"reads from cache",
"writes to cache",
"total to/from cache",
"reads from exported object(s)",
"reads from core",
"reads from exported object",
]
write_wa_zero_stats = [
"reads from core(s)",
"reads from cache",
"writes to cache",
"total to/from cache",
"reads from exported object(s)",
"reads from core",
"reads from exported object",
]
write_wo_zero_stats = [
"reads from core(s)",
"writes to core(s)",
"total to/from core(s)",
"reads from cache",
"reads from exported object(s)",
"reads from core",
"writes to core",
"total to/from core",
"reads from exported object",
]
@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
@pytest.mark.parametrize(
"cache_mode,zero_stats",
[
(CacheMode.WB, write_wb_zero_stats),
(CacheMode.WT, write_wt_zero_stats),
(CacheMode.PT, write_pt_zero_stats),
(CacheMode.WA, write_wa_zero_stats),
(CacheMode.WO, write_wo_zero_stats),
],
)
def test_block_stats_write(cache_mode, zero_stats):
"""Perform read and write operations to cache instance in different cache modes
and check if block stats values are correct"""
cache, cores = prepare(cache_mode)
iterations = 10
dd_size = Size(4, Unit.KibiByte)
dd_count = 10
flush(cache)
# Check stats for cache after performing write operation
for core in cores:
dd_seek = 0
dd = (
Dd()
.input("/dev/zero")
.output(f"{core.path}")
.count(dd_count)
.block_size(dd_size)
.oflag("direct")
)
# Since every IO has the same size, every stat should be increased with the same step.
# So there is no need to keep value of every stat in separate variable
cache_stat = (
(dd_size.get_value(Unit.Blocks4096) * dd_count) * (core.core_id - 1) * iterations
)
for i in range(iterations):
dd.seek(dd_seek)
dd.run()
cache_stats = cache.get_statistics_flat(stat_filter=[StatsFilter.blk])
core_stats = core.get_statistics_flat(stat_filter=[StatsFilter.blk])
# Check cache stats
assumed_value = (dd_size.get_value(Unit.Blocks4096) * dd_count) * (i + 1)
for key, value in cache_stats.items():
if key in zero_stats:
assert value.get_value(Unit.Blocks4096) == 0, (
f"{key} has invalid value\n"
f"core id {core.core_id}, i: {i}, dd_size: "
f"{dd_size.get_value(Unit.Blocks4096)}\n"
f"dd count: {dd_count}, cache_stat {cache_stat}"
)
else:
# For each next tested core, cache stats has to include
# sum of each previous core
assert cache_stat + assumed_value == value.get_value(Unit.Blocks4096), (
f"{key} has invalid value of {value.get_value(Unit.Blocks4096)}\n"
f"core id {core.core_id}, i: {i}, dd_size: "
f"{dd_size.get_value(Unit.Blocks4096)}\n"
f"dd count: {dd_count}, cache_stat {cache_stat}"
)
# Check single core stats
for key, value in core_stats.items():
if key in zero_stats:
assert value.get_value(Unit.Blocks4096) == 0, (
f"{key} has invalid value of \n"
f"core id {core.core_id}, i: {i}, dd_size: "
f"{dd_size.get_value(Unit.Blocks4096)}\n"
f"dd count: {dd_count}, cache_stat {cache_stat}"
)
else:
assert assumed_value == value.get_value(Unit.Blocks4096), (
f"{key} has invalid value of {value.get_value(Unit.Blocks4096)}\n"
f"core id {core.core_id}, i: {i}, dd_size: "
f"{dd_size.get_value(Unit.Blocks4096)}\n"
f"dd count: {dd_count}, dd seek: {dd_seek}. Cache mode {cache_mode}"
)
dd_seek += dd_count
# lists of cache and core block stats, that should have zero value for particular cache modes
read_wb_zero_stats = [
"writes to core(s)",
"reads from cache",
"writes to exported object(s)",
"writes to core",
"writes to exported object",
]
read_wt_zero_stats = [
"writes to core(s)",
"reads from cache",
"writes to exported object(s)",
"writes to core",
"writes to exported object",
]
read_pt_zero_stats = [
"writes to core(s)",
"reads from cache",
"writes to cache",
"total to/from cache",
"writes to exported object(s)",
"writes to core",
"writes to exported object",
]
read_wa_zero_stats = [
"writes to core(s)",
"reads from cache",
"writes to exported object(s)",
"writes to core",
"writes to exported object",
]
read_wo_zero_stats = [
"writes to core(s)",
"reads from cache",
"writes to cache",
"total to/from cache",
"writes to exported object(s)",
"writes to core",
"writes to exported object",
]
@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
@pytest.mark.parametrize(
"cache_mode,zero_stats",
[
(CacheMode.WB, read_wb_zero_stats),
(CacheMode.WT, read_wt_zero_stats),
(CacheMode.PT, read_pt_zero_stats),
(CacheMode.WA, read_wa_zero_stats),
(CacheMode.WO, read_wo_zero_stats),
],
)
def test_block_stats_read(cache_mode, zero_stats):
"""Perform read and write operations to cache instance in different cache modes
and check if block stats values are correct"""
cache, cores = prepare(cache_mode)
iterations = 10
dd_size = Size(4, Unit.KibiByte)
dd_count = 10
flush(cache)
# Check stats for cache after performing read operation
for core in cores:
dd_skip = 0
dd = (
Dd()
.output("/dev/zero")
.input(f"{core.path}")
.count(dd_count)
.block_size(dd_size)
.iflag("direct")
)
# Since every IO has the same size, every stat should be increased with the same step.
# So there is no need to keep value of every stat in separate variable
cache_stat = (
(dd_size.get_value(Unit.Blocks4096) * dd_count) * (core.core_id - 1) * iterations
)
for i in range(iterations):
dd.skip(dd_skip)
dd.run()
cache_stats = cache.get_statistics_flat(stat_filter=[StatsFilter.blk])
core_stats = core.get_statistics_flat(stat_filter=[StatsFilter.blk])
# Check cache stats
assumed_value = (dd_size.get_value(Unit.Blocks4096) * dd_count) * (i + 1)
for key, value in cache_stats.items():
if key in zero_stats:
assert value.get_value(Unit.Blocks4096) == 0, (
f"{key} has invalid value\n"
f"core id {core.core_id}, i: {i}, dd_size: "
f"{dd_size.get_value(Unit.Blocks4096)}\n"
f"dd count: {dd_count}, cache_stat {cache_stat}"
)
else:
# For each next tested core, cache stats has to include
# sum of each previous core
assert cache_stat + assumed_value == value.get_value(Unit.Blocks4096), (
f"{key} has invalid value of {value.get_value(Unit.Blocks4096)}\n"
f"core id {core.core_id}, i: {i}, dd_size: "
f"{dd_size.get_value(Unit.Blocks4096)}\n"
f"dd count: {dd_count}. Cache mode: {cache_mode}"
)
# Check single core stats
for key, value in core_stats.items():
if key in zero_stats:
assert value.get_value(Unit.Blocks4096) == 0, (
f"{key} has invalid value\n"
f"core id {core.core_id}, i: {i}, dd_size: "
f"{dd_size.get_value(Unit.Blocks4096)}\n"
f"dd count: {dd_count}. Cache mode: {cache_mode}"
)
else:
assert assumed_value == value.get_value(Unit.Blocks4096), (
f"{key} has invalid value of {value.get_value(Unit.Blocks4096)}\n"
f"core id {core.core_id}, i: {i}, dd_size: "
f"{dd_size.get_value(Unit.Blocks4096)}\n"
f"dd count: {dd_count} dd skip {dd_skip}. Cache mode: {cache_mode}"
)
dd_skip += dd_count
def flush(cache):
cache.flush_cache()
cache.reset_counters()
stats = cache.get_statistics_flat(stat_filter=[StatsFilter.blk])
for key, value in stats.items():
assert value.get_value(Unit.Blocks4096) == 0
def prepare(cache_mode: CacheMode):
ioclass_config.remove_ioclass_config()
cache_device = TestRun.disks['cache']
core_device = TestRun.disks['core']
cache_device.create_partitions([Size(500, Unit.MebiByte)])
core_device.create_partitions(
[Size(1, Unit.GibiByte), Size(1, Unit.GibiByte), Size(1, Unit.GibiByte)]
)
cache_device = cache_device.partitions[0]
core_device_1 = core_device.partitions[0]
core_device_2 = core_device.partitions[1]
core_device_3 = core_device.partitions[2]
Udev.disable()
TestRun.LOGGER.info(f"Starting cache")
cache = casadm.start_cache(cache_device, cache_mode=cache_mode, force=True)
TestRun.LOGGER.info(f"Setting cleaning policy to NOP")
casadm.set_param_cleaning(cache_id=cache_id, policy=CleaningPolicy.nop)
TestRun.LOGGER.info(f"Adding core devices")
core_1 = cache.add_core(core_dev=core_device_1)
core_2 = cache.add_core(core_dev=core_device_2)
core_3 = cache.add_core(core_dev=core_device_3)
output = TestRun.executor.run(f"mkdir -p {mountpoint}")
if output.exit_code != 0:
raise Exception(f"Failed to create mountpoint")
return cache, [core_1, core_2, core_3]