Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions test/functional/tests/io_class/test_io_class_wlth.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ def test_ioclass_wlth():
cache.purge_cache()
cache.reset_counters()

with TestRun.step("Trigger IO with a write life time hint"):
# Fio adds hints only to direct IO. Even if `write_hint` param isn't provided, direct IO
# has assigned a hint by default
with TestRun.step("Trigger IO without write life time hint"):
# Trigger the un-hinted IO first. On kernels that store the hint on the inode (>= 6.9),
# the hint persists on the /dev/cas1-1 inode until cleared with F_SET_RW_HINT.
io_count = 12345
io_size = Size(io_count, Unit.Blocks4096)
bs = Size(1, Unit.Blocks4096)
Expand All @@ -78,27 +78,29 @@ def test_ioclass_wlth():
.io_engine(IoEngine.libaio)
.size(io_size)
.block_size(bs)
.write_hint("long")
.read_write(ReadWrite.write)
.target(core.path)
.direct()
.run()
)
sync()
drop_caches()

with TestRun.step("Trigger IO without write life time hint"):
with TestRun.step("Trigger IO with a write life time hint"):
# Fio adds hints only to direct IO. Even if `write_hint` param isn't provided, direct IO
# has assigned a hint by default
(
Fio()
.create_command()
.io_engine(IoEngine.libaio)
.size(io_size)
.offset(io_size)
.block_size(bs)
.write_hint("long")
.read_write(ReadWrite.write)
.target(core.path)
.direct()
.run()
)
sync()
drop_caches()

with TestRun.step("Check stats"):
default_io_class_stats = core.get_io_class_statistics(io_class_id=0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from api.cas.cache_config import CacheMode, CacheModeTrait, CacheLineSize
from core.test_run import TestRun
from storage_devices.disk import DiskTypeSet, DiskType, DiskTypeLowerThan
from test_tools.dd import Dd
from test_tools.fs_tools import Filesystem
from connection.utils.output import CmdException
from type_def.size import Size, Unit
Expand Down Expand Up @@ -129,7 +130,8 @@ def test_recovery_unplug_cache_raw(cache_mode, cls):
title: Test for recovery after cache drive removal - test on raw device.
description: |
Verify that unflushed data can be safely recovered after, when SSD drive is removed
after write completion - test on raw device.
after write completion - test on raw device. Stop immediately after unplug to verify
flush error handling.
pass_criteria:
- CAS recovers successfully after cache drive unplug
- No data corruption
Expand Down Expand Up @@ -198,3 +200,93 @@ def test_recovery_unplug_cache_raw(cache_mode, cls):
except Exception:
# On some OSes files at /tmp location are automatically removed after DUT hard reset
pass


@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
@pytest.mark.parametrizex("cache_mode", CacheMode.with_traits(CacheModeTrait.LazyWrites))
@pytest.mark.parametrizex("cls", [CacheLineSize.LINE_4KiB, CacheLineSize.LINE_64KiB])
@pytest.mark.require_plugin("power_control")
def test_recovery_unplug_cache_raw_metadata_err(cache_mode, cls):
"""
title: Test for recovery after cache drive removal - test on raw device.
description: |
Verify that unflushed data can be safely recovered after, when SSD drive is removed
after write completion - test on raw device. Trigger cache error by write attempt
after unplugging cache, to enter metadata error mode before stop.
pass_criteria:
- CAS recovers successfully after cache drive unplug
- No data corruption
"""
with TestRun.step("Prepare devices"):
cache_disk = TestRun.disks['cache']
core_disk = TestRun.disks['core']
cache_disk.create_partitions([Size(2, Unit.GibiByte)])
core_disk.create_partitions([Size(16, Unit.GibiByte)])
cache_device = cache_disk.partitions[0]
core_device = core_disk.partitions[0]

with TestRun.step("Create test files."):
source_file, target_file = create_test_files(test_file_size)
source_file_md5 = source_file.md5sum()

with TestRun.step("Start cache and add core."):
cache = casadm.start_cache(cache_device, cache_mode, cls, force=True)
core = cache.add_core(core_device)

with TestRun.step("Copy file to CAS."):
copy_file(source=source_file.full_path, target=core.path,
size=test_file_size, direct="oflag")
TestRun.LOGGER.info(str(core.get_statistics()))

with TestRun.step("Unplug cache device."):
cache_disk.unplug()
TestRun.LOGGER.info(f"List caches:\n{casadm.list_caches().stdout}")
TestRun.LOGGER.info(f"Dirty blocks on cache: "
f"{cache.get_dirty_blocks().get_value(Unit.Blocks4096)}")

with TestRun.step("Try to write to CAS."):
Dd().input("/dev/random") \
.output(core.path) \
.oflag("direct") \
.block_size(Size(1, Unit.Blocks4096)) \
.count(1) \
.run()

with TestRun.step("Stop cache."):
try:
cache.stop()
TestRun.fail("Stopping the cache should be aborted without --no-flush flag.")
except CmdException as e:
TestRun.LOGGER.info(str(e.output))
try:
cache.stop(no_data_flush=True)
TestRun.LOGGER.warning("Expected stopping cache with errors with --no-flush flag.")
except CmdException as e1:
cli_messages.check_stderr_msg(e1.output, cli_messages.stop_cache_errors)

with TestRun.step("Plug missing cache device."):
TestRun.LOGGER.info(str(casadm.list_caches(by_id_path=False)))
cache_disk.plug_all()

with TestRun.step("Load cache."):
cache = casadm.load_cache(cache_device)
TestRun.LOGGER.info(f"Dirty blocks on cache: "
f"{cache.get_dirty_blocks().get_value(Unit.Blocks4096)}")

with TestRun.step("Stop cache with data flush."):
cache.stop()

with TestRun.step("Copy file from core device and check md5sum."):
copy_file(source=core_device.path, target=target_file.full_path,
size=test_file_size, direct="iflag")
target_file_md5 = target_file.md5sum()
compare_files(source_file_md5, target_file_md5)

with TestRun.step("Cleanup core device and remove test files."):
try:
target_file.remove()
source_file.remove()
except Exception:
# On some OSes files at /tmp location are automatically removed after DUT hard reset
pass
8 changes: 4 additions & 4 deletions test/functional/tests/misc/test_files_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@
}

