262 lines
10 KiB
Python
262 lines
10 KiB
Python
#
|
|
# 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]) |