ocf/tests/functional/tests/management/test_add_remove.py
Michal Mielewczyk baccc5560b pyocf: Add tests for detaching core
Signed-off-by: Michal Mielewczyk <michal.mielewczyk@huawei.com>
2025-03-26 15:39:55 +01:00

555 lines
17 KiB
Python

#
# Copyright(c) 2019-2022 Intel Corporation
# Copyright(c) 2024-2025 Huawei Technologies
# SPDX-License-Identifier: BSD-3-Clause
#
import pytest
from ctypes import c_int
from random import randint
from pyocf.types.cache import Cache, CacheMode, CleaningPolicy, PromotionPolicy
from pyocf.types.core import Core
from pyocf.types.volume import RamVolume, Volume
from pyocf.types.volume_core import CoreVolume
from pyocf.types.data import Data
from pyocf.types.io import IoDir, Sync
from pyocf.types.queue import Queue
from pyocf.utils import Size as S
from pyocf.types.shared import OcfError, OcfErrorCode, OcfCompletion, CacheLineSize
@pytest.mark.parametrize("cache_mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_adding_core(pyocf_ctx, cache_mode, cls):
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls)
# Create core device
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device)
# Check statistics before adding core
stats = cache.get_stats()
assert stats["conf"]["core_count"] == 0
# Add core to cache
cache.add_core(core)
# Check statistics after adding core
stats = cache.get_stats()
assert stats["conf"]["core_count"] == 1
@pytest.mark.parametrize("cache_mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_removing_core(pyocf_ctx, cache_mode, cls):
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls)
# Create core device
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device)
# Add core to cache
cache.add_core(core)
# Remove core from cache
cache.remove_core(core)
# Check statistics after removing core
stats = cache.get_stats()
assert stats["conf"]["core_count"] == 0
@pytest.mark.parametrize("cache_mode", [CacheMode.WB])
@pytest.mark.parametrize("cls", CacheLineSize)
def test_remove_dirty_no_flush(pyocf_ctx, cache_mode, cls):
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls)
# Create core device
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device)
cache.add_core(core)
vol = CoreVolume(core)
queue = core.cache.get_default_queue()
# Prepare data
core_size = core.get_stats()["size"]
data = Data(core_size.B)
_io_to_core(vol, queue, data)
# Remove core from cache
cache.remove_core(core)
@pytest.mark.parametrize("cleaning_policy", CleaningPolicy)
@pytest.mark.parametrize("promotion_policy", PromotionPolicy)
def test_detach_core_detach_cache_cleaning(pyocf_ctx, cleaning_policy, promotion_policy):
cache_device = RamVolume(S.from_MiB(100))
core_device_1 = RamVolume(S.from_MiB(10))
core_device_2 = RamVolume(S.from_MiB(10))
cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB)
core_1 = Core.using_device(core_device_1, name="core_1")
core_2 = Core.using_device(core_device_2, name="core_2")
cache.add_core(core_1)
cache.add_core(core_2)
cache.set_cleaning_policy(cleaning_policy)
cache.set_promotion_policy(promotion_policy)
core_1.detach()
with pytest.raises(OcfError, match="OCF_ERR_CACHE_IN_INCOMPLETE_STATE"):
cache.detach_device()
@pytest.mark.parametrize("cleaning_policy", CleaningPolicy)
@pytest.mark.parametrize("promotion_policy", PromotionPolicy)
def test_detach_core_stop_cache_cleaning(pyocf_ctx, cleaning_policy, promotion_policy):
cache_device = RamVolume(S.from_MiB(100))
core_device_1 = RamVolume(S.from_MiB(10))
core_device_2 = RamVolume(S.from_MiB(10))
cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB)
core_1 = Core.using_device(core_device_1, name="core_1")
core_2 = Core.using_device(core_device_2, name="core_2")
cache.add_core(core_1)
cache.add_core(core_2)
cache.set_cleaning_policy(cleaning_policy)
cache.set_promotion_policy(promotion_policy)
core_1.detach()
cache.stop()
@pytest.mark.parametrize("cleaning_policy", CleaningPolicy)
@pytest.mark.parametrize("promotion_policy", PromotionPolicy)
def test_detach_cache_detach_core_cleaning(pyocf_ctx, cleaning_policy, promotion_policy):
cache_device = RamVolume(S.from_MiB(100))
core_device_1 = RamVolume(S.from_MiB(10))
core_device_2 = RamVolume(S.from_MiB(10))
cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB)
core_1 = Core.using_device(core_device_1, name="core_1")
core_2 = Core.using_device(core_device_2, name="core_2")
cache.add_core(core_1)
cache.add_core(core_2)
cache.set_cleaning_policy(cleaning_policy)
cache.set_promotion_policy(promotion_policy)
for core in [core_1, core_2]:
vol = CoreVolume(core)
queue = core.cache.get_default_queue()
core_size = core.get_stats()["size"]
data = Data(core_size.B)
_io_to_core(vol, queue, data)
core_1.detach()
cache.stop()
@pytest.mark.parametrize("cleaning_policy", CleaningPolicy)
@pytest.mark.parametrize("promotion_policy", PromotionPolicy)
def test_detach_cache_retach_core_cleaning(pyocf_ctx, cleaning_policy, promotion_policy):
cache_device = RamVolume(S.from_MiB(100))
core_device_1 = RamVolume(S.from_MiB(10))
core_device_2 = RamVolume(S.from_MiB(10))
cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB)
core_1 = Core.using_device(core_device_1, name="core_1")
core_2 = Core.using_device(core_device_2, name="core_2")
def _write_cores(cores_list):
for core in cores_list:
vol = CoreVolume(core)
queue = core.cache.get_default_queue()
core_size = core.get_stats()["size"]
data = Data(core_size.B)
_io_to_core(vol, queue, data)
cache.add_core(core_1)
cache.add_core(core_2)
cache.set_cleaning_policy(cleaning_policy)
cache.set_promotion_policy(promotion_policy)
cache.detach_device()
core_1.detach()
_write_cores([core_2])
cache.attach_device(cache_device)
_write_cores([core_2])
cache.add_core(core_1, try_add=True)
_write_cores([core_1, core_2])
cache.stop()
@pytest.mark.parametrize("cleaning_policy", CleaningPolicy)
@pytest.mark.parametrize("promotion_policy", PromotionPolicy)
def test_reattach_cache_reattach_core_cleaning(pyocf_ctx, cleaning_policy, promotion_policy):
cache_device = RamVolume(S.from_MiB(100))
core_device_1 = RamVolume(S.from_MiB(10))
core_device_2 = RamVolume(S.from_MiB(10))
cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB)
core_1 = Core.using_device(core_device_1, name="core_1")
core_2 = Core.using_device(core_device_2, name="core_2")
def _write_cores(cores_list):
for core in cores_list:
vol = CoreVolume(core)
queue = core.cache.get_default_queue()
core_size = core.get_stats()["size"]
data = Data(core_size.B)
_io_to_core(vol, queue, data)
cache.add_core(core_1)
cache.add_core(core_2)
cache.set_cleaning_policy(cleaning_policy)
cache.set_promotion_policy(promotion_policy)
cache.detach_device()
core_1.detach()
cache.add_core(core_1, try_add=True)
cache.attach_device(cache_device)
_write_cores([core_1, core_2])
cache.stop()
@pytest.mark.parametrize("cleaning_policy", CleaningPolicy)
@pytest.mark.parametrize("promotion_policy", PromotionPolicy)
def test_detach_cache_detach_core_load_cleaning(pyocf_ctx, cleaning_policy, promotion_policy):
cache_device = RamVolume(S.from_MiB(100))
core_device_1 = RamVolume(S.from_MiB(10))
core_device_2 = RamVolume(S.from_MiB(10))
cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB)
core_1 = Core.using_device(core_device_1, name="core_1")
core_2 = Core.using_device(core_device_2, name="core_2")
cache.add_core(core_1)
cache.add_core(core_2)
cache.set_cleaning_policy(cleaning_policy)
cache.set_promotion_policy(promotion_policy)
core_1.detach()
cache.stop()
cache = Cache.load_from_device(cache_device)
def test_30add_remove(pyocf_ctx):
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device)
# Create core device
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device)
# Add and remove core device in a loop 100 times
# Check statistics after every operation
for i in range(0, 30):
cache.add_core(core)
stats = cache.get_stats()
assert stats["conf"]["core_count"] == 1
cache.remove_core(core)
stats = cache.get_stats()
assert stats["conf"]["core_count"] == 0
def test_10add_remove_with_io(pyocf_ctx):
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device)
# Create core device
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device)
# Add and remove core 10 times in a loop with io in between
for i in range(0, 10):
cache.add_core(core)
vol = CoreVolume(core)
stats = cache.get_stats()
assert stats["conf"]["core_count"] == 1
vol.open()
write_data = Data.from_string("Test data")
io = vol.new_io(
cache.get_default_queue(), S.from_sector(1).B, write_data.size, IoDir.WRITE, 0, 0
)
io.set_data(write_data)
Sync(io).submit()
vol.close()
cache.remove_core(core)
stats = cache.get_stats()
assert stats["conf"]["core_count"] == 0
def test_add_remove_30core(pyocf_ctx):
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device)
core_devices = []
core_amount = 30
# Add 50 cores and check stats after each addition
for i in range(0, core_amount):
stats = cache.get_stats()
assert stats["conf"]["core_count"] == i
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device, name=f"core{i}")
core_devices.append(core)
cache.add_core(core)
# Remove 50 cores and check stats before each removal
for i in range(0, core_amount):
stats = cache.get_stats()
assert stats["conf"]["core_count"] == core_amount - i
cache.remove_core(core_devices[i])
# Check statistics
stats = cache.get_stats()
assert stats["conf"]["core_count"] == 0
def test_adding_to_random_cache(pyocf_ctx):
cache_devices = []
core_devices = {}
cache_amount = 5
core_amount = 30
# Create 5 cache devices
for i in range(0, cache_amount):
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device, name=f"cache{i}")
cache_devices.append(cache)
# Create 50 core devices and add to random cache
for i in range(0, core_amount):
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device, name=f"core{i}")
core_devices[core] = randint(0, cache_amount - 1)
cache_devices[core_devices[core]].add_core(core)
# Count expected number of cores per cache
count_dict = {}
for i in range(0, cache_amount):
count_dict[i] = sum(k == i for k in core_devices.values())
# Check if cache statistics are as expected
for i in range(0, cache_amount):
stats = cache_devices[i].get_stats()
assert stats["conf"]["core_count"] == count_dict[i]
@pytest.mark.parametrize("cache_mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_adding_core_twice(pyocf_ctx, cache_mode, cls):
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls)
# Create core device
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device)
# Add core
cache.add_core(core)
# Check that it is not possible to add the same core again
with pytest.raises(OcfError):
cache.add_core(core)
# Check that core count is still equal to one
stats = cache.get_stats()
assert stats["conf"]["core_count"] == 1
@pytest.mark.parametrize("cache_mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_adding_core_already_used(pyocf_ctx, cache_mode, cls):
# Start first cache device
cache_device1 = RamVolume(S.from_MiB(50))
cache1 = Cache.start_on_device(
cache_device1, cache_mode=cache_mode, cache_line_size=cls, name="cache1"
)
# Start second cache device
cache_device2 = RamVolume(S.from_MiB(50))
cache2 = Cache.start_on_device(
cache_device2, cache_mode=cache_mode, cache_line_size=cls, name="cache2"
)
# Create core device
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device)
# Add core to first cache
cache1.add_core(core)
# Check that it is not possible to add core to second cache
with pytest.raises(OcfError):
cache2.add_core(core)
# Check that core count is as expected
stats = cache1.get_stats()
assert stats["conf"]["core_count"] == 1
stats = cache2.get_stats()
assert stats["conf"]["core_count"] == 0
@pytest.mark.parametrize("cache_mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_add_remove_incrementally(pyocf_ctx, cache_mode, cls):
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls)
core_devices = []
core_amount = 5
# Create 5 core devices and add to cache
for i in range(0, core_amount):
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device, name=f"core{i}")
core_devices.append(core)
cache.add_core(core)
# Check that core count is as expected
stats = cache.get_stats()
assert stats["conf"]["core_count"] == core_amount
# Remove 3 cores
cache.remove_core(core_devices[0])
cache.remove_core(core_devices[1])
cache.remove_core(core_devices[2])
# Add 2 cores and check if core count is as expected
cache.add_core(core_devices[0])
cache.add_core(core_devices[1])
stats = cache.get_stats()
assert stats["conf"]["core_count"] == core_amount - 1
# Remove 1 core and check if core count is as expected
cache.remove_core(core_devices[1])
stats = cache.get_stats()
assert stats["conf"]["core_count"] == core_amount - 2
# Add 2 cores and check if core count is as expected
cache.add_core(core_devices[1])
cache.add_core(core_devices[2])
stats = cache.get_stats()
assert stats["conf"]["core_count"] == core_amount
def _io_to_core(vol: Volume, queue: Queue, data: Data):
vol.open()
io = vol.new_io(queue, 0, data.size, IoDir.WRITE, 0, 0)
io.set_data(data)
completion = Sync(io).submit()
vol.close()
assert completion.results["err"] == 0, "IO to exported object completion"
@pytest.mark.parametrize("cache_mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_try_add_core_with_changed_size(pyocf_ctx, cache_mode, cls):
"""
Test changing volume size before load
:param pyocf_ctx: basic pyocf context fixture
:param cm: cache mode we start with
:param cls: cache line size we start with
"""
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls)
# Add core to cache
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device)
cache.add_core(core)
# Stop cache
cache.stop()
# Change core device size
core_device.resize(S.from_MiB(12))
# Load cache with changed core size
cache = Cache.load_from_device(cache_device, open_cores=False)
core = Core(device=core_device)
with pytest.raises(OcfError, match="OCF_ERR_CORE_SIZE_MISMATCH"):
cache.add_core(core, try_add=True)
@pytest.mark.parametrize("cache_mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_load_with_changed_core_size(pyocf_ctx, cache_mode, cls):
"""
Test changing volume size before load
:param pyocf_ctx: basic pyocf context fixture
:param cm: cache mode we start with
:param cls: cache line size we start with
"""
# Start cache device
cache_device = RamVolume(S.from_MiB(50))
cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls)
# Add core to cache
core_device = RamVolume(S.from_MiB(10))
core = Core.using_device(core_device)
cache.add_core(core)
# Stop cache
cache.stop()
# Change core device size
core_device.resize(S.from_MiB(12))
# Load cache with changed core size
with pytest.raises(OcfError, match="OCF_ERR_CORE_SIZE_MISMATCH"):
cache = Cache.load_from_device(cache_device)