rbd: Add rbd_sparsify_with_progress rbd API

Added a new rbd API `rbd_sparsify_with_progress`, and supporting
tests.

Fixes: #281

Signed-off-by: Nikhil-Ladha <nikhilladha1999@gmail.com>
This commit is contained in:
Nikhil-Ladha 2023-03-27 17:12:40 +05:30 committed by mergify[bot]
parent 66ffbfa633
commit 3d986f2bd3
4 changed files with 183 additions and 0 deletions

View File

@ -1867,6 +1867,12 @@
"comment": "SetMirrorPeerSiteDirection sets the direction of a mirror peer site.\n",
"added_in_version": "v0.21.0",
"expected_stable_version": "v0.23.0"
},
{
"name": "Image.SparsifyWithProgress",
"comment": "SparsifyWithProgress makes an image sparse by deallocating runs of zeros.\nThe sparseSize value will be used to find runs of zeros and must be\na power of two no less than 4096 and no larger than the image size.\nThe given progress callback will be called to report on the progress\nof sparse. The operation will be aborted if the progress callback returns\na non-zero value.\n\nImplements:\n\n\tint rbd_sparsify_with_progress(rbd_image_t image, size_t sparse_size,\n\t\t\t\t\t\t\t\t librbd_progress_fn_t cb, void *cbdata);\n",
"added_in_version": "$NEXT_RELEASE",
"expected_stable_version": "$NEXT_RELEASE_STABLE"
}
]
},

View File

@ -63,6 +63,7 @@ ListMirrorPeerSite | v0.21.0 | v0.23.0 |
SetMirrorPeerSiteClientName | v0.21.0 | v0.23.0 |
SetMirrorPeerSiteName | v0.21.0 | v0.23.0 |
SetMirrorPeerSiteDirection | v0.21.0 | v0.23.0 |
Image.SparsifyWithProgress | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
### Deprecated APIs

82
rbd/rbd_sparsify.go Normal file
View File

@ -0,0 +1,82 @@
//go:build !nautilus && ceph_preview
// +build !nautilus,ceph_preview
package rbd
/*
#cgo LDFLAGS: -lrbd
#include <errno.h>
#include <stdlib.h>
#include <rbd/librbd.h>
extern int sparsifyCallback(uint64_t, uint64_t, uintptr_t);
// inline wrapper to cast uintptr_t to void*
static inline int wrap_rbd_sparsify_with_progress(
rbd_image_t image, size_t sparse_size, uintptr_t arg) {
return rbd_sparsify_with_progress(
image, sparse_size, (librbd_progress_fn_t)sparsifyCallback, (void*)arg);
};
*/
import "C"
import (
"github.com/ceph/go-ceph/internal/callbacks"
)
// SparsifyCallback defines the function signature needed for the
// SparsifyWithProgress callback.
//
// This callback will be called by SparsifyWithProgress when it
// wishes to report progress on sparse.
type SparsifyCallback func(uint64, uint64, interface{}) int
var sparsifyCallbacks = callbacks.New()
type sparsifyCallbackCtx struct {
callback SparsifyCallback
data interface{}
}
// SparsifyWithProgress makes an image sparse by deallocating runs of zeros.
// The sparseSize value will be used to find runs of zeros and must be
// a power of two no less than 4096 and no larger than the image size.
// The given progress callback will be called to report on the progress
// of sparse. The operation will be aborted if the progress callback returns
// a non-zero value.
//
// Implements:
//
// int rbd_sparsify_with_progress(rbd_image_t image, size_t sparse_size,
// librbd_progress_fn_t cb, void *cbdata);
func (image *Image) SparsifyWithProgress(
sparseSize uint, cb SparsifyCallback, data interface{}) error {
// the provided callback must be a real function
if cb == nil {
return rbdError(C.EINVAL)
}
if err := image.validate(imageIsOpen); err != nil {
return err
}
ctx := sparsifyCallbackCtx{
callback: cb,
data: data,
}
cbIndex := sparsifyCallbacks.Add(ctx)
defer diffIterateCallbacks.Remove(cbIndex)
ret := C.wrap_rbd_sparsify_with_progress(image.image, C.size_t(sparseSize), C.uintptr_t(cbIndex))
return getError(ret)
}
//export sparsifyCallback
func sparsifyCallback(
offset, total C.uint64_t, index uintptr) C.int {
v := sparsifyCallbacks.Lookup(index)
ctx := v.(sparsifyCallbackCtx)
return C.int(ctx.callback(uint64(offset), uint64(total), ctx.data))
}

94
rbd/rbd_sparsify_test.go Normal file
View File

@ -0,0 +1,94 @@
//go:build !nautilus && ceph_preview
// +build !nautilus,ceph_preview
package rbd
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSparsifyWithProgress(t *testing.T) {
conn := radosConnect(t)
defer conn.Shutdown()
poolname := GetUUID()
err := conn.MakePool(poolname)
require.NoError(t, err)
defer conn.DeletePool(poolname)
ioctx, err := conn.OpenIOContext(poolname)
require.NoError(t, err)
defer ioctx.Destroy()
name := GetUUID()
err = quickCreate(ioctx, name, testImageSize, testImageOrder)
require.NoError(t, err)
defer func() { assert.NoError(t, RemoveImage(ioctx, name)) }()
t.Run("valid", func(t *testing.T) {
img, err := OpenImage(ioctx, name, NoSnapshot)
assert.NoError(t, err)
defer func() { assert.NoError(t, img.Close()) }()
cc := 0
cb := func(offset, total uint64, v interface{}) int {
cc++
val := v.(int)
assert.Equal(t, 0, val)
assert.Equal(t, uint64(1), total)
return 0
}
err = img.SparsifyWithProgress(4096, cb, 0)
assert.NoError(t, err)
assert.GreaterOrEqual(t, cc, 1)
})
t.Run("negativeReturnValue", func(t *testing.T) {
img, err := OpenImage(ioctx, name, NoSnapshot)
assert.NoError(t, err)
defer func() { assert.NoError(t, img.Close()) }()
cc := 0
cb := func(offset, total uint64, v interface{}) int {
cc++
val := v.(int)
assert.Equal(t, 0, val)
assert.Equal(t, uint64(1), total)
return -1
}
err = img.SparsifyWithProgress(4096, cb, 0)
assert.Error(t, err)
})
t.Run("closedImage", func(t *testing.T) {
img, err := OpenImage(ioctx, name, NoSnapshot)
assert.NoError(t, err)
assert.NoError(t, img.Close())
cc := 0
cb := func(offset, total uint64, v interface{}) int {
cc++
val := v.(int)
assert.Equal(t, 0, val)
assert.Equal(t, uint64(1), total)
return 0
}
err = img.SparsifyWithProgress(4096, cb, 0)
assert.Error(t, err)
})
t.Run("invalidValue", func(t *testing.T) {
img, err := OpenImage(ioctx, name, NoSnapshot)
assert.NoError(t, err)
defer func() { assert.NoError(t, img.Close()) }()
err = img.SparsifyWithProgress(4096, nil, nil)
assert.Error(t, err)
})
}