Merge pull request #727 from arutk/pyvolume

pyocf: composite volume API and initial tests
This commit is contained in:
Adam Rutkowski 2022-06-14 11:04:29 +02:00 committed by GitHub
commit 3f552703f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 425 additions and 73 deletions

View File

@ -16,6 +16,8 @@
#include "ocf_err.h"
#include "ocf_volume.h"
#define OCF_VOLUME_TYPE_COMPOSITE 10
/**
* @brief handle to object designating composite volume
*/

View File

@ -152,6 +152,11 @@ static void ocf_composite_volume_on_deinit(ocf_volume_t cvolume)
struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume);
int i;
/* priv can be NULL if this volume had been moved from. In this case
* it's the owner responsibility to deinit member volumes. */
if (!composite)
return;
for (i = 0; i < composite->members_cnt; i++)
ocf_volume_deinit(&composite->member[i].volume);
}
@ -282,7 +287,7 @@ static void *ocf_composite_io_allocator_new(ocf_io_allocator_t allocator,
member_addr = cur_addr - (i > 0 ? composite->end_addr[i-1] : 0);
member_bytes =
OCF_MIN(cur_addr + cur_bytes, composite->end_addr[i])
- member_addr;
- cur_addr;
cio->member_io[i] = ocf_io_new(&composite->member[i].volume, queue,
member_addr, member_bytes, dir, 0, 0);

View File

@ -8,6 +8,7 @@
#include "ocf_env.h"
#include "ocf/ocf_ctx.h"
#include "ocf/ocf_composite_volume.h"
#include "ocf_logger_priv.h"
#include "ocf_volume_priv.h"
@ -19,7 +20,11 @@
#define OCF_VOLUME_TYPE_CORE (OCF_VOLUME_TYPE_MAX_USER + 0)
#define OCF_VOLUME_TYPE_CACHE (OCF_VOLUME_TYPE_MAX_USER + 1)
#define OCF_VOLUME_TYPE_COMPOSITE (OCF_VOLUME_TYPE_MAX_USER + 2)
#define OCF_VOLUME_TYPE_COMPOSITE_PLACEHOLDER (OCF_VOLUME_TYPE_MAX_USER + 2)
#if OCF_VOLUME_TYPE_COMPOSITE_PLACEHOLDER != OCF_VOLUME_TYPE_COMPOSITE
#error "composite volume id mismatch"
#endif
/**
* @brief OCF main control structure

View File

@ -327,8 +327,10 @@ int ocf_volume_open(ocf_volume_t volume, void *volume_params)
{
int ret;
if (volume->opened)
return -OCF_ERR_NOT_OPEN_EXC;
ENV_BUG_ON(!volume->type->properties->ops.open);
ENV_BUG_ON(volume->opened);
ret = volume->type->properties->ops.open(volume, volume_params);
if (ret)
@ -352,7 +354,9 @@ void ocf_volume_close(ocf_volume_t volume)
env_completion cmpl;
ENV_BUG_ON(!volume->type->properties->ops.close);
ENV_BUG_ON(!volume->opened);
if (!volume->opened)
return;
env_completion_init(&cmpl);
ocf_refcnt_freeze(&volume->refcnt);

View File

@ -0,0 +1,13 @@
/*
* Copyright(c) 2022-2022 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ocf/ocf_composite_volume.h"
int ocf_get_composite_volume_type_id_helper()
{
return OCF_VOLUME_TYPE_COMPOSITE;
}

View File

@ -34,3 +34,8 @@ def get_metadata_segment_elem_size(cache, segment):
def get_metadata_segment_is_flapped(cache, segment):
lib = OcfLib.getInstance()
return bool(lib.ocf_get_metadata_segment_is_flapped(cache, segment))
def get_composite_volume_type_id():
lib = OcfLib.getInstance()
return int(lib.ocf_get_composite_volume_type_id_helper())

View File

@ -299,7 +299,7 @@ class Cache:
raise OcfError("Failed to detach failover cache device", c.results["error"])
def standby_activate(self, device, open_cores=True):
device_cfg = self.generate_device_config(device)
device_cfg = self.alloc_device_config(device)
activate_cfg = CacheStandbyActivateConfig(_device=device_cfg, _open_cores=open_cores,)
@ -311,6 +311,8 @@ class Cache:
c.wait()
self.write_unlock()
self.free_device_config(device_cfg)
if c.results["error"]:
raise OcfError("Failed to activate standby cache", c.results["error"])
@ -499,7 +501,8 @@ class Cache:
if status:
raise OcfError("Error adding partition to cache", status)
def generate_device_config(self, device, perform_test=True):
def alloc_device_config(self, device, perform_test=True):
if not device.handle:
uuid = Uuid(
_data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p),
_size=len(device.uuid) + 1,
@ -512,8 +515,11 @@ class Cache:
self.owner.ocf_volume_type[type(device)],
byref(uuid)
)
if result != 0:
raise OcfError("Cache volume initialization failed", result)
else:
volume = device.handle
device_config = CacheDeviceConfig(
_volume=volume,
@ -523,13 +529,16 @@ class Cache:
return device_config
def free_device_config(self, cfg):
lib = OcfLib.getInstance().ocf_volume_destroy(cfg._volume)
def attach_device(
self, device, force=False, perform_test=False, cache_line_size=None, open_cores=False,
):
self.device = device
self.device_name = device.uuid
device_config = self.generate_device_config(device, perform_test=perform_test)
device_config = self.alloc_device_config(device, perform_test=perform_test)
attach_cfg = CacheAttachConfig(
_device=device_config,
@ -548,6 +557,8 @@ class Cache:
self.write_unlock()
self.free_device_config(device_config)
if c.results["error"]:
raise OcfError(
f"Attaching cache device failed", c.results["error"],
@ -557,7 +568,7 @@ class Cache:
self.device = device
self.device_name = device.uuid
device_config = self.generate_device_config(device, perform_test=False)
device_config = self.alloc_device_config(device, perform_test=False)
attach_cfg = CacheAttachConfig(
_device=device_config,
@ -576,6 +587,8 @@ class Cache:
self.write_unlock()
self.free_device_config(device_config)
if c.results["error"]:
raise OcfError(
f"Attaching to standby cache failed", c.results["error"],
@ -585,7 +598,7 @@ class Cache:
self.device = device
self.device_name = device.uuid
device_config = self.generate_device_config(device, perform_test=perform_test)
device_config = self.alloc_device_config(device, perform_test=perform_test)
attach_cfg = CacheAttachConfig(
_device=device_config,
@ -601,6 +614,8 @@ class Cache:
c.wait()
self.write_unlock()
self.free_device_config(device_config)
if c.results["error"]:
raise OcfError("Loading standby cache device failed", c.results["error"])
@ -622,7 +637,7 @@ class Cache:
self.device = device
self.device_name = device.uuid
device_config = self.generate_device_config(device)
device_config = self.alloc_device_config(device)
attach_cfg = CacheAttachConfig(
_device=device_config,
@ -638,6 +653,8 @@ class Cache:
c.wait()
self.write_unlock()
self.free_device_config(device_config)
if c.results["error"]:
raise OcfError("Loading cache device failed", c.results["error"])
@ -759,10 +776,16 @@ class Cache:
self.cores.remove(core)
def get_front_volume(self):
return Volume.get_instance(lib.ocf_cache_get_front_volume(self.cache_handle))
return Volume.get_instance(self.get_c_front_volume())
def get_c_front_volume(self):
return lib.ocf_cache_get_front_volume(self.cache_handle)
def get_volume(self):
return Volume.get_instance(lib.ocf_cache_get_volume(self.cache_handle))
return Volume.get_instance(self.get_c_volume())
def get_c_volume(self):
return lib.ocf_cache_get_volume(self.cache_handle)
def get_conf(self):
cache_info = CacheInfo()
@ -971,3 +994,4 @@ lib.ocf_mngt_cache_io_classes_configure.restype = c_int
lib.ocf_mngt_cache_io_classes_configure.argtypes = [c_void_p, c_void_p]
lib.ocf_volume_create.restype = c_int
lib.ocf_volume_create.argtypes = [c_void_p, c_void_p, c_void_p]
lib.ocf_volume_destroy.argtypes = [c_void_p]

View File

@ -97,10 +97,16 @@ class Core:
return self.handle
def get_front_volume(self):
return Volume.get_instance(lib.ocf_core_get_front_volume(self.handle))
return Volume.get_instance(self.get_c_front_volume())
def get_c_front_volume(self):
return lib.ocf_core_get_front_volume(self.handle)
def get_volume(self):
return Volume.get_instance(lib.ocf_core_get_volume(self.handle))
return Volume.get_instance(self.get_c_volume())
def get_c_volume(self):
return lib.ocf_core_get_volume(self.handle)
def get_default_queue(self):
return self.cache.get_default_queue()

View File

@ -66,7 +66,18 @@ class OcfCtx:
return cls.default()
def register_internal_volume_type_id(self, volume_type, volume_type_id):
if volume_type_id in self.volume_types:
raise RuntimeError(f"volume type id {volume_type_id} already used")
self.volume_types[volume_type_id] = volume_type
volume_type.internal = True
def register_volume_type(self, volume_type):
if self.volume_types_count in self.volume_types:
raise RuntimeError(
f"volume type id slot already used by internal volume "
f"{self.volume_types[self.volume_types_count]}"
)
self.volume_types[self.volume_types_count] = volume_type
volume_type.type_id = self.volume_types_count
@ -79,10 +90,11 @@ class OcfCtx:
raise OcfError("Volume type registration failed", result)
self.ocf_volume_type[volume_type] = self.lib.ocf_ctx_get_volume_type(
self.ctx_handle,
volume_type.type_id
self.ctx_handle, volume_type.type_id
)
volume_type.internal = False
self.volume_types_count += 1
def unregister_volume_type(self, vol_type):
@ -96,7 +108,7 @@ class OcfCtx:
def cleanup_volume_types(self):
for k, vol_type in list(self.volume_types.items()):
if vol_type:
if vol_type and not vol_type.internal:
self.unregister_volume_type(vol_type)
def stop_caches(self):

View File

@ -0,0 +1,78 @@
#
# Copyright(c) 2022 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause
#
from ctypes import (
c_int,
c_uint32,
c_uint64,
c_void_p,
c_char_p,
byref,
cast,
create_string_buffer,
)
from ..ocf import OcfLib
from .ctx import OcfCtx
from .io import Io, IoDir
from .queue import Queue
from .shared import OcfError, Uuid
from .volume_exp_obj import OcfInternalVolume
class CVolume(OcfInternalVolume):
def __init__(self, ctx):
super().__init__(None)
self.ctx = ctx
self.lib = ctx.lib
self.cvol = c_void_p()
ret = lib.ocf_composite_volume_create(byref(self.cvol), self.ctx.ctx_handle)
if ret != 0:
raise OcfError("Composite volume creation failed", ret)
self.handle = self.cvol.value
def destroy(self):
self.lib.ocf_composite_volume_destroy(self.cvol)
self.cvol = None
self.handle = 0
def add(self, vol):
uuid = Uuid(
_data=cast(create_string_buffer(vol.uuid.encode("ascii")), c_char_p),
_size=len(vol.uuid) + 1,
)
volume = c_void_p()
ocf_vol_type = self.ctx.ocf_volume_type[type(vol)]
ret = self.lib.ocf_composite_volume_add(self.cvol, ocf_vol_type, byref(uuid), c_void_p())
if ret != 0:
raise OcfError("Failed to add volume to a composite volume", ret)
def get_c_handle(self):
return self.cvol.value
def do_open(self):
ret = self.lib.ocf_volume_open(self.cvol, c_void_p())
if ret != 0:
raise OcfError("openning composite volume failed", ret)
def do_close(self):
self.lib.ocf_volume_close(self.cvol)
lib = OcfLib.getInstance()
lib.ocf_composite_volume_create.restype = c_int
lib.ocf_composite_volume_create.argtypes = [c_void_p, c_void_p]
lib.ocf_composite_volume_destroy.argtypes = [c_void_p]
lib.ocf_composite_volume_add.argtypes = [c_void_p, c_void_p, c_void_p, c_void_p]
lib.ocf_composite_volume_add.restype = c_int
lib.ocf_volume_open.restype = c_int
lib.ocf_volume_open.argtypes = [c_void_p, c_void_p]
lib.ocf_volume_close.argtypes = [c_void_p]

View File

@ -147,13 +147,12 @@ class Volume:
print("{}".format(Volume._uuid_))
return -1
return Volume.open(ref, volume)
return Volume.s_open(ref, volume)
@VolumeOps.CLOSE
def _close(ref):
volume = Volume.get_instance(ref)
volume.close()
volume.opened = False
Volume.s_close(volume)
@VolumeOps.GET_MAX_IO_SIZE
def _get_max_io_size(ref):
@ -180,14 +179,29 @@ class Volume:
return Volume._ops_[cls]
@staticmethod
def open(ref, volume):
def s_open(ref, volume):
if volume.opened:
return -OcfErrorCode.OCF_ERR_NOT_OPEN_EXC
Volume._instances_[ref] = volume
volume.handle = ref
return volume.do_open()
ret = volume.do_open()
if ret == 0:
volume.opened = True
return ret
@staticmethod
def s_close(volume):
if not volume.opened:
return
volume.do_close()
volume.opened = False
del Volume._instances_[volume.handle]
volume.handle = None
@classmethod
def get_io_ops(cls):
@ -253,13 +267,13 @@ class Volume:
self.reset_stats()
self.is_online = True
self.opened = False
self.handle = None
def do_open(self):
self.opened = True
return 0
def close(self):
self.opened = False
def do_close(self):
pass
def get_length(self):
raise NotImplementedError
@ -324,7 +338,13 @@ class Volume:
):
lib = OcfLib.getInstance()
io = lib.ocf_volume_new_io(
self.handle, queue.handle, addr, length, direction, io_class, flags
self.handle,
queue.handle if queue else c_void_p(),
addr,
length,
direction,
io_class,
flags,
)
return Io.from_pointer(io)
@ -518,15 +538,18 @@ class TraceDevice(Volume):
if submit:
self.vol.do_submit_flush(io)
def do_submit_discard(self, io):
submit = self._trace(io, TraceDevice.IoType.Discard)
if submit:
self.vol.do_submit_discard(io)
def get_length(self):
return self.vol.get_length()
def get_max_io_size(self):
return self.vol.get_max_io_size()
def do_submit_discard(self, discard):
return self.vol.do_submit_discard(discard)
def dump(self, offset=0, size=0, ignore=VOLUME_POISON, **kwargs):
return self.vol.dump(offset, size, ignore=ignore, **kwargs)

View File

@ -8,11 +8,11 @@ from ctypes import cast, POINTER
from .cache import Cache
from .io import Io
from .io import IoDir
from .volume_exp_obj import ExpObjVolume
from .volume_exp_obj import OcfInternalVolume
from .volume import Volume
class CacheVolume(ExpObjVolume):
class CacheVolume(OcfInternalVolume):
def __init__(self, cache, open=False, uuid=None):
super().__init__(cache, uuid)
self.cache = cache
@ -20,8 +20,8 @@ class CacheVolume(ExpObjVolume):
if open:
self.open()
def open(self):
return Volume.open(self.lib.ocf_cache_get_front_volume(self.cache.cache_handle), self)
def get_c_handle(self):
return self.cache.get_c_front_volume()
def md5(self):
out = self.cache.get_conf()

View File

@ -4,12 +4,12 @@
#
from .core import Core
from .volume_exp_obj import ExpObjVolume
from .volume_exp_obj import OcfInternalVolume
from .io import IoDir
from .volume import Volume
class CoreVolume(ExpObjVolume):
class CoreVolume(OcfInternalVolume):
def __init__(self, core, open=False, uuid=None):
super().__init__(core, uuid)
self.core = core
@ -17,8 +17,8 @@ class CoreVolume(ExpObjVolume):
if open:
self.open()
def open(self):
return Volume.open(self.lib.ocf_core_get_front_volume(self.core.handle), self)
def get_c_handle(self):
return self.core.get_c_front_volume()
def md5(self):
return self._exp_obj_md5(4096)

View File

@ -14,7 +14,7 @@ from pyocf.types.io import IoDir, Io
from pyocf.types.shared import OcfCompletion
class ExpObjVolume(Volume):
class OcfInternalVolume(Volume):
def __init__(self, parent, uuid=None):
super().__init__(uuid)
self.parent = parent
@ -113,6 +113,13 @@ class ExpObjVolume(Volume):
return read_buffer_all.md5()
def open(self):
handle = self.get_c_handle()
return Volume.s_open(handle, self)
def close(self):
return Volume.s_close(self)
lib = OcfLib.getInstance()
lib.ocf_volume_get_max_io_size.argtypes = [c_void_p]

View File

@ -14,7 +14,9 @@ from pyocf.types.volume import RamVolume, ErrorDevice
from pyocf.types.volume_cache import CacheVolume
from pyocf.types.volume_core import CoreVolume
from pyocf.types.volume_replicated import ReplicatedVolume
from pyocf.types.cvolume import CVolume
from pyocf.types.ctx import OcfCtx
from pyocf.helpers import get_composite_volume_type_id
default_registered_volumes = [RamVolume, ErrorDevice, CacheVolume, CoreVolume, ReplicatedVolume]
@ -28,6 +30,7 @@ def pyocf_ctx():
c = OcfCtx.with_defaults(DefaultLogger(LogLevel.WARN))
for vol_type in default_registered_volumes:
c.register_volume_type(vol_type)
c.register_internal_volume_type_id(CVolume, get_composite_volume_type_id())
yield c
c.exit()
gc.collect()
@ -39,6 +42,7 @@ def pyocf_ctx_log_buffer():
c = OcfCtx.with_defaults(logger)
for vol_type in default_registered_volumes:
c.register_volume_type(vol_type)
c.register_internal_volume_type_id(CVolume, get_composite_volume_type_id())
yield logger
c.exit()
gc.collect()
@ -51,6 +55,8 @@ def pyocf_2_ctx():
for vol_type in default_registered_volumes:
c1.register_volume_type(vol_type)
c2.register_volume_type(vol_type)
c1.register_internal_volume_type_id(CVolume, get_composite_volume_type_id())
c2.register_internal_volume_type_id(CVolume, get_composite_volume_type_id())
yield [c1, c2]
c1.exit()
c2.exit()

View File

@ -34,10 +34,11 @@ def test_test_standby_io(pyocf_ctx, cacheline_size):
cache.add_io_queue(f"io-queue-{i}")
cache.standby_attach(cache_vol)
cache_vol = CacheVolume(cache, open=True)
r = (
Rio()
.target(cache)
.target(cache_vol)
.njobs(num_jobs)
.readwrite(ReadWrite.RANDWRITE)
.size(vol_size)

View File

@ -4,9 +4,16 @@
#
import pytest
from ctypes import c_int
from pyocf.types.volume import RamVolume, ErrorDevice, TraceDevice, IoFlags
from pyocf.types.cvolume import CVolume
from pyocf.types.data import Data
from pyocf.types.io import IoDir
from pyocf.types.shared import OcfError, OcfCompletion
from pyocf.utils import Size as S
@pytest.mark.skip(reason="not implemented")
def test_create_composite_volume(pyocf_ctx):
"""
title: Create composite volume.
@ -26,10 +33,12 @@ def test_create_composite_volume(pyocf_ctx):
- composite_volume::creation
- composite_volume::adding_component_volume
"""
pass
cvol = CVolume(pyocf_ctx)
vol = RamVolume(S.from_MiB(1))
cvol.add(vol)
cvol.destroy()
@pytest.mark.skip(reason="not implemented")
def test_add_subvolumes_of_different_types(pyocf_ctx):
"""
title: Add subvolumes of different types.
@ -49,10 +58,16 @@ def test_add_subvolumes_of_different_types(pyocf_ctx):
requirements:
- composite_volume::component_volume_types
"""
pass
vol1 = RamVolume(S.from_MiB(1))
vol2_backend = RamVolume(S.from_MiB(1))
vol2 = ErrorDevice(vol2_backend)
cvol = CVolume(pyocf_ctx)
cvol.add(vol1)
cvol.add(vol2)
cvol.destroy()
@pytest.mark.skip(reason="not implemented")
def test_add_max_subvolumes(pyocf_ctx):
"""
title: Add maximum number of subvolumes.
@ -69,10 +84,36 @@ def test_add_max_subvolumes(pyocf_ctx):
requirements:
- composite_volume::max_composite_volumes
"""
pass
cvol = CVolume(pyocf_ctx)
for i in range(16):
vol = RamVolume(S.from_MiB(1))
cvol.add(vol)
vol = RamVolume(S.from_MiB(1))
with pytest.raises(OcfError):
cvol.add(vol)
cvol.destroy()
def _cvol_io(cvol, addr, size, func, flags=0):
io = cvol.new_io(
queue=None, addr=addr, length=size, direction=IoDir.WRITE, io_class=0, flags=flags,
)
completion = OcfCompletion([("err", c_int)])
io.callback = completion.callback
data = Data(byte_count=size)
io.set_data(data, 0)
submit_fn = getattr(io, func)
submit_fn()
completion.wait()
return int(completion.results["err"])
@pytest.mark.skip(reason="not implemented")
def test_basic_volume_operations(pyocf_ctx):
"""
title: Perform basic volume operations.
@ -93,7 +134,56 @@ def test_basic_volume_operations(pyocf_ctx):
- composite_volume::volume_api
- composite_volume::io_request_passing
"""
pass
count = {"flush": 0, "discard": 0, "io": 0}
expected = {"flush": 0, "discard": 0, "io": 0}
pyocf_ctx.register_volume_type(TraceDevice)
addr = S.from_KiB(512).B
size = S.from_KiB(4).B
def trace(vol, io, io_type):
if io_type == TraceDevice.IoType.Flush or int(io.contents._flags) & IoFlags.FLUSH:
count["flush"] += 1
elif io_type == TraceDevice.IoType.Discard:
count["discard"] += 1
else:
assert io_type == TraceDevice.IoType.Data
count["io"] += 1
assert io.contents._dir == IoDir.WRITE
assert io.contents._addr == addr
assert io.contents._bytes == size
return True
backend = RamVolume(S.from_MiB(1))
trace_dev = TraceDevice(backend, trace_fcn=trace)
cvol = CVolume(pyocf_ctx)
cvol.add(trace_dev)
cvol.open()
# verify data properly propagated
ret = _cvol_io(cvol, addr, size, "submit")
assert ret == 0
expected["io"] += 1
assert expected == count
# verify flush properly propagated
ret = _cvol_io(cvol, addr, size, "submit_flush", IoFlags.FLUSH)
assert ret == 0
expected["flush"] += 1
assert expected == count
# verify discard properly propagated
ret = _cvol_io(cvol, addr, size, "submit_discard")
assert ret == 0
expected["discard"] += 1
assert expected == count
cvol.close()
cvol.destroy()
@pytest.mark.skip(reason="not implemented")

View File

@ -4,7 +4,15 @@
#
import logging
from ctypes import c_int, c_void_p, byref, c_uint32
from ctypes import (
c_int,
c_void_p,
byref,
c_uint32,
cast,
create_string_buffer,
c_char_p,
)
from random import randrange
from itertools import count
@ -19,13 +27,22 @@ from pyocf.types.cache import (
CacheConfig,
PromotionPolicy,
Backfill,
CacheDeviceConfig,
CacheAttachConfig,
)
from pyocf.types.core import Core
from pyocf.types.ctx import OcfCtx
from pyocf.types.data import Data
from pyocf.types.io import IoDir
from pyocf.types.queue import Queue
from pyocf.types.shared import OcfError, OcfCompletion, CacheLineSize, SeqCutOffPolicy
from pyocf.types.shared import (
Uuid,
OcfError,
OcfErrorCode,
OcfCompletion,
CacheLineSize,
SeqCutOffPolicy,
)
from pyocf.types.volume import Volume, RamVolume
from pyocf.types.volume_core import CoreVolume
from pyocf.utils import Size
@ -234,7 +251,7 @@ def test_start_stop_multiple(pyocf_ctx):
cache_line_size = CacheLineSize(size)
cache = Cache.start_on_device(
cache_device, name=cache_name, cache_mode=cache_mode, cache_line_size=cache_line_size
cache_device, name=cache_name, cache_mode=cache_mode, cache_line_size=cache_line_size,
)
caches.append(cache)
stats = cache.get_stats()
@ -264,7 +281,7 @@ def test_100_start_stop(pyocf_ctx):
cache_line_size = CacheLineSize(size)
cache = Cache.start_on_device(
cache_device, name=cache_name, cache_mode=cache_mode, cache_line_size=cache_line_size
cache_device, name=cache_name, cache_mode=cache_mode, cache_line_size=cache_line_size,
)
stats = cache.get_stats()
assert stats["conf"]["cache_mode"] == cache_mode, "Cache mode"
@ -378,18 +395,72 @@ def test_start_cache_huge_device(pyocf_ctx_log_buffer, cls):
@pytest.mark.parametrize("cls", CacheLineSize)
def test_start_cache_same_device(pyocf_ctx, mode, cls):
"""Adding two caches using the same cache device
Check that OCF does not allow for 2 caches using the same cache device to be started
Check that OCF does not allow for 2 caches using the same cache device to be started.
Low level OCF API is used for attach instead of Cache::attach_device as the latter operates
on pyocf Volume objects and this test requires explicit construction of two volumes with
identical UUID. Pyocf does not allow for two Volume objects with the same UUID, as these
represent a resource that should be uniquely identified by UUID. So we need to create
two distinct OCF volumes with identical UUID and pass them to OCF cache attach method.
"""
_uuid = "cache_dev"
cache_device = RamVolume(Size.from_MiB(50))
cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls, name="cache1")
cache.get_stats()
cache_device = RamVolume(Size.from_MiB(50), uuid=_uuid)
with pytest.raises(OcfError, match="OCF_ERR_NOT_OPEN_EXC"):
cache = Cache.start_on_device(
cache_device, cache_mode=mode, cache_line_size=cls, name="cache2"
uuid = Uuid(
_data=cast(create_string_buffer(_uuid.encode("ascii")), c_char_p), _size=len(_uuid) + 1,
)
cache.get_stats()
lib = OcfLib.getInstance()
vol1 = c_void_p()
vol2 = c_void_p()
result = lib.ocf_volume_create(byref(vol1), pyocf_ctx.ocf_volume_type[RamVolume], byref(uuid))
assert result == 0
result = lib.ocf_volume_create(byref(vol2), pyocf_ctx.ocf_volume_type[RamVolume], byref(uuid))
assert result == 0
dev_cfg = CacheDeviceConfig(_volume=vol1, _perform_test=False, _volume_params=None)
attach_cfg = CacheAttachConfig(
_device=dev_cfg,
_cache_line_size=cls,
_open_cores=True,
_force=False,
_discard_on_start=False,
)
# start first cache instance
cache1 = Cache(pyocf_ctx, cache_mode=mode, cache_line_size=cls, name="cache1")
cache1.start_cache()
cache1.write_lock()
c = OcfCompletion([("cache", c_void_p), ("priv", c_void_p), ("error", c_int)])
lib.ocf_mngt_cache_attach(cache1.cache_handle, byref(attach_cfg), c, None)
c.wait()
cache1.write_unlock()
assert not c.results["error"]
# attempt to start second cache instance on a volume with the same UUID
attach_cfg._device._volume = vol2
cache2 = Cache(pyocf_ctx, cache_mode=mode, cache_line_size=cls, name="cache2")
cache2.start_cache()
cache2.write_lock()
c = OcfCompletion([("cache", c_void_p), ("priv", c_void_p), ("error", c_int)])
lib.ocf_mngt_cache_attach(cache2.cache_handle, byref(attach_cfg), c, None)
c.wait()
cache2.write_unlock()
assert c.results["error"]
error_code = OcfErrorCode(abs(c.results["error"]))
assert error_code == OcfErrorCode.OCF_ERR_NOT_OPEN_EXC
cache1.stop()
cache2.stop()
lib = OcfLib.getInstance().ocf_volume_destroy(vol1)
lib = OcfLib.getInstance().ocf_volume_destroy(vol2)
del cache_device
@pytest.mark.parametrize("mode", CacheMode)