Merge pull request #727 from arutk/pyvolume
pyocf: composite volume API and initial tests
This commit is contained in:
commit
3f552703f5
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
13
tests/functional/pyocf/c/helpers/volume_type.c
Normal file
13
tests/functional/pyocf/c/helpers/volume_type.c
Normal 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;
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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]
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
|
78
tests/functional/pyocf/types/cvolume.py
Normal file
78
tests/functional/pyocf/types/cvolume.py
Normal 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]
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user