diff --git a/rbd/rbd_nautilus_test.go b/rbd/rbd_nautilus_test.go index 4e1cc12..39f3724 100644 --- a/rbd/rbd_nautilus_test.go +++ b/rbd/rbd_nautilus_test.go @@ -123,3 +123,80 @@ func TestSparsify(t *testing.T) { assert.Error(t, err) }) } + +func TestGetParent(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() + }() + + imgName := "parent" + 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()) + }() + + t.Run("ParentNotAvailable", func(t *testing.T) { + _, err := img.GetParent() + assert.Error(t, err) + assert.Equal(t, err, ErrNotFound) + }) + + t.Run("ParentAvailable", func(t *testing.T) { + cloneName := "child" + optionsClone := NewRbdImageOptions() + defer optionsClone.Destroy() + err := optionsClone.SetUint64(ImageOptionCloneFormat, 2) + assert.NoError(t, err) + + // Create a clone of the image using the snapshot. + err = CloneImage(ioctx, imgName, snapName, 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, imgName) + assert.Equal(t, parentInfo.Snap.SnapName, snapName) + assert.Equal(t, parentInfo.Image.PoolName, poolName) + // TODO: add a comaprison for snap ID + }) + + t.Run("ClosedImage", func(t *testing.T) { + closedImg, err := Create(ioctx, "someImage", testImageSize, testImageOrder, 1) + assert.NoError(t, err) + defer func() { + assert.NoError(t, closedImg.Remove()) + }() + _, err = closedImg.GetParent() + assert.Error(t, err) + }) +} diff --git a/rbd/snapshot_nautilus.go b/rbd/snapshot_nautilus.go index f0eed0c..3245457 100644 --- a/rbd/snapshot_nautilus.go +++ b/rbd/snapshot_nautilus.go @@ -65,6 +65,60 @@ func (image *Image) GetParentInfo(pool, name, snapname []byte) error { return nil } +// ImageSpec represents the image information. +type ImageSpec struct { + ImageName string + PoolName string +} + +// SnapSpec represents the snapshot infomation. +type SnapSpec struct { + ID uint64 + SnapName string +} + +// ParentInfo represents the parent image and the parent snapshot information. +type ParentInfo struct { + Image ImageSpec + Snap SnapSpec +} + +// GetParent looks for the parent of the image and returns the parent image +// information which includes the image name, the pool name and +// the snapshot information. +// +// Implements: +// int rbd_get_parent(rbd_image_t image, rbd_linked_image_spec_t *parent_image, rbd_snap_spec_t *parent_snap) +func (image *Image) GetParent() (*ParentInfo, error) { + if err := image.validate(imageIsOpen); err != nil { + return nil, err + } + + parentImage := C.rbd_linked_image_spec_t{} + parentSnap := C.rbd_snap_spec_t{} + ret := C.rbd_get_parent(image.image, &parentImage, &parentSnap) + if ret != 0 { + return nil, getError(ret) + } + defer C.rbd_linked_image_spec_cleanup(&parentImage) + defer C.rbd_snap_spec_cleanup(&parentSnap) + + imageSpec := ImageSpec{ + ImageName: C.GoString(parentImage.image_name), + PoolName: C.GoString(parentImage.pool_name), + } + + snapSpec := SnapSpec{ + ID: uint64(parentSnap.id), + SnapName: C.GoString(parentSnap.name), + } + + return &ParentInfo{ + Image: imageSpec, + Snap: snapSpec, + }, nil +} + // ListChildren returns arrays with the pools and names of the images that are // children of the given image. The index of the pools and images arrays can be // used to link the two items together.