installed_files_perms_exceptions = {
"lib/opencas/casctl": 755,
"lib/opencas/open-cas-loader.py": 755,
"sbin/casadm": 755,
"sbin/casctl": 755,
"usr/lib/opencas/casctl": 755,
"usr/lib/opencas/open-cas-loader.py": 755,
"usr/sbin/casadm": 755,
"usr/sbin/casctl": 755,
"usr/lib/systemd/system-shutdown/open-cas.shutdown": 755,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#
# Copyright(c) 2022 Intel Corporation
# Copyright(c) 2024 Huawei Technologies Co., Ltd.
# Copyright(c) 2026 Unvertical
# SPDX-License-Identifier: BSD-3-Clause
#

Expand Down Expand Up @@ -91,7 +92,8 @@ async def test_stress_io_class_change_config_during_io_fs():
- IO class configuration changes successfully
- No IO errors
"""
cores_per_cache = len(list(Filesystem))
filesystems = Filesystem.regular()
cores_per_cache = len(filesystems)

with TestRun.step("Prepare devices."):
cache_device = TestRun.disks['cache']
Expand All @@ -111,7 +113,7 @@ async def test_stress_io_class_change_config_during_io_fs():
generate_and_load_random_io_class_config(cache)

with TestRun.step("Create different filesystem on each CAS device."):
for core, fs in zip(cores, Filesystem):
for core, fs in zip(cores, filesystems):
core.create_filesystem(fs)
core.mount(os.path.join("/mnt", fs.name))

Expand Down
3 changes: 3 additions & 0 deletions test/functional/tests/volumes/test_discard_on_huge_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ def test_discard_on_huge_core():
with TestRun.step("Check dmesg for RCU-sched stall."):
check_for_rcu_sched_type_stall()

with TestRun.step("Stop cache."):
cache.stop()

with TestRun.step("Unload scsi_debug module."):
scsi_debug.unload()

Expand Down
Loading