librbd exposes rbd_resize2, which allows clients to pass in a boolean controlling

whether or not the allocation should be allowed to shrink, and a progress-tracking
callback. This contribution extends go-ceph to be able to call rbd_resize2.

Closes #933

Signed-off-by: Khayyam Saleem <khayyam.saleem@gmail.com>
This commit is contained in:
Khayyam Saleem 2023-11-08 06:09:36 -05:00 committed by mergify[bot]
parent 3d86572bf6
commit 51b8287148
4 changed files with 165 additions and 2 deletions

View File

@ -1918,7 +1918,14 @@
"became_stable_version": "v0.24.0"
}
],
"preview_api": []
"preview_api": [
{
"name": "Image.Resize2",
"comment": "Resize2 resizes an rbd image and allows configuration of allow_shrink and a callback function. The callback\nfunction will be called with the first argument as the progress, the second argument as the total, and the third\nargument as an opaque value that is passed to the Resize2 function's data argument in each callback execution.\nThe resize operation will be aborted if the progress callback returns a non-zero value.\n\nImplements:\n\n\tint rbd_resize(rbd_image_t image, uint64_t size, allow_shrink bool, librbd_progress_fn_t cb, void *cbdata);\n",
"added_in_version": "$NEXT_RELEASE",
"expected_stable_version": "$NEXT_RELEASE_STABLE"
}
]
},
"rbd/admin": {
"stable_api": [
@ -2209,4 +2216,4 @@
}
]
}
}
}

View File

@ -16,6 +16,12 @@ No Preview/Deprecated APIs found. All APIs are considered stable.
## Package: rbd
### Preview APIs
Name | Added in Version | Expected Stable Version |
---- | ---------------- | ----------------------- |
Image.Resize2 | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
### Deprecated APIs
Name | Deprecated in Version | Expected Removal Version |

77
rbd/resize.go Normal file
View File

@ -0,0 +1,77 @@
//go:build ceph_preview
package rbd
/*
#cgo LDFLAGS: -lrbd
#define _POSIX_C_SOURCE 200112L
#undef _GNU_SOURCE
#include <errno.h>
#include <stdlib.h>
#include <rados/librados.h>
#include <rbd/librbd.h>
extern int resize2Callback(uint64_t, uint64_t, uintptr_t);
// inline wrapper to cast uintptr_t to void*
static inline int wrap_rbd_resize2(
rbd_image_t image, uint64_t size, bool allow_shrink, uintptr_t arg) {
return rbd_resize2(
image, size, allow_shrink, (librbd_progress_fn_t)resize2Callback, (void*)arg);
};
*/
import "C"
import (
"github.com/ceph/go-ceph/internal/callbacks"
)
// Resize2ProgressCallback is the callback function type for Image.Resize2.
type Resize2ProgressCallback func(progress uint64, total uint64, data interface{}) int
var resizeCallbacks = callbacks.New()
type resizeProgressCallbackCtx struct {
callback Resize2ProgressCallback
data interface{}
}
//export resize2Callback
func resize2Callback(
offset, total C.uint64_t, index uintptr,
) C.int {
v := resizeCallbacks.Lookup(index)
ctx := v.(resizeProgressCallbackCtx)
return C.int(ctx.callback(uint64(offset), uint64(total), ctx.data))
}
// Resize2 resizes an rbd image and allows configuration of allow_shrink and a callback function. The callback
// function will be called with the first argument as the progress, the second argument as the total, and the third
// argument as an opaque value that is passed to the Resize2 function's data argument in each callback execution.
// The resize operation will be aborted if the progress callback returns a non-zero value.
//
// Implements:
//
// int rbd_resize(rbd_image_t image, uint64_t size, allow_shrink bool, librbd_progress_fn_t cb, void *cbdata);
func (image *Image) Resize2(size uint64, allowShrink bool, cb Resize2ProgressCallback, 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 := resizeProgressCallbackCtx{
callback: cb,
data: data,
}
cbIndex := resizeCallbacks.Add(ctx)
defer resizeCallbacks.Remove(cbIndex)
ret := C.wrap_rbd_resize2(image.image, C.uint64_t(size), C.bool(allowShrink), C.uintptr_t(cbIndex))
return getError(ret)
}

73
rbd/resize_test.go Normal file
View File

@ -0,0 +1,73 @@
//go:build ceph_preview
package rbd
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestImageResize2(t *testing.T) {
cc := 0
cb := func(offset, total uint64, v interface{}) int {
cc++
val := v.(int)
assert.Equal(t, 0, val)
assert.Equal(t, uint64(2), total)
return 0
}
conn := radosConnect(t)
poolname := GetUUID()
err := conn.MakePool(poolname)
assert.NoError(t, err)
ioctx, err := conn.OpenIOContext(poolname)
require.NoError(t, err)
name := GetUUID()
reqSize := uint64(1024 * 1024 * 4) // 4MB
err = quickCreate(ioctx, name, reqSize, testImageOrder)
assert.NoError(t, err)
image, err := OpenImage(ioctx, name, NoSnapshot)
assert.NoError(t, err)
size, err := image.GetSize()
assert.NoError(t, err)
assert.Equal(t, size, reqSize)
newReqSize := reqSize * 2
// Test normal resize (no shrinking allowed)
err = image.Resize2(newReqSize, false, cb, nil)
assert.NoError(t, err)
size, err = image.GetSize()
assert.NoError(t, err)
assert.Equal(t, size, newReqSize)
// Resize to a smaller size with shrinking allowed
err = image.Resize2(reqSize, true, cb, 0)
assert.NoError(t, err)
// Attempt to resize to a smaller size with shrinking disallowed (should error)
err = image.Resize2(reqSize-1024*1024, false, cb, 0)
assert.Error(t, err)
err = image.Close()
assert.NoError(t, err)
err = image.Resize2(newReqSize, false, cb, 0)
assert.Error(t, err) // Expect an error since the image is not open/
err = image.Remove()
assert.NoError(t, err)
ioctx.Destroy()
conn.DeletePool(poolname)
conn.Shutdown()
}