Skip to content
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

rbd: Adding ListChildrenWithParams Api #1079

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions docs/api-status.json
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,10 @@
"name": "Image.ListChildren",
"comment": "ListChildren returns arrays with the pools and names of the images that are\nchildren of the given image. The index of the pools and images arrays can be\nused to link the two items together.\n\nImplements:\n int rbd_list_children3(rbd_image_t image, rbd_linked_image_spec_t *images,\n size_t *max_images);\n"
},
{
"name": "Image.ListChildrenWithParams",
"comment": "ListChildrenWithparams returns an array of struct with the names and ids of the\nimages and pools and the trash of the images that are children of the given image.\n\nImplements:\n int rbd_list_children3(rbd_image_t image, rbd_linked_image_spec_t *images,\n size_t *max_images);\n"
},
{
"name": "Image.SetSnapByID",
"comment": "SetSnapByID updates the rbd image (not the Snapshot) such that the snapshot\nis the source of readable data.\n\nImplements:\n int rbd_snap_set_by_id(rbd_image_t image, uint64_t snap_id);\n"
Expand Down
44 changes: 44 additions & 0 deletions rbd/snapshot_nautilus.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,50 @@ func (image *Image) ListChildren() (pools []string, images []string, err error)
return pools, images, nil
}

// ListChildrenWithParams returns an array of struct with the names and ids of the
// images and pools and the trash of the images that are children of the given image.
//
// Implements:
//
// int rbd_list_children3(rbd_image_t image, rbd_linked_image_spec_t *images,
// size_t *max_images);
func (image *Image) ListChildrenWithParams() (imgSpec []ImageSpec, err error) {
if err := image.validate(imageIsOpen); err != nil {
return nil, err
}
var (
csize C.size_t
children []C.rbd_linked_image_spec_t
)
retry.WithSizes(16, 4096, func(size int) retry.Hint {
csize = C.size_t(size)
children = make([]C.rbd_linked_image_spec_t, csize)
ret := C.rbd_list_children3(
image.image,
(*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])),
&csize)
err = getErrorIfNegative(ret)
return retry.Size(int(csize)).If(err == errRange)
})
if err != nil {
return nil, err
}
defer C.rbd_linked_image_spec_list_cleanup((*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])), csize)

imgSpec = make([]ImageSpec, csize)
for i, child := range children[:csize] {
imgSpec[i] = ImageSpec{
ImageName: C.GoString(child.image_name),
ImageID: C.GoString(child.image_id),
PoolName: C.GoString(child.pool_name),
PoolNamespace: C.GoString(child.pool_namespace),
PoolID: uint64(child.pool_id),
Trash: bool(child.trash),
}
}
return imgSpec, nil
}

