Skip to content

Commit 1bb42ac

Browse files
rbd: add ListChildrenAttributes Api
Api retrieves Image,Pool and Trash details of the existing Children. Signed-off-by: ShravaniVangur <[email protected]>
1 parent e84ad6c commit 1bb42ac

File tree

3 files changed

+236
-0
lines changed

3 files changed

+236
-0
lines changed

docs/api-status.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,6 +1766,10 @@
17661766
"name": "Image.ListChildren",
17671767
"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"
17681768
},
1769+
{
1770+
"name": "Image.ListChildrenAttributes",
1771+
"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"
1772+
},
17691773
{
17701774
"name": "Image.SetSnapByID",
17711775
"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"

rbd/list_children_attributes_test.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package rbd
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestListChildrenAttributes(t *testing.T) {
11+
conn := radosConnect(t)
12+
defer conn.Shutdown()
13+
14+
poolName := GetUUID()
15+
err := conn.MakePool(poolName)
16+
require.NoError(t, err)
17+
defer conn.DeletePool(poolName)
18+
19+
ioctx, err := conn.OpenIOContext(poolName)
20+
require.NoError(t, err)
21+
defer ioctx.Destroy()
22+
23+
namespace := "ns01"
24+
err = NamespaceCreate(ioctx, namespace)
25+
assert.NoError(t, err)
26+
27+
ioctx.SetNamespace(namespace)
28+
29+
parentName := "parent"
30+
img, err := Create(ioctx, parentName, testImageSize, testImageOrder, 1)
31+
assert.NoError(t, err)
32+
defer img.Remove()
33+
34+
img, err = OpenImage(ioctx, parentName, NoSnapshot)
35+
assert.NoError(t, err)
36+
defer img.Close()
37+
38+
snapName := "snapshot"
39+
snapshot, err := img.CreateSnapshot(snapName)
40+
assert.NoError(t, err)
41+
defer snapshot.Remove()
42+
43+
err = snapshot.Protect()
44+
assert.NoError(t, err)
45+
46+
snapImg, err := OpenImage(ioctx, parentName, snapName)
47+
assert.NoError(t, err)
48+
defer snapImg.Close()
49+
50+
// ensure no children prior to clone
51+
result, err := snapImg.ListChildrenAttributes()
52+
assert.NoError(t, err)
53+
assert.Equal(t, len(result), 0, "List should be empty before cloning")
54+
55+
//create first child image
56+
childImage01 := "childImage01"
57+
_, err = img.Clone(snapName, ioctx, childImage01, 1, testImageOrder)
58+
assert.NoError(t, err)
59+
60+
_, err = OpenImage(ioctx, childImage01, NoSnapshot)
61+
require.NoError(t, err, "Failed to open cloned child image")
62+
63+
//retrieve and validate child image properties
64+
result, err = snapImg.ListChildrenAttributes()
65+
assert.NoError(t, err)
66+
assert.Equal(t, len(result), 1, "List should contain one child image")
67+
68+
assert.Equal(t, childImage01, result[0].ImageName)
69+
assert.NotNil(t, result[0].ImageID)
70+
assert.NotNil(t, result[0].PoolID)
71+
assert.Equal(t, poolName, result[0].PoolName)
72+
assert.Equal(t, namespace, result[0].PoolNamespace)
73+
assert.False(t, result[0].Trash, "Newly cloned image should not be in trash")
74+
75+
//parent image cannot be deleted while having children attached to it
76+
err = img.Remove()
77+
assert.Error(t, err, "Expected an error but got nil")
78+
79+
childImg1, err := OpenImage(ioctx, childImage01, NoSnapshot)
80+
assert.NoError(t, err)
81+
defer childImg1.Close()
82+
83+
//trash the image and validate
84+
err = childImg1.Trash(0)
85+
assert.NoError(t, err, "Failed to move child image to trash")
86+
87+
result, err = snapImg.ListChildrenAttributes()
88+
assert.NoError(t, err)
89+
assert.True(t, result[0].Trash, "Child image should be marked as trashed")
90+
91+
//validate for multiple clones by creating second child image
92+
childImage02 := "childImage02"
93+
_, err = img.Clone(snapName, ioctx, childImage02, 1, testImageOrder)
94+
assert.NoError(t, err)
95+
96+
result, err = snapImg.ListChildrenAttributes()
97+
assert.NoError(t, err)
98+
require.Len(t, result, 2, "List should contain two child images")
99+
assert.Equal(t, childImage02, result[1].ImageName)
100+
101+
//the first image is trashed where as the second is not
102+
expectedChildren := map[string]bool{
103+
childImage01: true,
104+
childImage02: false,
105+
}
106+
107+
for _, child := range result {
108+
exists := expectedChildren[child.ImageName]
109+
assert.Equal(t, exists, child.Trash)
110+
}
111+
112+
childImg2, err := OpenImage(ioctx, childImage02, NoSnapshot)
113+
require.NoError(t, err, "Failed to open cloned child image")
114+
defer childImg2.Close()
115+
116+
//flattening the image should detach it from the parent and remove it from ListChildrenAttributes
117+
err = childImg2.Flatten()
118+
assert.NoError(t, err, "Failed to flatten cloned child image")
119+
120+
result, err = snapImg.ListChildrenAttributes()
121+
assert.NoError(t, err)
122+
assert.Equal(t, len(result), 1, "List should not contain the second image after flattening the clone")
123+
assert.NotEqual(t, childImage02, result[0].ImageName)
124+
}
125+
126+
func TestCloneInDifferentPool(t *testing.T) {
127+
conn := radosConnect(t)
128+
defer conn.Shutdown()
129+
130+
//create two pools:poolA(parent) and poolB(child)
131+
poolA := GetUUID()
132+
err := conn.MakePool(poolA)
133+
require.NoError(t, err)
134+
defer conn.DeletePool(poolA)
135+
136+
poolB := GetUUID()
137+
err = conn.MakePool(poolB)
138+
require.NoError(t, err)
139+
defer conn.DeletePool(poolB)
140+
141+
//ensure that both the pools are not the same
142+
assert.NotEqual(t, poolA, poolB)
143+
144+
ioctxA, err := conn.OpenIOContext(poolA)
145+
require.NoError(t, err)
146+
defer ioctxA.Destroy()
147+
ioctxB, err := conn.OpenIOContext(poolB)
148+
require.NoError(t, err)
149+
defer ioctxB.Destroy()
150+
151+
//create a parent image in poolA
152+
parentName := "parent-image"
153+
img, err := Create(ioctxA, parentName, testImageSize, testImageOrder, 1)
154+
assert.NoError(t, err)
155+
156+
img, err = OpenImage(ioctxA, parentName, NoSnapshot)
157+
assert.NoError(t, err)
158+
defer img.Close()
159+
160+
snapName := "snap01"
161+
snapshot, err := img.CreateSnapshot(snapName)
162+
assert.NoError(t, err)
163+
defer snapshot.Remove()
164+
165+
err = snapshot.Protect()
166+
assert.NoError(t, err)
167+
168+
snapImg, err := OpenImage(ioctxA, parentName, snapName)
169+
assert.NoError(t, err)
170+
defer snapImg.Close()
171+
172+
//create a child image in poolB
173+
childImageName := "child-image"
174+
_, err = img.Clone(snapName, ioctxB, childImageName, 1, testImageOrder)
175+
assert.NoError(t, err, "Failed to clone image into poolB")
176+
177+
_, err = OpenImage(ioctxB, childImageName, NoSnapshot)
178+
require.NoError(t, err, "Failed to open cloned child image in poolB")
179+
180+
//verify and validate properties of the child image
181+
result, err := snapImg.ListChildrenAttributes()
182+
assert.NoError(t, err)
183+
require.Len(t, result, 1, "List should contain one child image")
184+
185+
child := result[0]
186+
assert.Equal(t, childImageName, child.ImageName, "Child image name should match")
187+
assert.Equal(t, poolB, child.PoolName, "Child image should be in poolB")
188+
}

rbd/snapshot_nautilus.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,50 @@ func (image *Image) ListChildren() (pools []string, images []string, err error)
168168
return pools, images, nil
169169
}
170170

