Skip to content

rbd: add support for CloneImageByID() #1000

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/api-status.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
},
Expand Down Expand Up @@ -2259,4 +2265,4 @@
}
]
}
}
}
1 change: 1 addition & 0 deletions docs/api-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
46 changes: 46 additions & 0 deletions rbd/clone_image_by_id.go
Original file line number Diff line number Diff line change
@@ -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)
}
154 changes: 154 additions & 0 deletions rbd/clone_image_by_id_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}