Skip to content

Commit

Permalink
rbd: Adding ListChildrenWithParams Api
Browse files Browse the repository at this point in the history
Api retrieves Image,Pool and Trash details of the existing Children.

Signed-off-by: ShravaniVangur <[email protected]>
  • Loading branch information
ShravaniVangur committed Feb 26, 2025
1 parent e84ad6c commit 7c165db
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 0 deletions.
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.ListChildrenAttributes",
"comment": "ListChildrenAttributes 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
188 changes: 188 additions & 0 deletions rbd/list_children_attributes_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 TestListChildrenAttributes(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.ListChildrenAttributes()
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.ListChildrenAttributes()
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.ListChildrenAttributes()
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.ListChildrenAttributes()
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 ListChildrenAttributes
err = childImg2.Flatten()
assert.NoError(t, err, "Failed to flatten cloned child image")

result, err = snapImg.ListChildrenAttributes()
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.ListChildrenAttributes()
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")
}
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
}

// ListChildrenAttributes 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) ListChildrenAttributes() (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

0 comments on commit 7c165db

Please sign in to comment.