diff --git a/rbd/mirror.go b/rbd/mirror.go index 46f2740..5955af6 100644 --- a/rbd/mirror.go +++ b/rbd/mirror.go @@ -165,3 +165,57 @@ func (image *Image) MirrorInstanceID() (string, error) { } return string(buf[:cSize]), nil } + +// MirrorImageState represents the mirroring state of a RBD image. +type MirrorImageState C.rbd_mirror_image_state_t + +const ( + // MirrorImageDisabling is the representation of + // RBD_MIRROR_IMAGE_DISABLING from librbd. + MirrorImageDisabling = MirrorImageState(C.RBD_MIRROR_IMAGE_DISABLING) + // MirrorImageEnabled is the representation of + // RBD_MIRROR_IMAGE_ENABLED from librbd. + MirrorImageEnabled = MirrorImageState(C.RBD_MIRROR_IMAGE_ENABLED) + // MirrorImageDisabled is the representation of + // RBD_MIRROR_IMAGE_DISABLED from librbd. + MirrorImageDisabled = MirrorImageState(C.RBD_MIRROR_IMAGE_DISABLED) +) + +// MirrorImageInfo represents the mirroring status information of a RBD image. +type MirrorImageInfo struct { + GlobalID string + State MirrorImageState + Primary bool +} + +// GetMirrorImageInfo fetches the mirroring status information of a RBD image. +// +// Implements: +// int rbd_mirror_image_get_info(rbd_image_t image, +// rbd_mirror_image_info_t *mirror_image_info, +// size_t info_size) +func (image *Image) GetMirrorImageInfo() (*MirrorImageInfo, error) { + if err := image.validate(imageIsOpen); err != nil { + return nil, err + } + + var cInfo C.rbd_mirror_image_info_t + + ret := C.rbd_mirror_image_get_info( + image.image, + &cInfo, + C.sizeof_rbd_mirror_image_info_t) + if ret < 0 { + return nil, getError(ret) + } + + mii := MirrorImageInfo{ + GlobalID: C.GoString(cInfo.global_id), + State: MirrorImageState(cInfo.state), + Primary: bool(cInfo.primary), + } + + // free C memory allocated by C.rbd_mirror_image_get_info call + C.rbd_mirror_image_get_info_cleanup(&cInfo) + return &mii, nil +} diff --git a/rbd/mirror_test.go b/rbd/mirror_test.go index fed76cb..2f49175 100644 --- a/rbd/mirror_test.go +++ b/rbd/mirror_test.go @@ -182,3 +182,53 @@ func TestMirroring(t *testing.T) { assert.Error(t, err) }) } + +func TestGetMirrorImageInfo(t *testing.T) { + conn := radosConnect(t) + poolName := GetUUID() + err := conn.MakePool(poolName) + require.NoError(t, err) + defer func() { + assert.NoError(t, conn.DeletePool(poolName)) + conn.Shutdown() + }() + + ioctx, err := conn.OpenIOContext(poolName) + assert.NoError(t, err) + defer func() { + ioctx.Destroy() + }() + + // enable per-image mirroring for this pool + err = SetMirrorMode(ioctx, MirrorModeImage) + require.NoError(t, err) + + imgName := GetUUID() + options := NewRbdImageOptions() + assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) + err = CreateImage(ioctx, imgName, testImageSize, options) + require.NoError(t, err) + + t.Run("closedImage", func(t *testing.T) { + img := GetImage(ioctx, imgName) + _, err = img.GetMirrorImageInfo() + assert.Error(t, err) + }) + + t.Run("getInfo", func(t *testing.T) { + // open image, enable, mirroring. + img, err := OpenImage(ioctx, imgName, NoSnapshot) + assert.NoError(t, err) + defer func() { + assert.NoError(t, img.Close()) + }() + + err = img.MirrorEnable(ImageMirrorModeSnapshot) + assert.NoError(t, err) + mii, err := img.GetMirrorImageInfo() + assert.NoError(t, err) + assert.NotNil(t, mii.GlobalID) + assert.Equal(t, mii.State, MirrorImageEnabled) + assert.Equal(t, mii.Primary, true) + }) +}