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_err.h"
#include "ocf_volume.h" #include "ocf_volume.h"
#define OCF_VOLUME_TYPE_COMPOSITE 10
/** /**
* @brief handle to object designating composite volume * @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); struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume);
int i; 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++) for (i = 0; i < composite->members_cnt; i++)
ocf_volume_deinit(&composite->member[i].volume); 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_addr = cur_addr - (i > 0 ? composite->end_addr[i-1] : 0);
member_bytes = member_bytes =
OCF_MIN(cur_addr + cur_bytes, composite->end_addr[i]) 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, cio->member_io[i] = ocf_io_new(&composite->member[i].volume, queue,
member_addr, member_bytes, dir, 0, 0); member_addr, member_bytes, dir, 0, 0);

View File

@ -8,6 +8,7 @@
#include "ocf_env.h" #include "ocf_env.h"
#include "ocf/ocf_ctx.h" #include "ocf/ocf_ctx.h"
#include "ocf/ocf_composite_volume.h"
#include "ocf_logger_priv.h" #include "ocf_logger_priv.h"
#include "ocf_volume_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_CORE (OCF_VOLUME_TYPE_MAX_USER + 0)
#define OCF_VOLUME_TYPE_CACHE (OCF_VOLUME_TYPE_MAX_USER + 1) #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 * @brief OCF main control structure

View File

@ -327,8 +327,10 @@ int ocf_volume_open(ocf_volume_t volume, void *volume_params)
{ {
int ret; int ret;
if (volume->opened)
return -OCF_ERR_NOT_OPEN_EXC;
ENV_BUG_ON(!volume->type->properties->ops.open); ENV_BUG_ON(!volume->type->properties->ops.open);
ENV_BUG_ON(volume->opened);
ret = volume->type->properties->ops.open(volume, volume_params); ret = volume->type->properties->ops.open(volume, volume_params);
if (ret) if (ret)
@ -352,7 +354,9 @@ void ocf_volume_close(ocf_volume_t volume)
env_completion cmpl; env_completion cmpl;
ENV_BUG_ON(!volume->type->properties->ops.close); ENV_BUG_ON(!volume->type->properties->ops.close);
ENV_BUG_ON(!volume->opened);
if (!volume->opened)
return;
env_completion_init(&cmpl); env_completion_init(&cmpl);
ocf_refcnt_freeze(&volume->refcnt); 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): def get_metadata_segment_is_flapped(cache, segment):
lib = OcfLib.getInstance() lib = OcfLib.getInstance()
return bool(lib.ocf_get_metadata_segment_is_flapped(cache, segment)) 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"]) raise OcfError("Failed to detach failover cache device", c.results["error"])
def standby_activate(self, device, open_cores=True): 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,) activate_cfg = CacheStandbyActivateConfig(_device=device_cfg, _open_cores=open_cores,)
@ -311,6 +311,8 @@ class Cache:
c.wait() c.wait()
self.write_unlock() self.write_unlock()
self.free_device_config(device_cfg)
if c.results["error"]: if c.results["error"]:
raise OcfError("Failed to activate standby cache", c.results["error"]) raise OcfError("Failed to activate standby cache", c.results["error"])
@ -499,21 +501,25 @@ class Cache:
if status: if status:
raise OcfError("Error adding partition to cache", 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):
uuid = Uuid( if not device.handle:
_data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p), uuid = Uuid(
_size=len(device.uuid) + 1, _data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p),
) _size=len(device.uuid) + 1,
volume = c_void_p() )
volume = c_void_p()
lib = OcfLib.getInstance() lib = OcfLib.getInstance()
result = lib.ocf_volume_create( result = lib.ocf_volume_create(
byref(volume), byref(volume),
self.owner.ocf_volume_type[type(device)], self.owner.ocf_volume_type[type(device)],
byref(uuid) byref(uuid)
) )
if result != 0:
raise OcfError("Cache volume initialization failed", result) if result != 0:
raise OcfError("Cache volume initialization failed", result)
else:
volume = device.handle
device_config = CacheDeviceConfig( device_config = CacheDeviceConfig(
_volume=volume, _volume=volume,
@ -523,13 +529,16 @@ class Cache:
return device_config return device_config
def free_device_config(self, cfg):
lib = OcfLib.getInstance().ocf_volume_destroy(cfg._volume)
def attach_device( def attach_device(
self, device, force=False, perform_test=False, cache_line_size=None, open_cores=False, self, device, force=False, perform_test=False, cache_line_size=None, open_cores=False,
): ):
self.device = device self.device = device
self.device_name = device.uuid 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( attach_cfg = CacheAttachConfig(
_device=device_config, _device=device_config,
@ -548,6 +557,8 @@ class Cache:
self.write_unlock() self.write_unlock()
self.free_device_config(device_config)
if c.results["error"]: if c.results["error"]:
raise OcfError( raise OcfError(
f"Attaching cache device failed", c.results["error"], f"Attaching cache device failed", c.results["error"],
@ -557,7 +568,7 @@ class Cache:
self.device = device self.device = device
self.device_name = device.uuid 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( attach_cfg = CacheAttachConfig(
_device=device_config, _device=device_config,
@ -576,6 +587,8 @@ class Cache:
self.write_unlock() self.write_unlock()
self.free_device_config(device_config)
if c.results["error"]: if c.results["error"]:
raise OcfError( raise OcfError(
f"Attaching to standby cache failed", c.results["error"], f"Attaching to standby cache failed", c.results["error"],
@ -585,7 +598,7 @@ class Cache:
self.device = device self.device = device
self.device_name = device.uuid 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( attach_cfg = CacheAttachConfig(
_device=device_config, _device=device_config,
@ -601,6 +614,8 @@ class Cache:
c.wait() c.wait()
self.write_unlock() self.write_unlock()
self.free_device_config(device_config)
if c.results["error"]: if c.results["error"]:
raise OcfError("Loading standby cache device failed", c.results["error"]) raise OcfError("Loading standby cache device failed", c.results["error"])
@ -622,7 +637,7 @@ class Cache:
self.device = device self.device = device
self.device_name = device.uuid self.device_name = device.uuid
device_config = self.generate_device_config(device) device_config = self.alloc_device_config(device)
attach_cfg = CacheAttachConfig( attach_cfg = CacheAttachConfig(
_device=device_config, _device=device_config,
@ -638,6 +653,8 @@ class Cache:
c.wait() c.wait()
self.write_unlock() self.write_unlock()
self.free_device_config(device_config)
if c.results["error"]: if c.results["error"]:
raise OcfError("Loading cache device failed", c.results["error"]) raise OcfError("Loading cache device failed", c.results["error"])
@ -759,10 +776,16 @@ class Cache:
self.cores.remove(core) self.cores.remove(core)
def get_front_volume(self): 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): 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): def get_conf(self):
cache_info = CacheInfo() 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_mngt_cache_io_classes_configure.argtypes = [c_void_p, c_void_p]
lib.ocf_volume_create.restype = c_int lib.ocf_volume_create.restype = c_int
lib.ocf_volume_create.argtypes = [c_void_p, c_void_p, c_void_p] 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 return self.handle
def get_front_volume(self): 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): 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): def get_default_queue(self):
return self.cache.get_default_queue() return self.cache.get_default_queue()

View File

@ -66,7 +66,18 @@ class OcfCtx:
return cls.default() 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): 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 self.volume_types[self.volume_types_count] = volume_type
volume_type.type_id = self.volume_types_count volume_type.type_id = self.volume_types_count
@ -79,10 +90,11 @@ class OcfCtx:
raise OcfError("Volume type registration failed", result) raise OcfError("Volume type registration failed", result)
self.ocf_volume_type[volume_type] = self.lib.ocf_ctx_get_volume_type( self.ocf_volume_type[volume_type] = self.lib.ocf_ctx_get_volume_type(
self.ctx_handle, self.ctx_handle, volume_type.type_id
volume_type.type_id
) )
volume_type.internal = False
self.volume_types_count += 1 self.volume_types_count += 1
def unregister_volume_type(self, vol_type): def unregister_volume_type(self, vol_type):
@ -96,7 +108,7 @@ class OcfCtx:
def cleanup_volume_types(self): def cleanup_volume_types(self):
for k, vol_type in list(self.volume_types.items()): 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) self.unregister_volume_type(vol_type)
def stop_caches(self): 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_)) print("{}".format(Volume._uuid_))
return -1 return -1
return Volume.open(ref, volume) return Volume.s_open(ref, volume)
@VolumeOps.CLOSE @VolumeOps.CLOSE
def _close(ref): def _close(ref):
volume = Volume.get_instance(ref) volume = Volume.get_instance(ref)
volume.close() Volume.s_close(volume)
volume.opened = False
@VolumeOps.GET_MAX_IO_SIZE @VolumeOps.GET_MAX_IO_SIZE
def _get_max_io_size(ref): def _get_max_io_size(ref):
@ -180,14 +179,29 @@ class Volume:
return Volume._ops_[cls] return Volume._ops_[cls]
@staticmethod @staticmethod
def open(ref, volume): def s_open(ref, volume):
if volume.opened: if volume.opened:
return -OcfErrorCode.OCF_ERR_NOT_OPEN_EXC return -OcfErrorCode.OCF_ERR_NOT_OPEN_EXC
Volume._instances_[ref] = volume Volume._instances_[ref] = volume
volume.handle = ref 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 @classmethod
def get_io_ops(cls): def get_io_ops(cls):
@ -253,13 +267,13 @@ class Volume:
self.reset_stats() self.reset_stats()
self.is_online = True self.is_online = True
self.opened = False self.opened = False
self.handle = None
def do_open(self): def do_open(self):
self.opened = True
return 0 return 0
def close(self): def do_close(self):
self.opened = False pass
def get_length(self): def get_length(self):
raise NotImplementedError raise NotImplementedError
@ -324,7 +338,13 @@ class Volume:
): ):
lib = OcfLib.getInstance() lib = OcfLib.getInstance()
io = lib.ocf_volume_new_io( 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) return Io.from_pointer(io)
@ -518,15 +538,18 @@ class TraceDevice(Volume):
if submit: if submit:
self.vol.do_submit_flush(io) 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): def get_length(self):
return self.vol.get_length() return self.vol.get_length()
def get_max_io_size(self): def get_max_io_size(self):
return self.vol.get_max_io_size() 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): def dump(self, offset=0, size=0, ignore=VOLUME_POISON, **kwargs):
return self.vol.dump(offset, size, ignore=ignore, **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 .cache import Cache
from .io import Io from .io import Io
from .io import IoDir from .io import IoDir
from .volume_exp_obj import ExpObjVolume from .volume_exp_obj import OcfInternalVolume
from .volume import Volume from .volume import Volume
class CacheVolume(ExpObjVolume): class CacheVolume(OcfInternalVolume):
def __init__(self, cache, open=False, uuid=None): def __init__(self, cache, open=False, uuid=None):
super().__init__(cache, uuid) super().__init__(cache, uuid)
self.cache = cache self.cache = cache
@ -20,8 +20,8 @@ class CacheVolume(ExpObjVolume):
if open: if open:
self.open() self.open()
def open(self): def get_c_handle(self):
return Volume.open(self.lib.ocf_cache_get_front_volume(self.cache.cache_handle), self) return self.cache.get_c_front_volume()
def md5(self): def md5(self):
out = self.cache.get_conf() out = self.cache.get_conf()

View File

@ -4,12 +4,12 @@
# #
from .core import Core from .core import Core
from .volume_exp_obj import ExpObjVolume from .volume_exp_obj import OcfInternalVolume
from .io import IoDir from .io import IoDir
from .volume import Volume from .volume import Volume
class CoreVolume(ExpObjVolume): class CoreVolume(OcfInternalVolume):
def __init__(self, core, open=False, uuid=None): def __init__(self, core, open=False, uuid=None):
super().__init__(core, uuid) super().__init__(core, uuid)
self.core = core self.core = core
@ -17,8 +17,8 @@ class CoreVolume(ExpObjVolume):
if open: if open:
self.open() self.open()
def open(self): def get_c_handle(self):
return Volume.open(self.lib.ocf_core_get_front_volume(self.core.handle), self) return self.core.get_c_front_volume()
def md5(self): def md5(self):
return self._exp_obj_md5(4096) 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 from pyocf.types.shared import OcfCompletion
class ExpObjVolume(Volume): class OcfInternalVolume(Volume):
def __init__(self, parent, uuid=None): def __init__(self, parent, uuid=None):
super().__init__(uuid) super().__init__(uuid)
self.parent = parent self.parent = parent
@ -113,6 +113,13 @@ class ExpObjVolume(Volume):
return read_buffer_all.md5() 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 = OcfLib.getInstance()
lib.ocf_volume_get_max_io_size.argtypes = [c_void_p] 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_cache import CacheVolume
from pyocf.types.volume_core import CoreVolume from pyocf.types.volume_core import CoreVolume
from pyocf.types.volume_replicated import ReplicatedVolume from pyocf.types.volume_replicated import ReplicatedVolume
from pyocf.types.cvolume import CVolume
from pyocf.types.ctx import OcfCtx from pyocf.types.ctx import OcfCtx
from pyocf.helpers import get_composite_volume_type_id
default_registered_volumes = [RamVolume, ErrorDevice, CacheVolume, CoreVolume, ReplicatedVolume] default_registered_volumes = [RamVolume, ErrorDevice, CacheVolume, CoreVolume, ReplicatedVolume]
@ -28,6 +30,7 @@ def pyocf_ctx():
c = OcfCtx.with_defaults(DefaultLogger(LogLevel.WARN)) c = OcfCtx.with_defaults(DefaultLogger(LogLevel.WARN))
for vol_type in default_registered_volumes: for vol_type in default_registered_volumes:
c.register_volume_type(vol_type) c.register_volume_type(vol_type)
c.register_internal_volume_type_id(CVolume, get_composite_volume_type_id())
yield c yield c
c.exit() c.exit()
gc.collect() gc.collect()
@ -39,6 +42,7 @@ def pyocf_ctx_log_buffer():
c = OcfCtx.with_defaults(logger) c = OcfCtx.with_defaults(logger)
for vol_type in default_registered_volumes: for vol_type in default_registered_volumes:
c.register_volume_type(vol_type) c.register_volume_type(vol_type)
c.register_internal_volume_type_id(CVolume, get_composite_volume_type_id())
yield logger yield logger
c.exit() c.exit()
gc.collect() gc.collect()
@ -51,6 +55,8 @@ def pyocf_2_ctx():
for vol_type in default_registered_volumes: for vol_type in default_registered_volumes:
c1.register_volume_type(vol_type) c1.register_volume_type(vol_type)
c2.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] yield [c1, c2]
c1.exit() c1.exit()
c2.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.add_io_queue(f"io-queue-{i}")
cache.standby_attach(cache_vol) cache.standby_attach(cache_vol)
cache_vol = CacheVolume(cache, open=True)
r = ( r = (
Rio() Rio()
.target(cache) .target(cache_vol)
.njobs(num_jobs) .njobs(num_jobs)
.readwrite(ReadWrite.RANDWRITE) .readwrite(ReadWrite.RANDWRITE)
.size(vol_size) .size(vol_size)

View File

@ -4,9 +4,16 @@
# #
import pytest 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): def test_create_composite_volume(pyocf_ctx):
""" """
title: Create composite volume. title: Create composite volume.
@ -26,10 +33,12 @@ def test_create_composite_volume(pyocf_ctx):
- composite_volume::creation - composite_volume::creation
- composite_volume::adding_component_volume - 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): def test_add_subvolumes_of_different_types(pyocf_ctx):
""" """
title: Add subvolumes of different types. title: Add subvolumes of different types.
@ -49,10 +58,16 @@ def test_add_subvolumes_of_different_types(pyocf_ctx):
requirements: requirements:
- composite_volume::component_volume_types - 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): def test_add_max_subvolumes(pyocf_ctx):
""" """
title: Add maximum number of subvolumes. title: Add maximum number of subvolumes.
@ -69,10 +84,36 @@ def test_add_max_subvolumes(pyocf_ctx):
requirements: requirements:
- composite_volume::max_composite_volumes - 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): def test_basic_volume_operations(pyocf_ctx):
""" """
title: Perform basic volume operations. title: Perform basic volume operations.
@ -93,7 +134,56 @@ def test_basic_volume_operations(pyocf_ctx):
- composite_volume::volume_api - composite_volume::volume_api
- composite_volume::io_request_passing - 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") @pytest.mark.skip(reason="not implemented")

View File

@ -4,7 +4,15 @@
# #
import logging 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 random import randrange
from itertools import count from itertools import count
@ -19,13 +27,22 @@ from pyocf.types.cache import (
CacheConfig, CacheConfig,
PromotionPolicy, PromotionPolicy,
Backfill, Backfill,
CacheDeviceConfig,
CacheAttachConfig,
) )
from pyocf.types.core import Core from pyocf.types.core import Core
from pyocf.types.ctx import OcfCtx from pyocf.types.ctx import OcfCtx
from pyocf.types.data import Data from pyocf.types.data import Data
from pyocf.types.io import IoDir from pyocf.types.io import IoDir
from pyocf.types.queue import Queue 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 import Volume, RamVolume
from pyocf.types.volume_core import CoreVolume from pyocf.types.volume_core import CoreVolume
from pyocf.utils import Size from pyocf.utils import Size
@ -234,7 +251,7 @@ def test_start_stop_multiple(pyocf_ctx):
cache_line_size = CacheLineSize(size) cache_line_size = CacheLineSize(size)
cache = Cache.start_on_device( 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) caches.append(cache)
stats = cache.get_stats() stats = cache.get_stats()
@ -264,7 +281,7 @@ def test_100_start_stop(pyocf_ctx):
cache_line_size = CacheLineSize(size) cache_line_size = CacheLineSize(size)
cache = Cache.start_on_device( 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() stats = cache.get_stats()
assert stats["conf"]["cache_mode"] == cache_mode, "Cache mode" 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) @pytest.mark.parametrize("cls", CacheLineSize)
def test_start_cache_same_device(pyocf_ctx, mode, cls): def test_start_cache_same_device(pyocf_ctx, mode, cls):
"""Adding two caches using the same cache device """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_device = RamVolume(Size.from_MiB(50), uuid=_uuid)
cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls, name="cache1")
cache.get_stats()
with pytest.raises(OcfError, match="OCF_ERR_NOT_OPEN_EXC"): uuid = Uuid(
cache = Cache.start_on_device( _data=cast(create_string_buffer(_uuid.encode("ascii")), c_char_p), _size=len(_uuid) + 1,
cache_device, cache_mode=mode, cache_line_size=cls, name="cache2" )
)
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) @pytest.mark.parametrize("mode", CacheMode)