171+
// ListChildrenAttributes returns an array of struct with the names and ids of the
172+
// images and pools and the trash of the images that are children of the given image.
173+
//
174+
// Implements:
175+
//
176+
// int rbd_list_children3(rbd_image_t image, rbd_linked_image_spec_t *images,
177+
// size_t *max_images);
178+
func (image *Image) ListChildrenAttributes() (imgSpec []ImageSpec, err error) {
179+
if err := image.validate(imageIsOpen); err != nil {
180+
return nil, err
181+
}
182+
var (
183+
csize C.size_t
184+
children []C.rbd_linked_image_spec_t
185+
)
186+
retry.WithSizes(16, 4096, func(size int) retry.Hint {
187+
csize = C.size_t(size)
188+
children = make([]C.rbd_linked_image_spec_t, csize)
189+
ret := C.rbd_list_children3(
190+
image.image,
191+
(*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])),
192+
&csize)
193+
err = getErrorIfNegative(ret)
194+
return retry.Size(int(csize)).If(err == errRange)
195+
})
196+
if err != nil {
197+
return nil, err
198+
}
199+
defer C.rbd_linked_image_spec_list_cleanup((*C.rbd_linked_image_spec_t)(unsafe.Pointer(&children[0])), csize)
200+
201+
imgSpec = make([]ImageSpec, csize)
202+
for i, child := range children[:csize] {
203+
imgSpec[i] = ImageSpec{
204+
ImageName: C.GoString(child.image_name),
205+
ImageID: C.GoString(child.image_id),
206+
PoolName: C.GoString(child.pool_name),
207+
PoolNamespace: C.GoString(child.pool_namespace),
208+
PoolID: uint64(child.pool_id),
209+
Trash: bool(child.trash),
210+
}
211+
}
212+
return imgSpec, nil
213+
}
214+
171215
// SetSnapByID updates the rbd image (not the Snapshot) such that the snapshot
172216
// is the source of readable data.
173217
//

0 commit comments

Comments
 (0)