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_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
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
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):
|
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())
|
||||||
|
@ -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,7 +501,8 @@ 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):
|
||||||
|
if not device.handle:
|
||||||
uuid = Uuid(
|
uuid = Uuid(
|
||||||
_data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p),
|
_data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p),
|
||||||
_size=len(device.uuid) + 1,
|
_size=len(device.uuid) + 1,
|
||||||
@ -512,8 +515,11 @@ class Cache:
|
|||||||
self.owner.ocf_volume_type[type(device)],
|
self.owner.ocf_volume_type[type(device)],
|
||||||
byref(uuid)
|
byref(uuid)
|
||||||
)
|
)
|
||||||
|
|
||||||
if result != 0:
|
if result != 0:
|
||||||
raise OcfError("Cache volume initialization failed", result)
|
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]
|
||||||
|
@ -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()
|
||||||
|
@ -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):
|
||||||
|
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_))
|
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)
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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]
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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")
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user