open-cas-linux/test/functional/tests/misc/test_device_capabilities.py
Kamil Gierszewski 720475f85c
tests: update_test
Signed-off-by: Kamil Gierszewski <kamil.gierszewski@huawei.com>
2024-11-25 14:20:09 +01:00

198 lines
8.5 KiB
Python

#
# Copyright(c) 2020-2021 Intel Corporation
# Copyright(c) 2024 Huawei Technologies Co., Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#
import math
import posixpath
import pytest
from api.cas import casadm, cli_messages
from api.cas.cache_config import CacheLineSize
from core.test_run import TestRun
from storage_devices.device import Device
from storage_devices.disk import DiskTypeSet, DiskType
from storage_devices.partition import Partition
from test_tools import disk_utils, fs_utils
from test_utils.output import CmdException
from test_utils.size import Size, Unit
@pytest.mark.os_dependent
@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane]))
@pytest.mark.require_disk("core", DiskTypeSet([DiskType.nand]))
@pytest.mark.require_plugin("scsi_debug")
def test_device_capabilities():
"""
title: Test whether CAS device capabilities are properly set.
description: |
Test if CAS device takes into consideration differences between devices which are used to
create it.
pass_criteria:
- CAS device starts successfully using differently configured devices.
- CAS device capabilities are as expected.
"""
core_device = TestRun.disks["core"]
default_max_io_size = core_device.get_max_io_size()
iteration_settings = [
{"device": "SCSI-debug module",
"dev_size_mb": 1024, "logical_block_size": 512, "max_sectors_kb": 1024},
{"device": "SCSI-debug module",
"dev_size_mb": 1024, "logical_block_size": 512, "max_sectors_kb": 256},
{"device": "SCSI-debug module",
"dev_size_mb": 1024, "logical_block_size": 512, "max_sectors_kb": 128},
{"device": "SCSI-debug module",
"dev_size_mb": 2048, "logical_block_size": 2048, "max_sectors_kb": 1024},
{"device": "standard core device",
"max_sectors_kb": int(default_max_io_size.get_value(Unit.KibiByte))},
{"device": "standard core device", "max_sectors_kb": 128}
]
for i in range(0, len(iteration_settings)):
device = iteration_settings[i]["device"]
group_title = f"{device} | "
if device == "SCSI-debug module":
group_title += f"dev_size_mb = {iteration_settings[i]['dev_size_mb']} | " \
f"logical_block_size = {iteration_settings[i]['logical_block_size']} | "
group_title += f"max_sectors_kb = {iteration_settings[i]['max_sectors_kb']}"
with TestRun.group(group_title):
with TestRun.step("Prepare devices."):
core_device = prepare_core_device(iteration_settings[i])
cache_device = TestRun.disks['cache']
with TestRun.step("Start cache and add prepared core device as core."):
cache, core, error_output = prepare_cas_device(cache_device, core_device)
with TestRun.step("Compare capabilities for CAS device, cache and core "
"(or check proper error if logical sector mismatch occurs)."):
compare_capabilities(cache_device, core_device, cache, core, error_output)
with TestRun.step("Recreate CAS device with switched cache and core devices."):
cache, core, error_output = prepare_cas_device(core_device, cache_device)
with TestRun.step("Compare capabilities for CAS device, cache and core "
"(or check proper error if logical sector mismatch occurs)."):
compare_capabilities(core_device, cache_device, cache, core, error_output)
# Methods used in test
def prepare_core_device(settings):
if settings["device"] == "SCSI-debug module":
core_device = create_scsi_debug_device(
settings["logical_block_size"], 4, settings["dev_size_mb"])
else:
core_device = TestRun.disks['core']
core_device.set_max_io_size(Size(settings["max_sectors_kb"], Unit.KibiByte))
return core_device
def create_scsi_debug_device(sector_size: int, physblk_exp: int, dev_size_mb=1024):
scsi_debug_params = {
"delay": "0",
"virtual_gb": "200",
"dev_size_mb": str(dev_size_mb),
"sector_size": str(sector_size),
"physblk_exp": str(physblk_exp)
}
scsi_debug = TestRun.plugin_manager.get_plugin('scsi_debug')
scsi_debug.params = scsi_debug_params
scsi_debug.reload()
return TestRun.scsi_debug_devices[0]
def prepare_cas_device(cache_device, core_device):
cache = casadm.start_cache(cache_device, cache_line_size=CacheLineSize.LINE_64KiB, force=True)
try:
cache_dev_bs = disk_utils.get_block_size(cache_device.device_id)
core_dev_bs = disk_utils.get_block_size(core_device.device_id)
core = cache.add_core(core_device)
if cache_dev_bs > core_dev_bs:
TestRun.LOGGER.error(
f"CAS device started with cache device logical block size ({cache_dev_bs}) "
f"greater than core device logical block size ({core_dev_bs})")
return cache, core, None
except CmdException as e:
if cache_dev_bs <= core_dev_bs:
TestRun.fail("Failed to create CAS device.")
TestRun.LOGGER.info("Cannot add core device with mismatching logical sector size. "
"Check output instead of capabilities.")
return cache, None, e.output
def method_min_not_zero(a, b):
return a if a != 0 and (a < b or b == 0) else b
def method_lcm_not_zero(a, b):
if a == 0 or b == 0:
return max([a, b])
# gcd - greatest common divisor
return a * b / math.gcd(a, b)
# device capabilities and their test comparison methods
capabilities = {"logical_block_size": max,
"max_hw_sectors_kb": None,
"max_integrity_segments": method_min_not_zero,
"max_sectors_kb": None,
"max_segments": None,
"minimum_io_size": max,
"optimal_io_size": method_lcm_not_zero,
"physical_block_size": max,
"write_same_max_bytes": min}
def measure_capabilities(dev: Device) -> dict:
dev_capabilities = {capability: int(dev.get_sysfs_property(capability))
for capability in capabilities}
return dev_capabilities
def compare_capabilities(cache_device, core_device, cache, core, msg):
if core is None:
cli_messages.check_stderr_msg(msg,
cli_messages.try_add_core_sector_size_mismatch)
else:
core_dev_sectors_num = \
disk_utils.get_size(core_device.device_id) / disk_utils.get_block_size(
core_device.device_id)
core_sectors_num = disk_utils.get_size(core.device_id) / disk_utils.get_block_size(
core.device_id)
if core_dev_sectors_num != core_sectors_num:
TestRun.LOGGER.error(
"Number of sectors in CAS device and attached core device is different.")
cache.stop()
return
cas_capabilities = measure_capabilities(core)
cache_dev_capabilities = measure_capabilities(cache_device)
core_dev_capabilities = measure_capabilities(core_device)
for (capability, method) in capabilities.items():
cas_val = cas_capabilities[capability]
cache_val = cache_dev_capabilities[capability]
core_val = core_dev_capabilities[capability]
expected_val = method(core_val, cache_val) if method is not None else core_val
if capability in ["max_sectors_kb", "max_hw_sectors_kb"] and expected_val != cas_val:
# On the newer kernels this trait is rounded. Instead of checking for
# the current kernel version, assume that both values are acceptable.
SECTOR_SHIFT = 9
lbs = measure_capabilities(core)["logical_block_size"]
# The original uint is kb, but number of sectors is needed
new_expected_val = expected_val * 2
round_val = lbs >> SECTOR_SHIFT
new_expected_val -= new_expected_val % round_val
# Restore the original unit
expected_val = new_expected_val // 2
if expected_val != cas_val:
TestRun.LOGGER.error(f"Cas device {capability} is not set properly. Is: {cas_val}, "
f"should be {expected_val} (cache: {cache_val}, "
f"core: {core_val})")
continue
TestRun.LOGGER.info(f"Cas device {capability} has proper value: {cas_val} "
f"(cache: {cache_val}, core: {core_val})")
cache.stop()