Introduce tests for cache attach/detach feature
Signed-off-by: Katarzyna Treder <katarzyna.treder@h-partners.com>
This commit is contained in:
262
test/functional/tests/cache_ops/test_attach_detach.py
Normal file
262
test/functional/tests/cache_ops/test_attach_detach.py
Normal file
@@ -0,0 +1,262 @@
|
||||
#
|
||||
# Copyright(c) 2023-2025 Huawei Technologies Co., Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import posixpath
|
||||
import random
|
||||
import time
|
||||
import pytest
|
||||
|
||||
|
||||
from api.cas import casadm_parser, casadm
|
||||
from api.cas.cache_config import CacheLineSize, CacheMode
|
||||
from api.cas.cli import attach_cache_cmd
|
||||
from api.cas.cli_messages import check_stderr_msg, attach_with_existing_metadata
|
||||
from connection.utils.output import CmdException
|
||||
from core.test_run import TestRun
|
||||
from core.test_run_utils import TestRun
|
||||
from storage_devices.disk import DiskTypeSet, DiskType, DiskTypeLowerThan
|
||||
from storage_devices.nullblk import NullBlk
|
||||
from test_tools.dmesg import clear_dmesg
|
||||
from test_tools.fs_tools import Filesystem, create_directory, create_random_test_file, \
|
||||
check_if_directory_exists, remove
|
||||
from type_def.size import Size, Unit
|
||||
|
||||
mountpoint = "/mnt/cas"
|
||||
test_file_path = f"{mountpoint}/test_file"
|
||||
|
||||
|
||||
@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.nand, DiskType.optane]))
|
||||
@pytest.mark.require_disk("cache2", DiskTypeSet([DiskType.nand, DiskType.optane]))
|
||||
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
|
||||
@pytest.mark.require_disk("core2", DiskTypeLowerThan("cache"))
|
||||
@pytest.mark.parametrizex("cache_mode", CacheMode)
|
||||
@pytest.mark.parametrizex("cache_line_size", CacheLineSize)
|
||||
def test_attach_device_with_existing_metadata(cache_mode, cache_line_size):
|
||||
"""
|
||||
title: Test attaching cache with valid and relevant metadata.
|
||||
description: |
|
||||
Attach disk with valid and relevant metadata and verify whether the running configuration
|
||||
wasn't affected by the values from the old metadata.
|
||||
pass_criteria:
|
||||
- no cache crash during attach and detach.
|
||||
- old metadata doesn't affect running cache.
|
||||
- no kernel panic
|
||||
"""
|
||||
|
||||
with TestRun.step("Prepare random cache line size and cache mode (different than tested)"):
|
||||
random_cache_mode = _get_random_uniq_cache_mode(cache_mode)
|
||||
cache_mode1, cache_mode2 = cache_mode, random_cache_mode
|
||||
random_cache_line_size = _get_random_uniq_cache_line_size(cache_line_size)
|
||||
cache_line_size1, cache_line_size2 = cache_line_size, random_cache_line_size
|
||||
|
||||
with TestRun.step("Clear dmesg log"):
|
||||
clear_dmesg()
|
||||
|
||||
with TestRun.step("Prepare devices for caches and cores"):
|
||||
cache_dev = TestRun.disks["cache"]
|
||||
cache_dev.create_partitions([Size(2, Unit.GibiByte)])
|
||||
cache_dev = cache_dev.partitions[0]
|
||||
|
||||
cache_dev2 = TestRun.disks["cache2"]
|
||||
cache_dev2.create_partitions([Size(2, Unit.GibiByte)])
|
||||
cache_dev2 = cache_dev2.partitions[0]
|
||||
|
||||
core_dev1 = TestRun.disks["core"]
|
||||
core_dev2 = TestRun.disks["core2"]
|
||||
core_dev1.create_partitions([Size(2, Unit.GibiByte)] * 2)
|
||||
core_dev2.create_partitions([Size(2, Unit.GibiByte)] * 2)
|
||||
|
||||
with TestRun.step("Start 2 caches with different parameters and add core to each"):
|
||||
cache1 = casadm.start_cache(
|
||||
cache_dev, force=True, cache_line_size=cache_line_size1
|
||||
)
|
||||
|
||||
if cache1.has_volatile_metadata():
|
||||
pytest.skip("Non-volatile metadata needed to run this test")
|
||||
|
||||
for core in core_dev1.partitions:
|
||||
cache1.add_core(core)
|
||||
|
||||
cache2 = casadm.start_cache(
|
||||
cache_dev2, force=True, cache_line_size=cache_line_size2
|
||||
)
|
||||
|
||||
for core in core_dev2.partitions:
|
||||
cache2.add_core(core)
|
||||
|
||||
cores_in_cache1_before = {
|
||||
core.core_device.path for core in casadm_parser.get_cores(cache_id=cache1.cache_id)
|
||||
}
|
||||
|
||||
with TestRun.step(f"Set cache modes for caches to {cache_mode1} and {cache_mode2}"):
|
||||
cache1.set_cache_mode(cache_mode1)
|
||||
cache2.set_cache_mode(cache_mode2)
|
||||
|
||||
with TestRun.step("Stop second cache"):
|
||||
cache2.stop()
|
||||
|
||||
with TestRun.step("Detach first cache device"):
|
||||
cache1.detach()
|
||||
|
||||
with TestRun.step("Try to attach the other cache device to first cache without force flag"):
|
||||
try:
|
||||
cache1.attach(device=cache_dev2)
|
||||
TestRun.fail("Cache attached successfully"
|
||||
"Expected: cache fail to attach")
|
||||
except CmdException as exc:
|
||||
check_stderr_msg(exc.output, attach_with_existing_metadata)
|
||||
TestRun.LOGGER.info("Cache attach failed as expected")
|
||||
|
||||
with TestRun.step("Attach the other cache device to first cache with force flag"):
|
||||
cache1.attach(device=cache_dev2, force=True)
|
||||
cores_after_attach = casadm_parser.get_cores(cache_id=cache1.cache_id)
|
||||
|
||||
with TestRun.step("Verify if old configuration doesn`t affect new cache"):
|
||||
cores_in_cache1 = {core.core_device.path for core in cores_after_attach}
|
||||
|
||||
if cores_in_cache1 != cores_in_cache1_before:
|
||||
TestRun.fail(
|
||||
f"After attaching cache device, core list has changed:"
|
||||
f"\nUsed {cores_in_cache1}"
|
||||
f"\nShould use {cores_in_cache1_before}."
|
||||
)
|
||||
if cache1.get_cache_line_size() == cache_line_size2:
|
||||
TestRun.fail(
|
||||
f"After attaching cache device, cache line size changed:"
|
||||
f"\nUsed {cache_line_size2}"
|
||||
f"\nShould use {cache_line_size1}."
|
||||
)
|
||||
if cache1.get_cache_mode() != cache_mode1:
|
||||
TestRun.fail(
|
||||
f"After attaching cache device, cache mode changed:"
|
||||
f"\nUsed {cache1.get_cache_mode()}"
|
||||
f"\nShould use {cache_mode1}."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.nand, DiskType.optane]))
|
||||
@pytest.mark.require_disk("cache2", DiskTypeSet([DiskType.nand, DiskType.optane]))
|
||||
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
|
||||
@pytest.mark.parametrizex("cache_mode", [CacheMode.WB, CacheMode.WT])
|
||||
def test_attach_detach_md5sum(cache_mode):
|
||||
"""
|
||||
title: Test for md5sum of file after attach/detach operation.
|
||||
description: |
|
||||
Test data integrity after detach/attach operations
|
||||
pass_criteria:
|
||||
- CAS doesn't crash during attach and detach.
|
||||
- md5sums before and after operations match each other
|
||||
"""
|
||||
|
||||
with TestRun.step("Prepare cache and core devices"):
|
||||
cache_dev = TestRun.disks["cache"]
|
||||
cache_dev.create_partitions([Size(2, Unit.GibiByte)])
|
||||
cache_dev = cache_dev.partitions[0]
|
||||
|
||||
cache_dev2 = TestRun.disks["cache2"]
|
||||
cache_dev2.create_partitions([Size(3, Unit.GibiByte)])
|
||||
cache_dev2 = cache_dev2.partitions[0]
|
||||
|
||||
core_dev = TestRun.disks["core"]
|
||||
core_dev.create_partitions([Size(6, Unit.GibiByte)])
|
||||
core_dev = core_dev.partitions[0]
|
||||
|
||||
with TestRun.step("Start cache and add core"):
|
||||
cache = casadm.start_cache(cache_dev, force=True, cache_mode=cache_mode)
|
||||
core = cache.add_core(core_dev)
|
||||
|
||||
with TestRun.step(f"Change cache mode to {cache_mode}"):
|
||||
cache.set_cache_mode(cache_mode)
|
||||
|
||||
with TestRun.step("Create a filesystem on the core device and mount it"):
|
||||
if check_if_directory_exists(mountpoint):
|
||||
remove(mountpoint, force=True, recursive=True)
|
||||
create_directory(path=mountpoint)
|
||||
core.create_filesystem(Filesystem.xfs)
|
||||
core.mount(mountpoint)
|
||||
|
||||
with TestRun.step("Write data to the exported object"):
|
||||
test_file_main = create_random_test_file(
|
||||
target_file_path=posixpath.join(mountpoint, "test_file"),
|
||||
file_size=Size(5, Unit.GibiByte),
|
||||
)
|
||||
|
||||
with TestRun.step("Calculate test file md5sums before detach"):
|
||||
test_file_md5sum_before = test_file_main.md5sum()
|
||||
|
||||
with TestRun.step("Detach cache device"):
|
||||
cache.detach()
|
||||
|
||||
with TestRun.step("Attach different cache device"):
|
||||
cache.attach(device=cache_dev2, force=True)
|
||||
|
||||
with TestRun.step("Calculate cache test file md5sums after cache attach"):
|
||||
test_file_md5sum_after = test_file_main.md5sum()
|
||||
|
||||
with TestRun.step("Compare test file md5sums"):
|
||||
if test_file_md5sum_before != test_file_md5sum_after:
|
||||
TestRun.fail(
|
||||
f"MD5 sums of core before and after do not match."
|
||||
f"Expected: {test_file_md5sum_before}"
|
||||
f"Actual: {test_file_md5sum_after}"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
|
||||
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
|
||||
@pytest.mark.parametrizex("cache_mode", CacheMode)
|
||||
def test_stop_cache_during_attach(cache_mode):
|
||||
"""
|
||||
title: Test cache stop during attach.
|
||||
description: Test for handling concurrent cache attach and stop.
|
||||
pass_criteria:
|
||||
- No system crash.
|
||||
- Stop operation completed successfully.
|
||||
"""
|
||||
|
||||
with TestRun.step("Create null_blk device for cache"):
|
||||
nullblk = NullBlk.create(size_gb=1500)
|
||||
|
||||
with TestRun.step("Prepare cache and core devices"):
|
||||
cache_dev = nullblk[0]
|
||||
core_dev = TestRun.disks["core"]
|
||||
core_dev.create_partitions([Size(2, Unit.GibiByte)])
|
||||
core_dev = core_dev.partitions[0]
|
||||
|
||||
with TestRun.step(f"Start cache and add core"):
|
||||
cache = casadm.start_cache(cache_dev, force=True, cache_mode=cache_mode)
|
||||
cache.add_core(core_dev)
|
||||
|
||||
with TestRun.step(f"Change cache mode to {cache_mode}"):
|
||||
cache.set_cache_mode(cache_mode)
|
||||
|
||||
with TestRun.step("Detach cache"):
|
||||
cache.detach()
|
||||
|
||||
with TestRun.step("Start cache re-attach in background"):
|
||||
TestRun.executor.run_in_background(
|
||||
attach_cache_cmd(str(cache.cache_id), cache_dev.path)
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
with TestRun.step("Stop cache"):
|
||||
cache.stop()
|
||||
|
||||
with TestRun.step("Verify if cache stopped"):
|
||||
caches = casadm_parser.get_caches()
|
||||
if caches:
|
||||
TestRun.fail(
|
||||
"Cache is still running despite stop operation"
|
||||
"expected behaviour: Cache stopped"
|
||||
"actual behaviour: Cache running"
|
||||
)
|
||||
|
||||
|
||||
def _get_random_uniq_cache_line_size(cache_line_size) -> CacheLineSize:
|
||||
return random.choice([c for c in list(CacheLineSize) if c is not cache_line_size])
|
||||
|
||||
|
||||
def _get_random_uniq_cache_mode(cache_mode) -> CacheMode:
|
||||
return random.choice([c for c in list(CacheMode) if c is not cache_mode])
|
Reference in New Issue
Block a user