Merge pull request #702 from robertbaldyga/v22.6-composite-volume
Introduce composite volume
This commit is contained in:
@@ -63,8 +63,7 @@ class CacheConfig(Structure):
|
||||
|
||||
class CacheDeviceConfig(Structure):
|
||||
_fields_ = [
|
||||
("_uuid", Uuid),
|
||||
("_volume_type", c_uint8),
|
||||
("_volume", c_void_p),
|
||||
("_perform_test", c_bool),
|
||||
("_volume_params", c_void_p),
|
||||
]
|
||||
@@ -257,7 +256,7 @@ class Cache:
|
||||
raise OcfError("Failed to detach failover cache device", c.results["error"])
|
||||
|
||||
def standby_activate(self, device, open_cores=True):
|
||||
device_cfg = Cache.generate_device_config(device)
|
||||
device_cfg = self.generate_device_config(device)
|
||||
|
||||
activate_cfg = CacheStandbyActivateConfig(_device=device_cfg, _open_cores=open_cores,)
|
||||
|
||||
@@ -457,14 +456,24 @@ class Cache:
|
||||
if status:
|
||||
raise OcfError("Error adding partition to cache", status)
|
||||
|
||||
@staticmethod
|
||||
def generate_device_config(device, perform_test=True):
|
||||
def generate_device_config(self, device, perform_test=True):
|
||||
uuid = Uuid(
|
||||
_data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p),
|
||||
_size=len(device.uuid) + 1,
|
||||
)
|
||||
volume = c_void_p()
|
||||
|
||||
lib = OcfLib.getInstance()
|
||||
result = lib.ocf_volume_create(
|
||||
byref(volume),
|
||||
self.owner.ocf_volume_type[type(device)],
|
||||
byref(uuid)
|
||||
)
|
||||
if result != 0:
|
||||
raise OcfError("Cache volume initialization failed", result)
|
||||
|
||||
device_config = CacheDeviceConfig(
|
||||
_uuid=Uuid(
|
||||
_data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p),
|
||||
_size=len(device.uuid) + 1,
|
||||
),
|
||||
_volume_type=device.type_id,
|
||||
_volume=volume,
|
||||
_perform_test=perform_test,
|
||||
_volume_params=None,
|
||||
)
|
||||
@@ -477,7 +486,7 @@ class Cache:
|
||||
self.device = device
|
||||
self.device_name = device.uuid
|
||||
|
||||
device_config = Cache.generate_device_config(device, perform_test=perform_test)
|
||||
device_config = self.generate_device_config(device, perform_test=perform_test)
|
||||
|
||||
attach_cfg = CacheAttachConfig(
|
||||
_device=device_config,
|
||||
@@ -505,7 +514,7 @@ class Cache:
|
||||
self.device = device
|
||||
self.device_name = device.uuid
|
||||
|
||||
device_config = Cache.generate_device_config(device, perform_test=False)
|
||||
device_config = self.generate_device_config(device, perform_test=False)
|
||||
|
||||
attach_cfg = CacheAttachConfig(
|
||||
_device=device_config,
|
||||
@@ -533,7 +542,7 @@ class Cache:
|
||||
self.device = device
|
||||
self.device_name = device.uuid
|
||||
|
||||
device_config = Cache.generate_device_config(device, perform_test=perform_test)
|
||||
device_config = self.generate_device_config(device, perform_test=perform_test)
|
||||
|
||||
attach_cfg = CacheAttachConfig(
|
||||
_device=device_config,
|
||||
@@ -570,7 +579,7 @@ class Cache:
|
||||
self.device = device
|
||||
self.device_name = device.uuid
|
||||
|
||||
device_config = Cache.generate_device_config(device)
|
||||
device_config = self.generate_device_config(device)
|
||||
|
||||
attach_cfg = CacheAttachConfig(
|
||||
_device=device_config,
|
||||
@@ -917,3 +926,5 @@ lib.ocf_mngt_add_partition_to_cache.argtypes = [
|
||||
]
|
||||
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]
|
||||
|
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
from ctypes import c_void_p, Structure, c_char_p, cast, pointer, byref, c_int
|
||||
from ctypes import c_void_p, Structure, c_char_p, cast, pointer, byref, c_int, c_uint8
|
||||
import weakref
|
||||
|
||||
from .logger import LoggerOps, Logger
|
||||
@@ -37,6 +37,7 @@ class OcfCtx:
|
||||
self.lib = lib
|
||||
self.volume_types_count = 1
|
||||
self.volume_types = {}
|
||||
self.ocf_volume_type = {}
|
||||
self.caches = []
|
||||
|
||||
self.cfg = OcfCtxCfg(
|
||||
@@ -77,6 +78,11 @@ class OcfCtx:
|
||||
if result != 0:
|
||||
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.volume_types_count += 1
|
||||
|
||||
def unregister_volume_type(self, vol_type):
|
||||
@@ -86,6 +92,7 @@ class OcfCtx:
|
||||
self.lib.ocf_ctx_unregister_volume_type(self.ctx_handle, vol_type.type_id)
|
||||
|
||||
del self.volume_types[vol_type.type_id]
|
||||
del self.ocf_volume_type[vol_type]
|
||||
|
||||
def cleanup_volume_types(self):
|
||||
for k, vol_type in list(self.volume_types.items()):
|
||||
@@ -108,3 +115,5 @@ class OcfCtx:
|
||||
lib = OcfLib.getInstance()
|
||||
lib.ocf_mngt_cache_get_by_name.argtypes = [c_void_p, c_void_p, c_void_p]
|
||||
lib.ocf_mngt_cache_get_by_name.restype = c_int
|
||||
lib.ocf_ctx_get_volume_type.argtypes = [c_void_p, c_uint8]
|
||||
lib.ocf_ctx_get_volume_type.restype = c_void_p
|
||||
|
@@ -47,7 +47,9 @@ class VolumeOps(Structure):
|
||||
SUBMIT_METADATA = CFUNCTYPE(None, c_void_p)
|
||||
SUBMIT_DISCARD = CFUNCTYPE(None, c_void_p)
|
||||
SUBMIT_WRITE_ZEROES = CFUNCTYPE(None, c_void_p)
|
||||
OPEN = CFUNCTYPE(c_int, c_void_p)
|
||||
ON_INIT = CFUNCTYPE(c_int, c_void_p)
|
||||
ON_DEINIT = CFUNCTYPE(None, c_void_p)
|
||||
OPEN = CFUNCTYPE(c_int, c_void_p, c_void_p)
|
||||
CLOSE = CFUNCTYPE(None, c_void_p)
|
||||
GET_MAX_IO_SIZE = CFUNCTYPE(c_uint, c_void_p)
|
||||
GET_LENGTH = CFUNCTYPE(c_uint64, c_void_p)
|
||||
@@ -58,6 +60,8 @@ class VolumeOps(Structure):
|
||||
("_submit_metadata", SUBMIT_METADATA),
|
||||
("_submit_discard", SUBMIT_DISCARD),
|
||||
("_submit_write_zeroes", SUBMIT_WRITE_ZEROES),
|
||||
("_on_init", ON_INIT),
|
||||
("_on_deinit", ON_DEINIT),
|
||||
("_open", OPEN),
|
||||
("_close", CLOSE),
|
||||
("_get_length", GET_LENGTH),
|
||||
@@ -124,8 +128,16 @@ class Volume:
|
||||
def _submit_write_zeroes(write_zeroes):
|
||||
raise NotImplementedError
|
||||
|
||||
@VolumeOps.ON_INIT
|
||||
def _on_init(ref):
|
||||
return 0
|
||||
|
||||
@VolumeOps.ON_DEINIT
|
||||
def _on_deinit(ref):
|
||||
return
|
||||
|
||||
@VolumeOps.OPEN
|
||||
def _open(ref):
|
||||
def _open(ref, params):
|
||||
uuid_ptr = cast(OcfLib.getInstance().ocf_volume_get_uuid(ref), POINTER(Uuid))
|
||||
uuid = str(uuid_ptr.contents._data, encoding="ascii")
|
||||
try:
|
||||
@@ -161,6 +173,8 @@ class Volume:
|
||||
_close=_close,
|
||||
_get_max_io_size=_get_max_io_size,
|
||||
_get_length=_get_length,
|
||||
_on_init=_on_init,
|
||||
_on_deinit=_on_deinit,
|
||||
)
|
||||
|
||||
return Volume._ops_[cls]
|
||||
|
@@ -74,6 +74,8 @@ def test_io_flags(pyocf_ctx, cache_mode):
|
||||
|
||||
data = bytes(block_size)
|
||||
|
||||
pyocf_ctx.register_volume_type(FlagsValVolume)
|
||||
|
||||
cache_device = FlagsValVolume(Size.from_MiB(50), flags)
|
||||
core_device = FlagsValVolume(Size.from_MiB(50), flags)
|
||||
|
||||
|
@@ -63,6 +63,8 @@ def test_flush_after_mngmt(pyocf_ctx):
|
||||
|
||||
data = bytes(block_size)
|
||||
|
||||
pyocf_ctx.register_volume_type(FlushValVolume)
|
||||
|
||||
cache_device = FlushValVolume(Size.from_MiB(50))
|
||||
core_device = FlushValVolume(Size.from_MiB(50))
|
||||
|
||||
|
276
tests/functional/tests/management/test_composite_volume.py
Normal file
276
tests/functional/tests/management/test_composite_volume.py
Normal file
@@ -0,0 +1,276 @@
|
||||
#
|
||||
# Copyright(c) 2022 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_create_composite_volume(pyocf_ctx):
|
||||
"""
|
||||
title: Create composite volume.
|
||||
description: |
|
||||
Check that it is possible to create and destroy composite volume
|
||||
object.
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolume is added without an error.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Verify that no error occured
|
||||
- Add RamVolume as a subvolume
|
||||
- Verify that no error occured
|
||||
- Destroy composite volume
|
||||
requirements:
|
||||
- composite_volume::creation
|
||||
- composite_volume::adding_component_volume
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_add_subvolumes_of_different_types(pyocf_ctx):
|
||||
"""
|
||||
title: Add subvolumes of different types.
|
||||
description: |
|
||||
Check that it is possible to add two subvolumes of different types to
|
||||
composite volume.
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolumes are added without an error.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add RamVolume as a subvolume
|
||||
- Verify that no error occured
|
||||
- Add ErrorDevice as a subvolume
|
||||
- Verify that no error occured
|
||||
- Destroy composite volume
|
||||
requirements:
|
||||
- composite_volume::component_volume_types
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_add_max_subvolumes(pyocf_ctx):
|
||||
"""
|
||||
title: Add maximum number of subvolumes.
|
||||
description: |
|
||||
Check that it is possible to add 16 subvolumes to composite volume.
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolumes are added without an error.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add 16 RamVolume instances as subvolumes
|
||||
- Verify that no error occured
|
||||
- Destroy composite volume
|
||||
requirements:
|
||||
- composite_volume::max_composite_volumes
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_basic_volume_operations(pyocf_ctx):
|
||||
"""
|
||||
title: Perform basic volume operations.
|
||||
description: |
|
||||
Check that basic volume operations work on composite volume.
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolume is added without an error.
|
||||
- Submit io, submit flush and submit discard operations work properly.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add mock volume as a subvolume
|
||||
- Submit io to composite volume and check if it was propagated
|
||||
- Submit flush to composite volume and check if it was propagated
|
||||
- Submit discard to composite volume and check if it was propagated
|
||||
- Destroy composite volume
|
||||
requirements:
|
||||
- composite_volume::volume_api
|
||||
- composite_volume::io_request_passing
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_io_propagation_basic(pyocf_ctx):
|
||||
"""
|
||||
title: Perform volume operations with multiple subvolumes.
|
||||
description: |
|
||||
Check that io operations are propagated properly to subvolumes.
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolumes are added without an error.
|
||||
- Operations are propagated properly.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add 16 mock volumes as subvolumes
|
||||
- Submit io to each subvolume address range
|
||||
- Check if requests were propagated properly
|
||||
- Submit flush to each subvolume address range
|
||||
- Check if requests were propagated properly
|
||||
- Submit discard to each subvolume address range
|
||||
- Check if requests were propagated properly
|
||||
- Destroy composite volume
|
||||
requirements:
|
||||
- composite_volume::volume_api
|
||||
- composite_volume::io_request_passing
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_io_propagation_cross_boundary(pyocf_ctx):
|
||||
"""
|
||||
title: Perform cross-subvolume operations.
|
||||
description: |
|
||||
Check that cross-subvolume operations are propagated properly.
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolumes are added without an error.
|
||||
- Operations are propagated properly.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add 16 mock volumes as subvolumes
|
||||
- Submit io that cross address range boundary between each subvolume
|
||||
- Check if requests were propagated properly
|
||||
- Submit flush that cross address range boundary between each subvolume
|
||||
- Check if requests were propagated properly
|
||||
- Submit discard that cross address range boundary between each subvolume
|
||||
- Check if requests were propagated properly
|
||||
- Destroy composite volume
|
||||
requirements:
|
||||
- composite_volume::io_request_passing
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_io_propagation_multiple_subvolumes(pyocf_ctx):
|
||||
"""
|
||||
title: Perform multi-subvolume operations.
|
||||
description: |
|
||||
Check that multi-subvolume operations are propagated properly.
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolumes are added without an error.
|
||||
- Operations are propagated properly.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add 16 mock volumes as subvolumes
|
||||
- Submit series of ios that touch from 2 to 16 subvolumes
|
||||
- Check if requests were propated properly
|
||||
- Submit series of flushes that touch from 2 to 16 subvolumes
|
||||
- Check if requests were propagated properly
|
||||
- Submit series of discardss that touch from 2 to 16 subvolumes
|
||||
- Check if requests were propagated properly
|
||||
- Destroy composite volume
|
||||
requirements:
|
||||
- composite_volume::io_request_passing
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_io_completion(pyocf_ctx):
|
||||
"""
|
||||
title: Composite volume completion order.
|
||||
description: |
|
||||
Check that composite volume waits for completions from all subvolumes.
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolumes are added without an error.
|
||||
- Operations are completed only after all subvolumes operations complete.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add 16 mock volumes as subvolumes
|
||||
- Submit series of ios that touch from 2 to 16 subvolumes
|
||||
- Check if completions are called only after all subvolumes completed
|
||||
- Submit series of flushes that touch from 2 to 16 subvolumes
|
||||
- Check if completions are called only after all subvolumes completed
|
||||
- Submit series of discardss that touch from 2 to 16 subvolumes
|
||||
- Check if completions are called only after all subvolumes completed
|
||||
- Destroy composite volume
|
||||
requirements:
|
||||
- composite_volume::io_request_completion
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_io_completion(pyocf_ctx):
|
||||
"""
|
||||
title: Composite volume error propagation.
|
||||
description: |
|
||||
Check that composite volume propagates errors from subvolumes.
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolumes are added without an error.
|
||||
- Errors from subvolumes are propagated to composite volume.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add 16 ErrorDevice instances as subvolumes
|
||||
- Before each request arm one of ErrorDevices touched by this request
|
||||
- Submit series of ios that touch from 2 to 16 subvolumes
|
||||
- Check if errors were propagated properly
|
||||
- Submit series of flushes that touch from 2 to 16 subvolumes
|
||||
- Check if errors were propagated properly
|
||||
- Submit series of discardss that touch from 2 to 16 subvolumes
|
||||
- Check if errors were propagated properly
|
||||
- Destroy composite volume
|
||||
requirements:
|
||||
- composite_volume::io_error_handling
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_attach(pyocf_ctx):
|
||||
"""
|
||||
title: Attach composite volume.
|
||||
description: |
|
||||
Check that it is possible to attach composite volume
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolumes are added without an error.
|
||||
- Cache attach succeeds.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add 16 RamVolume instances as subvolumes.
|
||||
- Start cache and attach it using composite volume instance.
|
||||
- Verify that cache was attached properly.
|
||||
- Stop the cache.
|
||||
- Verify that cache was stopped.
|
||||
requirements:
|
||||
- composite_volume::cache_attach_load
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="not implemented")
|
||||
def test_load(pyocf_ctx):
|
||||
"""
|
||||
title: Load composite volume.
|
||||
description: |
|
||||
Check that it is possible to attach composite volume
|
||||
pass_criteria:
|
||||
- Composite volume is created without an error.
|
||||
- Subvolumes are added without an error.
|
||||
- Cache load succeeds.
|
||||
steps:
|
||||
- Create composite volume
|
||||
- Add 16 RamVolume instances as subvolumes.
|
||||
- Start cache and attach it using composite volume instance.
|
||||
- Stop the cache.
|
||||
- Start cache and load it using composite volume instance.
|
||||
- Verify that cache was loaded properly.
|
||||
- Stop the cache.
|
||||
- Verify that cache was stopped.
|
||||
requirements:
|
||||
- composite_volume::cache_attach_load
|
||||
"""
|
||||
pass
|
Reference in New Issue
Block a user