// SetSnapByID updates the rbd image (not the Snapshot) such that the snapshot
// is the source of readable data.
//
Expand Down
188 changes: 188 additions & 0 deletions rbd/snapshot_nautilus_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package rbd

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestListChildrenWithParams(t *testing.T) {
conn := radosConnect(t)
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()

namespace := "ns01"
err = NamespaceCreate(ioctx, namespace)
assert.NoError(t, err)

ioctx.SetNamespace(namespace)

parentName := "parent"
img, err := Create(ioctx, parentName, testImageSize, testImageOrder, 1)
assert.NoError(t, err)
defer img.Remove()

img, err = OpenImage(ioctx, parentName, NoSnapshot)
assert.NoError(t, err)
defer img.Close()

snapName := "snapshot"
snapshot, err := img.CreateSnapshot(snapName)
assert.NoError(t, err)
defer snapshot.Remove()

err = snapshot.Protect()
assert.NoError(t, err)

snapImg, err := OpenImage(ioctx, parentName, snapName)
assert.NoError(t, err)
defer snapImg.Close()

// ensure no children prior to clone
result, err := snapImg.ListChildrenWithParams()
assert.NoError(t, err)
assert.Equal(t, len(result), 0, "List should be empty before cloning")

//create first child image
childImage01 := "childImage01"
_, err = img.Clone(snapName, ioctx, childImage01, 1, testImageOrder)
assert.NoError(t, err)

_, err = OpenImage(ioctx, childImage01, NoSnapshot)
require.NoError(t, err, "Failed to open cloned child image")

//retrieve and validate child image properties
result, err = snapImg.ListChildrenWithParams()
assert.NoError(t, err)
assert.Equal(t, len(result), 1, "List should contain one child image")

assert.Equal(t, childImage01, result[0].ImageName)
assert.NotNil(t, result[0].ImageID)
assert.NotNil(t, result[0].PoolID)
assert.Equal(t, poolName, result[0].PoolName)
assert.Equal(t, namespace, result[0].PoolNamespace)
assert.False(t, result[0].Trash, "Newly cloned image should not be in trash")

//parent image cannot be deleted while having children attached to it
err = img.Remove()
assert.Error(t, err, "Expected an error but got nil")

childImg1, err := OpenImage(ioctx, childImage01, NoSnapshot)
assert.NoError(t, err)
defer childImg1.Close()

//trash the image and validate
err = childImg1.Trash(0)
assert.NoError(t, err, "Failed to move child image to trash")

result, err = snapImg.ListChildrenWithParams()
assert.NoError(t, err)
assert.True(t, result[0].Trash, "Child image should be marked as trashed")

//validate for multiple clones by creating second child image
childImage02 := "childImage02"
_, err = img.Clone(snapName, ioctx, childImage02, 1, testImageOrder)
assert.NoError(t, err)

result, err = snapImg.ListChildrenWithParams()
assert.NoError(t, err)
require.Len(t, result, 2, "List should contain two child images")
assert.Equal(t, childImage02, result[1].ImageName)

//the first image is trashed where as the second is not
expectedChildren := map[string]bool{
childImage01: true,
childImage02: false,
}

for _, child := range result {
exists := expectedChildren[child.ImageName]
assert.Equal(t, exists, child.Trash)
}

childImg2, err := OpenImage(ioctx, childImage02, NoSnapshot)
require.NoError(t, err, "Failed to open cloned child image")
defer childImg2.Close()

//flattening the image should detach it from the parent and remove it from ListChildrenWithParams
err = childImg2.Flatten()
assert.NoError(t, err, "Failed to flatten cloned child image")

result, err = snapImg.ListChildrenWithParams()
assert.NoError(t, err)
assert.Equal(t, len(result), 1, "List should not contain the second image after flattening the clone")
assert.NotEqual(t, childImage02, result[0].ImageName)
}

func TestCloneInDifferentPool(t *testing.T) {
conn := radosConnect(t)
defer conn.Shutdown()

//create two pools:poolA(parent) and poolB(child)
poolA := GetUUID()
err := conn.MakePool(poolA)
require.NoError(t, err)
defer conn.DeletePool(poolA)

poolB := GetUUID()
err = conn.MakePool(poolB)
require.NoError(t, err)
defer conn.DeletePool(poolB)

//ensure that both the pools are not the same
assert.NotEqual(t, poolA, poolB)

ioctxA, err := conn.OpenIOContext(poolA)
require.NoError(t, err)
defer ioctxA.Destroy()
ioctxB, err := conn.OpenIOContext(poolB)
require.NoError(t, err)
defer ioctxB.Destroy()

//create a parent image in poolA
parentName := "parent-image"
img, err := Create(ioctxA, parentName, testImageSize, testImageOrder, 1)
assert.NoError(t, err)

img, err = OpenImage(ioctxA, parentName, NoSnapshot)
assert.NoError(t, err)
defer img.Close()

snapName := "snap01"
snapshot, err := img.CreateSnapshot(snapName)
assert.NoError(t, err)
defer snapshot.Remove()

err = snapshot.Protect()
assert.NoError(t, err)

snapImg, err := OpenImage(ioctxA, parentName, snapName)
assert.NoError(t, err)
defer snapImg.Close()

//create a child image in poolB
childImageName := "child-image"
_, err = img.Clone(snapName, ioctxB, childImageName, 1, testImageOrder)
assert.NoError(t, err, "Failed to clone image into poolB")

_, err = OpenImage(ioctxB, childImageName, NoSnapshot)
require.NoError(t, err, "Failed to open cloned child image in poolB")

//verify and validate properties of the child image
result, err := snapImg.ListChildrenWithParams()
assert.NoError(t, err)
require.Len(t, result, 1, "List should contain one child image")

child := result[0]
assert.Equal(t, childImageName, child.ImageName, "Child image name should match")
assert.Equal(t, poolB, child.PoolName, "Child image should be in poolB")
}