rbd: implement binding for rbd_mirror_image_instance_id_list

This commit adds MirrorImageInstanceIDList and
MirrorImageInstanceIDIter with necessary helper functions
and tests.

Fixes: #483

Signed-off-by: Rakshith R <rar@redhat.com>
This commit is contained in:
Rakshith R 2021-08-17 17:22:24 +05:30 committed by mergify[bot]
parent 1d2ef78c2f
commit a32355cba2
2 changed files with 256 additions and 0 deletions

View File

@ -908,3 +908,132 @@ func (iter *MirrorImageInfoIter) fetch() error {
}
return nil
}
// MirrorImageInstanceIDItem contains an ID string for a RBD image and
// its corresponding mirrored image's Instance ID.
type MirrorImageInstanceIDItem struct {
ID string
InstanceID string
}
// MirrorImageInstanceIDList returns a slice of MirrorImageInstanceIDItem. If
// the length of the returned slice equals max, the next chunk of the list can
// be obtained by setting start to the ID of the last item of the returned slice.
// If max is 0 a slice of all items is returned.
//
// Implements:
// int rbd_mirror_image_instance_id_list(
// rados_ioctx_t io_ctx,
// const char *start_id,
// size_t max, char **image_ids,
// char **instance_ids,
// size_t *len)
func MirrorImageInstanceIDList(
ioctx *rados.IOContext, start string,
max int) ([]MirrorImageInstanceIDItem, error) {
var (
result []MirrorImageInstanceIDItem
fetchAll bool
)
if max <= 0 {
max = iterBufSize
fetchAll = true
}
chunk := make([]MirrorImageInstanceIDItem, max)
for {
length, err := mirrorImageInstanceIDList(ioctx, start, chunk)
if err != nil {
return nil, err
}
result = append(result, chunk[:length]...)
if !fetchAll || length < max {
break
}
start = chunk[length-1].ID
}
return result, nil
}
func mirrorImageInstanceIDList(ioctx *rados.IOContext, start string,
results []MirrorImageInstanceIDItem) (int, error) {
cStart := C.CString(start)
defer C.free(unsafe.Pointer(cStart))
var (
max = C.size_t(len(results))
length = C.size_t(0)
ids = make([]*C.char, len(results))
instance_ids = make([]*C.char, len(results))
)
ret := C.rbd_mirror_image_instance_id_list(
cephIoctx(ioctx),
cStart,
max,
&ids[0],
&instance_ids[0],
&length,
)
if err := getError(ret); err != nil {
return 0, err
}
for i := 0; i < int(length); i++ {
results[i].ID = C.GoString(ids[i])
results[i].InstanceID = C.GoString(instance_ids[i])
}
C.rbd_mirror_image_instance_id_list_cleanup(
&ids[0],
&instance_ids[0],
length)
return int(length), getError(ret)
}
// MirrorImageInstanceIDIter provide methods for iterating over all
// the MirrorImageInstanceIDItem values in a pool.
type MirrorImageInstanceIDIter struct {
ioctx *rados.IOContext
buf []MirrorImageInstanceIDItem
lastID string
}
// NewMirrorImageInstanceIDIter creates a new iterator ready for use.
func NewMirrorImageInstanceIDIter(ioctx *rados.IOContext) *MirrorImageInstanceIDIter {
return &MirrorImageInstanceIDIter{
ioctx: ioctx,
}
}
// Next fetches one MirrorImageInstanceIDItem value or a nil value if iteration is
// exhausted. The error return will be non-nil if an underlying error fetching
// more values occurred.
func (iter *MirrorImageInstanceIDIter) Next() (*MirrorImageInstanceIDItem, error) {
if len(iter.buf) == 0 {
if err := iter.fetch(); err != nil {
return nil, err
}
if len(iter.buf) == 0 {
return nil, nil
}
iter.lastID = iter.buf[len(iter.buf)-1].ID
}
item := iter.buf[0]
iter.buf = iter.buf[1:]
return &item, nil
}
func (iter *MirrorImageInstanceIDIter) fetch() error {
iter.buf = nil
items := make([]MirrorImageInstanceIDItem, iterBufSize)
n, err := mirrorImageInstanceIDList(
iter.ioctx,
iter.lastID,
items)
if err != nil {
return err
}
if n > 0 {
iter.buf = items[:n]
}
return nil
}

