Skip to content

Commit ee25db9

Browse files
nixpanicmergify[bot]
authored andcommitted
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>
1 parent 0fc95cf commit ee25db9

File tree

4 files changed

+208
-1
lines changed

4 files changed

+208
-1
lines changed

docs/api-status.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -1956,6 +1956,12 @@
19561956
"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",
19571957
"added_in_version": "v0.27.0",
19581958
"expected_stable_version": "v0.29.0"
1959+
},
1960+
{
1961+
"name": "CloneImageByID",
1962+
"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",
1963+
"added_in_version": "$NEXT_RELEASE",
1964+
"expected_stable_version": "$NEXT_RELEASE_STABLE"
19591965
}
19601966
]
19611967
},
@@ -2259,4 +2265,4 @@
22592265
}
22602266
]
22612267
}
2262-
}
2268+
}

docs/api-status.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ WriteOp.Exec | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
3131
Name | Added in Version | Expected Stable Version |
3232
---- | ---------------- | ----------------------- |
3333
Image.GetSnapGroupNamespace | v0.27.0 | v0.29.0 |
34+
CloneImageByID | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
3435

3536
### Deprecated APIs
3637

rbd/clone_image_by_id.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//go:build !(nautilus || octopus || pacific || quincy || reef) && ceph_preview
2+
3+
package rbd
4+
5+
// #cgo LDFLAGS: -lrbd
6+
// #include <errno.h>
7+
// #include <stdlib.h>
8+
// #include <rados/librados.h>
9+
// #include <rbd/librbd.h>
10+
import "C"
11+
12+
import (
13+
"unsafe"
14+
15+
"github.com/ceph/go-ceph/rados"
16+
)
17+
18+
// CloneImageByID creates a clone of the image from a snapshot with the given
19+
// ID in the provided io-context with the given name and image options.
20+
//
21+
// Implements:
22+
//
23+
// int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name,
24+
// uint64_t p_snap_id, rados_ioctx_t c_ioctx,
25+
// const char *c_name, rbd_image_options_t c_opts);
26+
func CloneImageByID(ioctx *rados.IOContext, parentName string, snapID uint64,
27+
destctx *rados.IOContext, name string, rio *ImageOptions) error {
28+
29+
if rio == nil {
30+
return rbdError(C.EINVAL)
31+
}
32+
33+
cParentName := C.CString(parentName)
34+
defer C.free(unsafe.Pointer(cParentName))
35+
cCloneName := C.CString(name)
36+
defer C.free(unsafe.Pointer(cCloneName))
37+
38+
ret := C.rbd_clone4(
39+
cephIoctx(ioctx),
40+
cParentName,
41+
C.uint64_t(snapID),
42+
cephIoctx(destctx),
43+
cCloneName,
44+
C.rbd_image_options_t(rio.options))
45+
return getError(ret)
46+
}

