From c662d6fb2e85322f0522e2c07f5ce815ed285939 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Fri, 3 Jan 2020 15:35:15 -0500 Subject: [PATCH] rbd: add CloneImage as a wrapper for rbd_clone3 func Add a new CloneImage that makes use of rbd_clone3 and thus behaves like the new CreateImage function. Signed-off-by: John Mulligan --- rbd/rbd.go | 44 ++++++++++++++++++++ rbd/rbd_test.go | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/rbd/rbd.go b/rbd/rbd.go index eab4109..9d54e86 100644 --- a/rbd/rbd.go +++ b/rbd/rbd.go @@ -1457,3 +1457,47 @@ func RemoveImage(ioctx *rados.IOContext, name string) error { defer C.free(unsafe.Pointer(c_name)) return getError(C.rbd_remove(C.rados_ioctx_t(ioctx.Pointer()), c_name)) } + +// CloneImage creates a clone of the image from the named snapshot in the +// provided io-context with the given name and image options. +// +// Implements: +// int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name, +// const char *p_snapname, rados_ioctx_t c_ioctx, +// const char *c_name, rbd_image_options_t c_opts); +func CloneImage(ioctx *rados.IOContext, parentName, snapName string, + destctx *rados.IOContext, name string, rio *RbdImageOptions) error { + + if rio == nil { + return RBDError(C.EINVAL) + } + + cParentName := C.CString(parentName) + defer C.free(unsafe.Pointer(cParentName)) + cParentSnapName := C.CString(snapName) + defer C.free(unsafe.Pointer(cParentSnapName)) + cCloneName := C.CString(name) + defer C.free(unsafe.Pointer(cCloneName)) + + ret := C.rbd_clone3( + C.rados_ioctx_t(ioctx.Pointer()), + cParentName, + cParentSnapName, + C.rados_ioctx_t(destctx.Pointer()), + cCloneName, + C.rbd_image_options_t(rio.options)) + return getError(ret) +} + +// CloneFromImage creates a clone of the image from the named snapshot in the +// provided io-context with the given name and image options. +// This function is a convenience wrapper around CloneImage to support cloning +// from an existing Image. +func CloneFromImage(parent *Image, snapName string, + destctx *rados.IOContext, name string, rio *RbdImageOptions) error { + + if err := parent.validate(imageNeedsIOContext); err != nil { + return err + } + return CloneImage(parent.ioctx, parent.name, snapName, destctx, name, rio) +} diff --git a/rbd/rbd_test.go b/rbd/rbd_test.go index a55eb5e..d644170 100644 --- a/rbd/rbd_test.go +++ b/rbd/rbd_test.go @@ -1413,6 +1413,112 @@ func TestRemoveImage(t *testing.T) { conn.Shutdown() } +func TestCloneImage(t *testing.T) { + conn := radosConnect(t) + + poolname := GetUUID() + err := conn.MakePool(poolname) + assert.NoError(t, err) + + ioctx, err := conn.OpenIOContext(poolname) + require.NoError(t, err) + + imageName := "parent1" + snapName := "snap1" + cloneName := "clone1" + options := NewRbdImageOptions() + defer options.Destroy() + err = options.SetUint64(RbdImageOptionOrder, uint64(testImageOrder)) + assert.NoError(t, err) + err = options.SetUint64(RbdImageOptionFeatures, 1) + assert.NoError(t, err) + err = CreateImage(ioctx, imageName, testImageSize, options) + assert.NoError(t, err) + + image, err := OpenImage(ioctx, imageName, NoSnapshot) + assert.NoError(t, err) + + snapshot, err := image.CreateSnapshot(snapName) + assert.NoError(t, err) + err = snapshot.Protect() + assert.NoError(t, err) + + t.Run("cloneImage", func(t *testing.T) { + imageNames, err := GetImageNames(ioctx) + assert.NoError(t, err) + assert.Contains(t, imageNames, imageName) + assert.NotContains(t, imageNames, cloneName) + + options := NewRbdImageOptions() + defer options.Destroy() + err = options.SetUint64(RbdImageOptionFormat, uint64(2)) + assert.NoError(t, err) + err = CloneImage(ioctx, imageName, snapName, ioctx, cloneName, options) + assert.NoError(t, err) + + imageNames, err = GetImageNames(ioctx) + assert.NoError(t, err) + assert.Contains(t, imageNames, imageName) + assert.Contains(t, imageNames, cloneName) + + err = RemoveImage(ioctx, cloneName) + assert.NoError(t, err) + }) + + t.Run("cloneFromImage", func(t *testing.T) { + imageNames, err := GetImageNames(ioctx) + assert.NoError(t, err) + assert.Contains(t, imageNames, imageName) + assert.NotContains(t, imageNames, cloneName) + + options := NewRbdImageOptions() + defer options.Destroy() + err = options.SetUint64(RbdImageOptionFormat, uint64(2)) + assert.NoError(t, err) + err = CloneFromImage(image, snapName, ioctx, cloneName, options) + assert.NoError(t, err) + + imageNames, err = GetImageNames(ioctx) + assert.NoError(t, err) + assert.Contains(t, imageNames, imageName) + assert.Contains(t, imageNames, cloneName) + + err = RemoveImage(ioctx, cloneName) + assert.NoError(t, err) + }) + + t.Run("cloneFromImageInvalidCtx", func(t *testing.T) { + options := NewRbdImageOptions() + defer options.Destroy() + err = options.SetUint64(RbdImageOptionFormat, uint64(2)) + assert.NoError(t, err) + badImage := GetImage(nil, image.name) + err = CloneFromImage(badImage, snapName, ioctx, cloneName, options) + assert.Error(t, err) + }) + + t.Run("cloneImageNilOpts", func(t *testing.T) { + err = CloneImage(ioctx, imageName, snapName, ioctx, cloneName, nil) + assert.Error(t, err) + }) + + err = snapshot.Unprotect() + assert.NoError(t, err) + + err = snapshot.Remove() + assert.NoError(t, err) + + err = image.Close() + assert.NoError(t, err) + + err = image.Remove() + assert.NoError(t, err) + + ioctx.Destroy() + conn.DeletePool(poolname) + conn.Shutdown() +} + // quickCreate creates an image similar to Create but uses CreateImage. // If possible, avoid using this function for new code/tests. It mainly exists // to help with refactoring of existing tests.