From f72828c5b29663a7ac3a189499e1155f5b585dc9 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Sun, 29 Dec 2019 19:31:49 -0500 Subject: [PATCH] rbd: add OpenImageById and OpenImageByIdReadOnly functions Add OpenImageById and OpenImageByIdReadOnly that wrap rbd_open_by_id and rbd_open_by_id_read_only respectively. The added test case can not currently test trivial error conditions due to a known bug in ceph, these tests are skipped for meanwhile. Signed-off-by: John Mulligan --- rbd/rbd.go | 73 ++++++++++++++++++++++++++++++++++++ rbd/rbd_test.go | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) diff --git a/rbd/rbd.go b/rbd/rbd.go index eef8619..eab4109 100644 --- a/rbd/rbd.go +++ b/rbd/rbd.go @@ -1356,6 +1356,79 @@ func OpenImageReadOnly(ioctx *rados.IOContext, name, snapName string) (*Image, e }, nil } +// OpenImageById will open an existing rbd image by ID and snapshot name, +// returning a new opened image. Pass the NoSnapshot sentinel value as the +// snapName to explicitly indicate that no snapshot name is being provided. +// Error handling will fail & segfault unless compiled with a version of ceph +// that fixes https://tracker.ceph.com/issues/43178 +// +// Implements: +// int rbd_open_by_id(rados_ioctx_t io, const char *id, +// rbd_image_t *image, const char *snap_name); +func OpenImageById(ioctx *rados.IOContext, id, snapName string) (*Image, error) { + cid := C.CString(id) + defer C.free(unsafe.Pointer(cid)) + + var cSnapName *C.char + if snapName != NoSnapshot { + cSnapName = C.CString(snapName) + defer C.free(unsafe.Pointer(cSnapName)) + } + + var cImage C.rbd_image_t + ret := C.rbd_open_by_id( + C.rados_ioctx_t(ioctx.Pointer()), + cid, + &cImage, + cSnapName) + + if ret != 0 { + return nil, getError(ret) + } + + return &Image{ + ioctx: ioctx, + image: cImage, + }, nil +} + +// OpenImageByIdReadOnly will open an existing rbd image by ID and snapshot +// name, returning a new opened-for-read image. Pass the NoSnapshot sentinel +// value as the snapName to explicitly indicate that no snapshot name is being +// provided. +// Error handling will fail & segfault unless compiled with a version of ceph +// that fixes https://tracker.ceph.com/issues/43178 +// +// Implements: +// int rbd_open_by_id_read_only(rados_ioctx_t io, const char *id, +// rbd_image_t *image, const char *snap_name); +func OpenImageByIdReadOnly(ioctx *rados.IOContext, id, snapName string) (*Image, error) { + cid := C.CString(id) + defer C.free(unsafe.Pointer(cid)) + + var cSnapName *C.char + if snapName != NoSnapshot { + cSnapName = C.CString(snapName) + defer C.free(unsafe.Pointer(cSnapName)) + } + + var cImage C.rbd_image_t + ret := C.rbd_open_by_id_read_only( + C.rados_ioctx_t(ioctx.Pointer()), + cid, + &cImage, + cSnapName) + + if ret != 0 { + return nil, getError(ret) + } + + return &Image{ + ioctx: ioctx, + image: cImage, + }, nil +} + // CreateImage creates a new rbd image using provided image options. // // Implements: diff --git a/rbd/rbd_test.go b/rbd/rbd_test.go index 9ac83c4..a55eb5e 100644 --- a/rbd/rbd_test.go +++ b/rbd/rbd_test.go @@ -1463,3 +1463,101 @@ func TestGetId(t *testing.T) { conn.DeletePool(poolname) conn.Shutdown() } + +func TestOpenImageById(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) + + name := GetUUID() + options := NewRbdImageOptions() + assert.NoError(t, + options.SetUint64(RbdImageOptionOrder, uint64(testImageOrder))) + err = CreateImage(ioctx, name, testImageSize, options) + assert.NoError(t, err) + + workingImage, err := OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + id, err := workingImage.GetId() + assert.NoError(t, err) + err = workingImage.Close() + assert.NoError(t, err) + + t.Run("ReadWriteBadId", func(t *testing.T) { + t.Skip("segfaults due to https://tracker.ceph.com/issues/43178") + // phony id + img, err := OpenImageById(ioctx, "102f00aaabbbccd", NoSnapshot) + require.Error(t, err) + require.Nil(t, img) + }) + t.Run("ReadOnlyBadId", func(t *testing.T) { + t.Skip("segfaults due to https://tracker.ceph.com/issues/43178") + // phony id + img, err := OpenImageByIdReadOnly(ioctx, "blubb", NoSnapshot) + require.Error(t, err) + require.Nil(t, img) + }) + t.Run("ReadWrite", func(t *testing.T) { + img, err := OpenImageById(ioctx, id, NoSnapshot) + require.NoError(t, err) + require.NotNil(t, img) + defer func() { assert.NoError(t, img.Close()) }() + + data := []byte("input data") + _, err = img.Write(data) + assert.NoError(t, err) + }) + t.Run("ReadOnly", func(t *testing.T) { + img, err := OpenImageByIdReadOnly(ioctx, id, NoSnapshot) + require.NoError(t, err) + require.NotNil(t, img) + defer func() { assert.NoError(t, img.Close()) }() + + data := []byte("input data") + _, err = img.Write(data) + // writing should fail in read-only mode + assert.Error(t, err) + }) + t.Run("Snapshot", func(t *testing.T) { + img, err := OpenImageById(ioctx, id, NoSnapshot) + require.NoError(t, err) + require.NotNil(t, img) + defer func() { assert.NoError(t, img.Close()) }() + + snapshot, err := img.CreateSnapshot("snerpshort") + assert.NoError(t, err) + defer func() { assert.NoError(t, snapshot.Remove()) }() + + snapImage, err := OpenImageById(ioctx, id, "snerpshort") + require.NoError(t, err) + require.NotNil(t, snapImage) + assert.NoError(t, snapImage.Close()) + }) + t.Run("ReadOnlySnapshot", func(t *testing.T) { + img, err := OpenImageById(ioctx, id, NoSnapshot) + require.NoError(t, err) + require.NotNil(t, img) + defer func() { assert.NoError(t, img.Close()) }() + + snapshot, err := img.CreateSnapshot("snerpshort2") + assert.NoError(t, err) + defer func() { assert.NoError(t, snapshot.Remove()) }() + + snapImage, err := OpenImageByIdReadOnly(ioctx, id, "snerpshort2") + require.NoError(t, err) + require.NotNil(t, snapImage) + assert.NoError(t, snapImage.Close()) + }) + + err = workingImage.Remove() + assert.NoError(t, err) + + ioctx.Destroy() + conn.DeletePool(poolname) + conn.Shutdown() +}