rbd/clone_image_by_id_test.go

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//go:build !(nautilus || octopus || pacific || quincy || reef) && ceph_preview
2+
3+
package rbd
4+
5+
import (
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestCloneImageByID(t *testing.T) {
13+
// tests are done as subtests to avoid creating pools, images, etc
14+
// over and over again.
15+
conn := radosConnect(t)
16+
require.NotNil(t, conn)
17+
defer conn.Shutdown()
18+
19+
poolname := GetUUID()
20+
err := conn.MakePool(poolname)
21+
require.NoError(t, err)
22+
defer conn.DeletePool(poolname)
23+
24+
ioctx, err := conn.OpenIOContext(poolname)
25+
require.NoError(t, err)
26+
defer ioctx.Destroy()
27+
28+
// create a group, some images, and add images to the group
29+
gname := "snapme"
30+
err = GroupCreate(ioctx, gname)
31+
assert.NoError(t, err)
32+
defer func() {
33+
assert.NoError(t, GroupRemove(ioctx, gname))
34+
}()
35+
36+
options := NewRbdImageOptions()
37+
assert.NoError(t,
38+
options.SetUint64(ImageOptionOrder, uint64(testImageOrder)))
39+
defer options.Destroy()
40+
41+
name1 := GetUUID()
42+
err = CreateImage(ioctx, name1, testImageSize, options)
43+
require.NoError(t, err)
44+
defer func() {
45+
assert.NoError(t, RemoveImage(ioctx, name1))
46+
}()
47+
48+
name2 := GetUUID()
49+
err = CreateImage(ioctx, name2, testImageSize, options)
50+
require.NoError(t, err)
51+
defer func() {
52+
assert.NoError(t, RemoveImage(ioctx, name2))
53+
}()
54+
55+
err = GroupImageAdd(ioctx, gname, ioctx, name1)
56+
assert.NoError(t, err)
57+
defer func() {
58+
assert.NoError(t, GroupImageRemove(ioctx, gname, ioctx, name1))
59+
}()
60+
61+
err = GroupImageAdd(ioctx, gname, ioctx, name2)
62+
assert.NoError(t, err)
63+
defer func() {
64+
assert.NoError(t, GroupImageRemove(ioctx, gname, ioctx, name2))
65+
}()
66+
67+
t.Run("CloneFromSnapshot", func(t *testing.T) {
68+
cloneName := "child"
69+
optionsClone := NewRbdImageOptions()
70+
defer optionsClone.Destroy()
71+
err := optionsClone.SetUint64(ImageOptionCloneFormat, 2)
72+
assert.NoError(t, err)
73+
74+
// Get the snapID
75+
img, err := OpenImage(ioctx, name1, NoSnapshot)
76+
assert.NoError(t, err)
77+
defer img.Close()
78+
79+
snapName := "mysnap"
80+
snapshot, err := img.CreateSnapshot(snapName)
81+
assert.NoError(t, err)
82+
defer func() {
83+
assert.NoError(t, snapshot.Remove())
84+
}()
85+
86+
snapInfos, err := img.GetSnapshotNames()
87+
assert.NoError(t, err)
88+
require.Equal(t, 1, len(snapInfos))
89+
90+
snapID := snapInfos[0].Id
91+
92+
// Create a clone of the image using the snapshot.
93+
err = CloneImageByID(ioctx, name1, snapID, ioctx, cloneName, optionsClone)
94+
assert.NoError(t, err)
95+
defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }()
96+
97+
imgNew, err := OpenImage(ioctx, cloneName, NoSnapshot)
98+
defer func() {
99+
assert.NoError(t, imgNew.Close())
100+
}()
101+
assert.NoError(t, err)
102+
103+
parentInfo, err := imgNew.GetParent()
104+
assert.NoError(t, err)
105+
assert.Equal(t, parentInfo.Image.ImageName, name1)
106+
assert.Equal(t, parentInfo.Image.PoolName, poolname)
107+
assert.False(t, parentInfo.Image.Trash)
108+
assert.Equal(t, parentInfo.Snap.SnapName, snapName)
109+
assert.Equal(t, parentInfo.Snap.ID, snapID)
110+
})
111+
112+
t.Run("CloneFromGroupSnap", func(t *testing.T) {
113+
err := GroupSnapCreate(ioctx, gname, "groupsnap")
114+
assert.NoError(t, err)
115+
116+
cloneName := "img-clone"
117+
optionsClone := NewRbdImageOptions()
118+
defer optionsClone.Destroy()
119+
err = optionsClone.SetUint64(ImageOptionCloneFormat, 2)
120+
assert.NoError(t, err)
121+
122+
// Get the snapID of the image
123+
img, err := OpenImageReadOnly(ioctx, name1, NoSnapshot)
124+
assert.NoError(t, err)
125+
defer img.Close()
126+
127+
snapInfos, err := img.GetSnapshotNames()
128+
assert.NoError(t, err)
129+
require.Equal(t, 1, len(snapInfos))
130+
131+
snapID := snapInfos[0].Id
132+
133+
// Create a clone of the image using the snapshot.
134+
err = CloneImageByID(ioctx, name1, snapID, ioctx, cloneName, optionsClone)
135+
assert.NoError(t, err)
136+
defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }()
137+
138+
imgNew, err := OpenImage(ioctx, cloneName, NoSnapshot)
139+
defer func() {
140+
assert.NoError(t, imgNew.Close())
141+
}()
142+
assert.NoError(t, err)
143+
144+
parentInfo, err := imgNew.GetParent()
145+
assert.NoError(t, err)
146+
assert.Equal(t, parentInfo.Image.ImageName, name1)
147+
assert.Equal(t, parentInfo.Snap.ID, snapID)
148+
assert.Equal(t, parentInfo.Image.PoolName, poolname)
149+
assert.False(t, parentInfo.Image.Trash)
150+
151+
err = GroupSnapRemove(ioctx, gname, "groupsnap")
152+
assert.NoError(t, err)
153+
})
154+
}

0 commit comments

Comments
 (0)