mirror of https://github.com/ceph/go-ceph
rbd: add support for CloneImageByID()
RBD image groups can be used to create consistent snapshots of all images that are part of the group. The new rbd_clone4() API makes it possible to create a new RBD image from a single snapshot that was created as part of the group snapshot. Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
parent
0fc95cf6fe
commit
ee25db94c6
|
@ -1956,6 +1956,12 @@
|
|||
"comment": "GetSnapGroupNamespace returns the SnapGroupNamespace of the snapshot which\nis part of a group. The caller should make sure that the snapshot ID passed\nin this function belongs to a snapshot that was taken as part of a group\nsnapshot.\n\nImplements:\n\n\t\tint rbd_snap_get_group_namespace(rbd_image_t image, uint64_t snap_id,\n\t rbd_snap_group_namespace_t *group_snap,\n\t size_t group_snap_size)\n",
|
||||
"added_in_version": "v0.27.0",
|
||||
"expected_stable_version": "v0.29.0"
|
||||
},
|
||||
{
|
||||
"name": "CloneImageByID",
|
||||
"comment": "CloneImageByID creates a clone of the image from a snapshot with the given\nID in the provided io-context with the given name and image options.\n\nImplements:\n\n\tint rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name,\n\t uint64_t p_snap_id, rados_ioctx_t c_ioctx,\n\t const char *c_name, rbd_image_options_t c_opts);\n",
|
||||
"added_in_version": "$NEXT_RELEASE",
|
||||
"expected_stable_version": "$NEXT_RELEASE_STABLE"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2259,4 +2265,4 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ WriteOp.Exec | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
|
|||
Name | Added in Version | Expected Stable Version |
|
||||
---- | ---------------- | ----------------------- |
|
||||
Image.GetSnapGroupNamespace | v0.27.0 | v0.29.0 |
|
||||
CloneImageByID | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
//go:build !(nautilus || octopus || pacific || quincy || reef) && ceph_preview
|
||||
|
||||
package rbd
|
||||
|
||||
// #cgo LDFLAGS: -lrbd
|
||||
// #include <errno.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <rados/librados.h>
|
||||
// #include <rbd/librbd.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/rados"
|
||||
)
|
||||
|
||||
// CloneImageByID creates a clone of the image from a snapshot with the given
|
||||
// ID in the provided io-context with the given name and image options.
|
||||
//
|
||||
// Implements:
|
||||
//
|
||||
// int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name,
|
||||
// uint64_t p_snap_id, rados_ioctx_t c_ioctx,
|
||||
// const char *c_name, rbd_image_options_t c_opts);
|
||||
func CloneImageByID(ioctx *rados.IOContext, parentName string, snapID uint64,
|
||||
destctx *rados.IOContext, name string, rio *ImageOptions) error {
|
||||
|
||||
if rio == nil {
|
||||
return rbdError(C.EINVAL)
|
||||
}
|
||||
|
||||
cParentName := C.CString(parentName)
|
||||
defer C.free(unsafe.Pointer(cParentName))
|
||||
cCloneName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cCloneName))
|
||||
|
||||
ret := C.rbd_clone4(
|
||||
cephIoctx(ioctx),
|
||||
cParentName,
|
||||
C.uint64_t(snapID),
|
||||
cephIoctx(destctx),
|
||||
cCloneName,
|
||||
C.rbd_image_options_t(rio.options))
|
||||
return getError(ret)
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
//go:build !(nautilus || octopus || pacific || quincy || reef) && ceph_preview
|
||||
|
||||
package rbd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCloneImageByID(t *testing.T) {
|
||||
// tests are done as subtests to avoid creating pools, images, etc
|
||||
// over and over again.
|
||||
conn := radosConnect(t)
|
||||
require.NotNil(t, conn)
|
||||
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()
|
||||
|
||||
// create a group, some images, and add images to the group
|
||||
gname := "snapme"
|
||||
err = GroupCreate(ioctx, gname)
|
||||
assert.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, GroupRemove(ioctx, gname))
|
||||
}()
|
||||
|
||||
options := NewRbdImageOptions()
|
||||
assert.NoError(t,
|
||||
options.SetUint64(ImageOptionOrder, uint64(testImageOrder)))
|
||||
defer options.Destroy()
|
||||
|
||||
name1 := GetUUID()
|
||||
err = CreateImage(ioctx, name1, testImageSize, options)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, RemoveImage(ioctx, name1))
|
||||
}()
|
||||
|
||||
name2 := GetUUID()
|
||||
err = CreateImage(ioctx, name2, testImageSize, options)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, RemoveImage(ioctx, name2))
|
||||
}()
|
||||
|
||||
err = GroupImageAdd(ioctx, gname, ioctx, name1)
|
||||
assert.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, GroupImageRemove(ioctx, gname, ioctx, name1))
|
||||
}()
|
||||
|
||||
err = GroupImageAdd(ioctx, gname, ioctx, name2)
|
||||
assert.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, GroupImageRemove(ioctx, gname, ioctx, name2))
|
||||
}()
|
||||
|
||||
t.Run("CloneFromSnapshot", func(t *testing.T) {
|
||||
cloneName := "child"
|
||||
optionsClone := NewRbdImageOptions()
|
||||
defer optionsClone.Destroy()
|
||||
err := optionsClone.SetUint64(ImageOptionCloneFormat, 2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get the snapID
|
||||
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
||||
assert.NoError(t, err)
|
||||
defer img.Close()
|
||||
|
||||
snapName := "mysnap"
|
||||
snapshot, err := img.CreateSnapshot(snapName)
|
||||
assert.NoError(t, err)
|
||||
defer func() {
|
||||
assert.NoError(t, snapshot.Remove())
|
||||
}()
|
||||
|
||||
snapInfos, err := img.GetSnapshotNames()
|
||||
assert.NoError(t, err)
|
||||
require.Equal(t, 1, len(snapInfos))
|
||||
|
||||
snapID := snapInfos[0].Id
|
||||
|
||||
// Create a clone of the image using the snapshot.
|
||||
err = CloneImageByID(ioctx, name1, snapID, ioctx, cloneName, optionsClone)
|
||||
assert.NoError(t, err)
|
||||
defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }()
|
||||
|
||||
imgNew, err := OpenImage(ioctx, cloneName, NoSnapshot)
|
||||
defer func() {
|
||||
assert.NoError(t, imgNew.Close())
|
||||
}()
|
||||
assert.NoError(t, err)
|
||||
|
||||
parentInfo, err := imgNew.GetParent()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, parentInfo.Image.ImageName, name1)
|
||||
assert.Equal(t, parentInfo.Image.PoolName, poolname)
|
||||
assert.False(t, parentInfo.Image.Trash)
|
||||
assert.Equal(t, parentInfo.Snap.SnapName, snapName)
|
||||
assert.Equal(t, parentInfo.Snap.ID, snapID)
|
||||
})
|
||||
|
||||
t.Run("CloneFromGroupSnap", func(t *testing.T) {
|
||||
err := GroupSnapCreate(ioctx, gname, "groupsnap")
|
||||
assert.NoError(t, err)
|
||||
|
||||
cloneName := "img-clone"
|
||||
optionsClone := NewRbdImageOptions()
|
||||
defer optionsClone.Destroy()
|
||||
err = optionsClone.SetUint64(ImageOptionCloneFormat, 2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get the snapID of the image
|
||||
img, err := OpenImageReadOnly(ioctx, name1, NoSnapshot)
|
||||
assert.NoError(t, err)
|
||||
defer img.Close()
|
||||
|
||||
snapInfos, err := img.GetSnapshotNames()
|
||||
assert.NoError(t, err)
|
||||
require.Equal(t, 1, len(snapInfos))
|
||||
|
||||
snapID := snapInfos[0].Id
|
||||
|
||||
// Create a clone of the image using the snapshot.
|
||||
err = CloneImageByID(ioctx, name1, snapID, ioctx, cloneName, optionsClone)
|
||||
assert.NoError(t, err)
|
||||
defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }()
|
||||
|
||||
imgNew, err := OpenImage(ioctx, cloneName, NoSnapshot)
|
||||
defer func() {
|
||||
assert.NoError(t, imgNew.Close())
|
||||
}()
|
||||
assert.NoError(t, err)
|
||||
|
||||
parentInfo, err := imgNew.GetParent()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, parentInfo.Image.ImageName, name1)
|
||||
assert.Equal(t, parentInfo.Snap.ID, snapID)
|
||||
assert.Equal(t, parentInfo.Image.PoolName, poolname)
|
||||
assert.False(t, parentInfo.Image.Trash)
|
||||
|
||||
err = GroupSnapRemove(ioctx, gname, "groupsnap")
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue