diff --git a/rbd/snapshot_octopus.go b/rbd/snapshot_octopus.go new file mode 100644 index 0000000..765eb92 --- /dev/null +++ b/rbd/snapshot_octopus.go @@ -0,0 +1,66 @@ +// +build octopus + +package rbd + +// #cgo LDFLAGS: -lrbd +// #include +// #include +import "C" + +import ( + "unsafe" + + "github.com/ceph/go-ceph/internal/retry" +) + +// GetSnapID returns the snapshot ID for the given snapshot name. +// +// Implements: +// int rbd_snap_get_id(rbd_image_t image, const char *snapname, uint64_t *snap_id) +func (image *Image) GetSnapID(snapName string) (uint64, error) { + var snapID C.uint64_t + if err := image.validate(imageIsOpen); err != nil { + return uint64(snapID), err + } + if snapName == "" { + return uint64(snapID), ErrSnapshotNoName + } + + cSnapName := C.CString(snapName) + defer C.free(unsafe.Pointer(cSnapName)) + + ret := C.rbd_snap_get_id(image.image, cSnapName, &snapID) + return uint64(snapID), getError(ret) +} + +// GetSnapByID returns the snapshot name for the given snapshot ID. +// +// Implements: +// int rbd_snap_get_name(rbd_image_t image, uint64_t snap_id, char *snapname, size_t *name_len) +func (image *Image) GetSnapByID(snapID uint64) (string, error) { + if err := image.validate(imageIsOpen); err != nil { + return "", err + } + + var ( + buf []byte + err error + ) + // range from 1k to 64KiB + retry.WithSizes(1024, 1<<16, func(len int) retry.Hint { + cLen := C.size_t(len) + buf = make([]byte, cLen) + ret := C.rbd_snap_get_name( + image.image, + (C.uint64_t)(snapID), + (*C.char)(unsafe.Pointer(&buf[0])), + &cLen) + err = getError(ret) + return retry.Size(int(cLen)).If(err == errRange) + }) + + if err != nil { + return "", err + } + return C.GoString((*C.char)(unsafe.Pointer(&buf[0]))), nil +} diff --git a/rbd/snapshot_octopus_test.go b/rbd/snapshot_octopus_test.go new file mode 100644 index 0000000..71044cc --- /dev/null +++ b/rbd/snapshot_octopus_test.go @@ -0,0 +1,113 @@ +// +build octopus + +package rbd + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSnapIDFunctions(t *testing.T) { + conn := radosConnect(t) + poolName := GetUUID() + err := conn.MakePool(poolName) + assert.NoError(t, err) + ioctx, err := conn.OpenIOContext(poolName) + assert.NoError(t, err) + + defer func() { + ioctx.Destroy() + assert.NoError(t, conn.DeletePool(poolName)) + conn.Shutdown() + }() + + t.Run("happyPath", func(t *testing.T) { + imgName := "myImage" + img, err := Create(ioctx, imgName, testImageSize, testImageOrder, 1) + assert.NoError(t, err) + defer func() { + assert.NoError(t, img.Remove()) + }() + + img, err = OpenImage(ioctx, imgName, NoSnapshot) + assert.NoError(t, err) + defer func() { + assert.NoError(t, img.Close()) + }() + + snapName := "mySnap" + snapshot, err := img.CreateSnapshot(snapName) + assert.NoError(t, err) + defer func() { + assert.NoError(t, snapshot.Remove()) + }() + + snapID, err := img.GetSnapID(snapshot.name) + assert.NoError(t, err) + snapNameByID, err := img.GetSnapByID(snapID) + assert.NoError(t, err) + assert.Equal(t, snapshot.name, snapNameByID) + }) + + t.Run("closedImage", func(t *testing.T) { + imgName := "myImage" + closedImg, err := Create(ioctx, imgName, testImageSize, testImageOrder, 1) + assert.NoError(t, err) + defer func() { + assert.NoError(t, closedImg.Remove()) + }() + + img, err := OpenImage(ioctx, imgName, NoSnapshot) + assert.NoError(t, err) + snapName := "mySnap" + snapshot, err := img.CreateSnapshot(snapName) + assert.NoError(t, err) + + snapID, err := img.GetSnapID(snapshot.name) + assert.NoError(t, err) + + // close the image + assert.NoError(t, img.Close()) + + // try to get the ID again + _, err = img.GetSnapID(snapshot.name) + assert.Error(t, err) + + _, err = img.GetSnapByID(snapID) + assert.Error(t, err) + + // Open the image for snapshot removal + img, err = OpenImage(ioctx, imgName, NoSnapshot) + assert.NoError(t, err) + snapshot = img.GetSnapshot(snapName) + assert.NoError(t, snapshot.Remove()) + assert.NoError(t, img.Close()) + }) + + t.Run("invalidOptions", func(t *testing.T) { + imgName := "someImage" + img, err := Create(ioctx, imgName, testImageSize, testImageOrder, 1) + assert.NoError(t, err) + defer func() { + assert.NoError(t, img.Remove()) + }() + + img, err = OpenImage(ioctx, imgName, NoSnapshot) + assert.NoError(t, err) + defer func() { + assert.NoError(t, img.Close()) + }() + + _, err = img.GetSnapID("") + assert.Error(t, err) + + _, err = img.GetSnapID("someSnap") + assert.Error(t, err) + + var snapID uint64 + snapID = 22 + _, err = img.GetSnapByID(snapID) + assert.Error(t, err) + }) +}