View File

@ -971,3 +971,130 @@ func TestMirrorImageLists(t *testing.T) {
assert.Len(t, lst, 7)
})
}
func TestMirrorImageInstanceIDLists(t *testing.T) {
defer func(x int) {
iterBufSize = x
}(iterBufSize)
// shrink the buffer size in order to trigger more of the
// retry logic in the iter type
iterBufSize = 4
t.Run("instanceIDIterIoctxNil", func(t *testing.T) {
iter := NewMirrorImageInstanceIDIter(nil)
assert.Panics(t, func() {
iter.Next() //nolint:errcheck
})
})
mconfig := mirrorConfig()
if mconfig == "" {
t.Skip("no mirror config env var set")
}
conn := radosConnect(t)
defer conn.Shutdown()
poolName := GetUUID()
err := conn.MakePool(poolName)
require.NoError(t, err)
defer func() {
assert.NoError(t, conn.DeletePool(poolName))
}()
ioctx, err := conn.OpenIOContext(poolName)
assert.NoError(t, err)
defer func() {
ioctx.Destroy()
}()
err = SetMirrorMode(ioctx, MirrorModeImage)
require.NoError(t, err)
token, err := CreateMirrorPeerBootstrapToken(ioctx)
assert.NoError(t, err)
assert.GreaterOrEqual(t, len(token), 4)
conn2 := radosConnectConfig(t, mconfig)
defer conn2.Shutdown()
err = conn2.MakePool(poolName)
require.NoError(t, err)
defer func() {
assert.NoError(t, conn2.DeletePool(poolName))
}()
ioctx2, err := conn2.OpenIOContext(poolName)
assert.NoError(t, err)
defer func() {
ioctx2.Destroy()
}()
err = SetMirrorMode(ioctx2, MirrorModeImage)
require.NoError(t, err)
err = ImportMirrorPeerBootstrapToken(
ioctx2, MirrorPeerDirectionRxTx, token)
assert.NoError(t, err)
imgName := GetUUID()
options := NewRbdImageOptions()
assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder)))
for i := 0; i < 5; i++ {
name := fmt.Sprintf("%s%d", imgName, i)
err = CreateImage(ioctx, name, testImageSize, options)
require.NoError(t, err)
img, err := OpenImage(ioctx, name, NoSnapshot)
assert.NoError(t, err)
err = img.MirrorEnable(ImageMirrorModeSnapshot)
assert.NoError(t, err)
require.NoError(t, img.Close())
}
// wait for all the images to be mirrored
for i := 0; i < 30; i++ {
lst, err := MirrorImageInstanceIDList(ioctx, "", 0)
assert.NoError(t, err)
if len(lst) == 5 {
break
}
time.Sleep(time.Second)
}
t.Run("getInstanceID", func(t *testing.T) {
lst := []*MirrorImageInstanceIDItem{}
iter := NewMirrorImageInstanceIDIter(ioctx)
for {
istatus, err := iter.Next()
assert.NoError(t, err)
if istatus == nil {
break
}
lst = append(lst, istatus)
}
assert.Len(t, lst, 5)
assert.NoError(t, err)
})
t.Run("getInstanceIDSlice", func(t *testing.T) {
lst, err := MirrorImageInstanceIDList(ioctx, "", 0)
fmt.Print(lst)
assert.NoError(t, err)
assert.Len(t, lst, 5)
for i := 1; i < len(lst); i++ {
assert.NotEqual(t, lst[i-1].ID, lst[i].ID)
}
for i := 1; i <= iterBufSize; i++ {
lst, err := MirrorImageInstanceIDList(ioctx, "", i)
assert.NoError(t, err)
assert.Len(t, lst, i)
}
lst, err = MirrorImageInstanceIDList(ioctx, "", 3)
assert.NoError(t, err)
assert.Len(t, lst, 3)
lst, err = MirrorImageInstanceIDList(ioctx, lst[2].ID, 0)
assert.NoError(t, err)
assert.Len(t, lst, 2)
})
}