diff --git a/rbd/group_snap.go b/rbd/group_snap.go index a698368..fbd3b3f 100644 --- a/rbd/group_snap.go +++ b/rbd/group_snap.go @@ -2,14 +2,26 @@ package rbd /* #cgo LDFLAGS: -lrbd +#include #include #include + +extern int snapRollbackCallback(uint64_t, uint64_t, uintptr_t); + +// inline wrapper to cast uintptr_t to void* +static inline int wrap_rbd_group_snap_rollback_with_progress( + rados_ioctx_t group_p, const char *group_name, + const char *snap_name, uintptr_t arg) { + return rbd_group_snap_rollback_with_progress( + group_p, group_name, snap_name, (librbd_progress_fn_t)snapRollbackCallback, (void*)arg); +}; */ import "C" import ( "unsafe" + "github.com/ceph/go-ceph/internal/callbacks" "github.com/ceph/go-ceph/internal/retry" "github.com/ceph/go-ceph/rados" ) @@ -147,3 +159,65 @@ func GroupSnapRollback(ioctx *rados.IOContext, group, snap string) error { ret := C.rbd_group_snap_rollback(cephIoctx(ioctx), cGroupName, cSnapName) return getError(ret) } + +// GroupSnapRollbackCallback defines the function signature needed for the +// GroupSnapRollbackWithProgress callback. +// +// This callback will be called by GroupSnapRollbackWithProgress when it +// wishes to report progress rolling back a group snapshot. +type GroupSnapRollbackCallback func(uint64, uint64, interface{}) int + +var groupSnapRollbackCallbacks = callbacks.New() + +// GroupSnapRollbackWithProgress will roll back the images in the group +// to that of given snapshot. The given progress callback will be called +// to report on the progress of the snapshot rollback. +// +// Implements: +// int rbd_group_snap_rollback_with_progress(rados_ioctx_t group_p, +// const char *group_name, +// const char *snap_name, +// librbd_progress_fn_t cb, +// void *cbdata); +func GroupSnapRollbackWithProgress( + ioctx *rados.IOContext, group, snap string, + cb GroupSnapRollbackCallback, data interface{}) error { + // the provided callback must be a real function + if cb == nil { + return rbdError(C.EINVAL) + } + + cGroupName := C.CString(group) + defer C.free(unsafe.Pointer(cGroupName)) + cSnapName := C.CString(snap) + defer C.free(unsafe.Pointer(cSnapName)) + + ctx := gsnapRollbackCallbackCtx{ + callback: cb, + data: data, + } + cbIndex := groupSnapRollbackCallbacks.Add(ctx) + defer diffIterateCallbacks.Remove(cbIndex) + + ret := C.wrap_rbd_group_snap_rollback_with_progress( + cephIoctx(ioctx), + cGroupName, + cSnapName, + C.uintptr_t(cbIndex)) + + return getError(ret) +} + +type gsnapRollbackCallbackCtx struct { + callback GroupSnapRollbackCallback + data interface{} +} + +//export snapRollbackCallback +func snapRollbackCallback( + offset, total C.uint64_t, index uintptr) C.int { + + v := groupSnapRollbackCallbacks.Lookup(index) + ctx := v.(gsnapRollbackCallbackCtx) + return C.int(ctx.callback(uint64(offset), uint64(total), ctx.data)) +} diff --git a/rbd/group_snap_test.go b/rbd/group_snap_test.go index b7107bd..e1046c6 100644 --- a/rbd/group_snap_test.go +++ b/rbd/group_snap_test.go @@ -153,6 +153,69 @@ func TestGroupSnapshots(t *testing.T) { err = GroupSnapRemove(ioctx, gname, snapname) assert.NoError(t, err) }) + t.Run("groupSnapRollbackWithProgress", func(t *testing.T) { + img, err := OpenImage(ioctx, name1, NoSnapshot) + assert.NoError(t, err) + _, err = img.WriteAt([]byte("SAY CHEESE"), 0) + assert.NoError(t, err) + _, err = img.WriteAt([]byte("AND SMILE_"), 10240) + assert.NoError(t, err) + err = img.Close() + assert.NoError(t, err) + + snapname := "snap2r" + err = GroupSnapCreate(ioctx, gname, snapname) + assert.NoError(t, err) + + img, err = OpenImage(ioctx, name1, NoSnapshot) + assert.NoError(t, err) + _, err = img.WriteAt([]byte("GOODBYE WORLD"), 0) + assert.NoError(t, err) + _, err = img.WriteAt([]byte("_THIS_IS_ALL_"), 10240) + assert.NoError(t, err) + _, err = img.WriteAt([]byte("I_HAVE_TO_SAY"), 11240) + assert.NoError(t, err) + err = img.Close() + assert.NoError(t, err) + + img, err = OpenImage(ioctx, name2, NoSnapshot) + assert.NoError(t, err) + _, err = img.WriteAt([]byte("3333333333333"), 0) + assert.NoError(t, err) + err = img.Close() + assert.NoError(t, err) + + 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 = GroupSnapRollbackWithProgress(ioctx, gname, snapname, cb, 0) + assert.NoError(t, err) + assert.GreaterOrEqual(t, cc, 2) + + b := make([]byte, 8) + img, err = OpenImage(ioctx, name1, NoSnapshot) + assert.NoError(t, err) + _, err = img.ReadAt(b, 0) + assert.NoError(t, err) + err = img.Close() + assert.NoError(t, err) + assert.Equal(t, []byte("SAY CHEE"), b) + + img, err = OpenImage(ioctx, name2, NoSnapshot) + assert.NoError(t, err) + _, err = img.ReadAt(b, 0) + assert.NoError(t, err) + err = img.Close() + assert.NoError(t, err) + assert.Equal(t, []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), b) + + err = GroupSnapRemove(ioctx, gname, snapname) + assert.NoError(t, err) }) t.Run("invalidIOContext", func(t *testing.T) { assert.Panics(t, func() { @@ -170,5 +233,13 @@ func TestGroupSnapshots(t *testing.T) { assert.Panics(t, func() { GroupSnapRollback(nil, gname, "foo") }) + assert.Panics(t, func() { + cb := func(o, t uint64, v interface{}) int { return 0 } + GroupSnapRollbackWithProgress(nil, gname, "foo", cb, nil) + }) + }) + t.Run("invalidCallback", func(t *testing.T) { + err := GroupSnapRollbackWithProgress(ioctx, gname, "foo", nil, nil) + assert.Error(t, err) }) }