mirror of
https://github.com/ceph/go-ceph
synced 2024-12-22 06:10:09 +00:00
15d71fe192
The librbd API for mirroring-related operations changed substantially between Nautilus and Octopus. Due to this, go-ceph had previously only implemented mirroring functionality if built against Octopus client libraries. This patch implements equivalent functionality for use with Nautilus clients. Signed-off-by: Sanford Miller <smiller@digitalocean.com>
1097 lines
28 KiB
Go
1097 lines
28 KiB
Go
//go:build !nautilus
|
|
// +build !nautilus
|
|
|
|
package rbd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGetMirrorUUID(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()
|
|
}()
|
|
|
|
// verify that mirroring is not enabled on this new pool
|
|
m, err := GetMirrorMode(ioctx)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, m, MirrorModeDisabled)
|
|
|
|
// enable per-image mirroring for this pool
|
|
err = SetMirrorMode(ioctx, MirrorModeImage)
|
|
require.NoError(t, err)
|
|
|
|
name1 := GetUUID()
|
|
options := NewRbdImageOptions()
|
|
assert.NoError(t,
|
|
options.SetUint64(ImageOptionOrder, uint64(testImageOrder)))
|
|
err = CreateImage(ioctx, name1, testImageSize, options)
|
|
require.NoError(t, err)
|
|
t.Run("getUUID", func(t *testing.T) {
|
|
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
assert.NoError(t, img.Close())
|
|
}()
|
|
|
|
err = img.MirrorEnable(ImageMirrorModeSnapshot)
|
|
assert.NoError(t, err)
|
|
miid, err := GetMirrorUUID(ioctx)
|
|
assert.NoError(t, err)
|
|
assert.NotEqual(t, miid, "")
|
|
})
|
|
}
|
|
func TestGetMirrorMode(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()
|
|
}()
|
|
|
|
t.Run("mirrorModeDisabled", func(t *testing.T) {
|
|
m, err := GetMirrorMode(ioctx)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, m, MirrorModeDisabled)
|
|
})
|
|
t.Run("mirrorModeEnabled", func(t *testing.T) {
|
|
err = SetMirrorMode(ioctx, MirrorModeImage)
|
|
require.NoError(t, err)
|
|
m, err := GetMirrorMode(ioctx)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, m, MirrorModeImage)
|
|
})
|
|
t.Run("ioctxNil", func(t *testing.T) {
|
|
assert.Panics(t, func() {
|
|
GetMirrorMode(nil)
|
|
})
|
|
})
|
|
|
|
}
|
|
|
|
func TestMirroring(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()
|
|
}()
|
|
|
|
// verify that mirroring is not enabled on this new pool
|
|
m, err := GetMirrorMode(ioctx)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, m, MirrorModeDisabled)
|
|
|
|
// enable per-image mirroring for this pool
|
|
err = SetMirrorMode(ioctx, MirrorModeImage)
|
|
require.NoError(t, err)
|
|
|
|
name1 := GetUUID()
|
|
options := NewRbdImageOptions()
|
|
assert.NoError(t,
|
|
options.SetUint64(ImageOptionOrder, uint64(testImageOrder)))
|
|
err = CreateImage(ioctx, name1, testImageSize, options)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("enableDisable", func(t *testing.T) {
|
|
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
assert.NoError(t, img.Close())
|
|
}()
|
|
|
|
err = img.MirrorEnable(ImageMirrorModeSnapshot)
|
|
assert.NoError(t, err)
|
|
|
|
mode, err := img.GetImageMirrorMode()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, mode, ImageMirrorModeSnapshot)
|
|
|
|
err = img.MirrorDisable(false)
|
|
assert.NoError(t, err)
|
|
})
|
|
t.Run("enableDisableInvalid", func(t *testing.T) {
|
|
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, img.Close())
|
|
|
|
err = img.MirrorEnable(ImageMirrorModeSnapshot)
|
|
assert.Error(t, err)
|
|
err = img.MirrorDisable(false)
|
|
assert.Error(t, err)
|
|
_, err = img.GetImageMirrorMode()
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("promoteDemote", func(t *testing.T) {
|
|
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
assert.NoError(t, img.Close())
|
|
}()
|
|
|
|
err = img.MirrorEnable(ImageMirrorModeSnapshot)
|
|
assert.NoError(t, err)
|
|
err = img.MirrorDemote()
|
|
assert.NoError(t, err)
|
|
err = img.MirrorPromote(false)
|
|
assert.NoError(t, err)
|
|
err = img.MirrorDisable(false)
|
|
assert.NoError(t, err)
|
|
})
|
|
t.Run("promoteDemoteInvalid", func(t *testing.T) {
|
|
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, img.Close())
|
|
|
|
err = img.MirrorDemote()
|
|
assert.Error(t, err)
|
|
err = img.MirrorPromote(false)
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("resync", func(t *testing.T) {
|
|
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
assert.NoError(t, img.Close())
|
|
}()
|
|
|
|
err = img.MirrorEnable(ImageMirrorModeSnapshot)
|
|
assert.NoError(t, err)
|
|
err = img.MirrorDemote()
|
|
assert.NoError(t, err)
|
|
err = img.MirrorResync()
|
|
assert.NoError(t, err)
|
|
err = img.MirrorDisable(true)
|
|
assert.NoError(t, err)
|
|
})
|
|
t.Run("resyncInvalid", func(t *testing.T) {
|
|
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, img.Close())
|
|
|
|
err = img.MirrorResync()
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("instanceId", func(t *testing.T) {
|
|
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
assert.NoError(t, img.Close())
|
|
}()
|
|
|
|
err = img.MirrorEnable(ImageMirrorModeSnapshot)
|
|
assert.NoError(t, err)
|
|
miid, err := img.MirrorInstanceID()
|
|
// this is not currently testable for the "success" case
|
|
// see also the ceph tree where nothing is asserted except
|
|
// that the error is raised.
|
|
// TODO(?): figure out how to test this
|
|
assert.Error(t, err)
|
|
assert.Equal(t, "", miid)
|
|
err = img.MirrorDisable(false)
|
|
assert.NoError(t, err)
|
|
})
|
|
t.Run("instanceIdInvalid", func(t *testing.T) {
|
|
img, err := OpenImage(ioctx, name1, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, img.Close())
|
|
|
|
_, err = img.MirrorInstanceID()
|
|
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)
|
|
})
|
|
}
|
|
|
|
func TestMirrorConstantStrings(t *testing.T) {
|
|
x := []struct {
|
|
s fmt.Stringer
|
|
t string
|
|
}{
|
|
{MirrorModeDisabled, "disabled"},
|
|
{MirrorModeImage, "image"},
|
|
{MirrorModePool, "pool"},
|
|
{MirrorMode(9999), "<unknown>"},
|
|
{ImageMirrorModeJournal, "journal"},
|
|
{ImageMirrorModeSnapshot, "snapshot"},
|
|
{ImageMirrorMode(9999), "<unknown>"},
|
|
{MirrorImageDisabling, "disabling"},
|
|
{MirrorImageEnabled, "enabled"},
|
|
{MirrorImageDisabled, "disabled"},
|
|
{MirrorImageState(9999), "<unknown>"},
|
|
}
|
|
for _, v := range x {
|
|
assert.Equal(t, v.s.String(), v.t)
|
|
}
|
|
}
|
|
|
|
func TestGetGlobalMirrorStatus(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.GetGlobalMirrorStatus()
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("getStatus", 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)
|
|
gms, err := img.GetGlobalMirrorStatus()
|
|
assert.NoError(t, err)
|
|
assert.NotEqual(t, "", gms.Name)
|
|
assert.NotEqual(t, "", gms.Info.GlobalID)
|
|
assert.Equal(t, gms.Info.State, MirrorImageEnabled)
|
|
assert.Equal(t, gms.Info.Primary, true)
|
|
if assert.Len(t, gms.SiteStatuses, 1) {
|
|
ss := gms.SiteStatuses[0]
|
|
assert.Equal(t, "", ss.MirrorUUID)
|
|
assert.Equal(t, MirrorImageStatusStateUnknown, ss.State, ss.State)
|
|
assert.Equal(t, "status not found", ss.Description)
|
|
assert.Equal(t, int64(0), ss.LastUpdate)
|
|
assert.False(t, ss.Up)
|
|
ls, err := gms.LocalStatus()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, ss, ls)
|
|
}
|
|
})
|
|
}
|
|
|
|
func mirrorConfig() string {
|
|
return os.Getenv("MIRROR_CONF")
|
|
}
|
|
|
|
func TestGetGlobalMirrorStatusMirroredPool(t *testing.T) {
|
|
mconfig := mirrorConfig()
|
|
if mconfig == "" {
|
|
t.Skip("no mirror config env var set")
|
|
}
|
|
conn := radosConnect(t)
|
|
// this test assumes the rbd pool already exists and is mirrored
|
|
// this must be set up previously by the CI or manually
|
|
poolName := "rbd"
|
|
|
|
ioctx, err := conn.OpenIOContext(poolName)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
ioctx.Destroy()
|
|
}()
|
|
|
|
imgName := GetUUID()
|
|
options := NewRbdImageOptions()
|
|
assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder)))
|
|
err = CreateImage(ioctx, imgName, testImageSize, options)
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
err = RemoveImage(ioctx, imgName)
|
|
assert.NoError(t, err)
|
|
}()
|
|
|
|
// this next section is not a t.Run because it must be unconditionally
|
|
// executed. It is wrapped in a func to use defer to close the img.
|
|
func() {
|
|
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)
|
|
|
|
mid, err := img.CreateMirrorSnapshot()
|
|
assert.NoError(t, err)
|
|
assert.NotEqual(t, 0, mid)
|
|
|
|
// wait for site statuses to get updated
|
|
for i := 0; i < 30; i++ {
|
|
gms, err := img.GetGlobalMirrorStatus()
|
|
assert.NoError(t, err)
|
|
if len(gms.SiteStatuses) > 1 {
|
|
break
|
|
}
|
|
time.Sleep(time.Second)
|
|
}
|
|
|
|
gms, err := img.GetGlobalMirrorStatus()
|
|
assert.NoError(t, err)
|
|
assert.NotEqual(t, "", gms.Name)
|
|
assert.NotEqual(t, "", gms.Info.GlobalID)
|
|
assert.Equal(t, gms.Info.State, MirrorImageEnabled)
|
|
assert.Equal(t, gms.Info.Primary, true)
|
|
if assert.Len(t, gms.SiteStatuses, 2) {
|
|
ss1 := gms.SiteStatuses[0]
|
|
assert.Equal(t, "", ss1.MirrorUUID)
|
|
assert.Equal(t, MirrorImageStatusStateStopped, ss1.State, ss1.State)
|
|
assert.Equal(t, "local image is primary", ss1.Description)
|
|
assert.Greater(t, ss1.LastUpdate, int64(0))
|
|
assert.True(t, ss1.Up)
|
|
ls, err := gms.LocalStatus()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, ss1, ls)
|
|
|
|
ss2 := gms.SiteStatuses[1]
|
|
assert.NotEqual(t, "", ss2.MirrorUUID)
|
|
assert.Equal(t, MirrorImageStatusStateReplaying, ss2.State, ss2.State)
|
|
assert.Contains(t, ss2.Description, "replaying,")
|
|
assert.Greater(t, ss2.LastUpdate, int64(0))
|
|
assert.True(t, ss2.Up)
|
|
}
|
|
}()
|
|
|
|
// test the results of GetGlobalMirrorStatus using the "other"
|
|
// mirror+pool as a source
|
|
t.Run("fromMirror", func(t *testing.T) {
|
|
conn := radosConnectConfig(t, mconfig)
|
|
ioctx2, err := conn.OpenIOContext(poolName)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
ioctx2.Destroy()
|
|
}()
|
|
|
|
img, err := OpenImage(ioctx2, imgName, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
assert.NoError(t, img.Close())
|
|
}()
|
|
|
|
// wait for site statuses to get updated
|
|
for i := 0; i < 30; i++ {
|
|
gms, err := img.GetGlobalMirrorStatus()
|
|
assert.NoError(t, err)
|
|
if len(gms.SiteStatuses) > 1 {
|
|
break
|
|
}
|
|
time.Sleep(time.Second)
|
|
}
|
|
|
|
gms, err := img.GetGlobalMirrorStatus()
|
|
assert.NoError(t, err)
|
|
assert.NotEqual(t, "", gms.Name)
|
|
assert.NotEqual(t, "", gms.Info.GlobalID)
|
|
assert.Equal(t, gms.Info.State, MirrorImageEnabled)
|
|
assert.Equal(t, gms.Info.Primary, false)
|
|
if assert.Len(t, gms.SiteStatuses, 2) {
|
|
ls, err := gms.LocalStatus()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "", ls.MirrorUUID)
|
|
assert.Equal(t, MirrorImageStatusStateReplaying, ls.State, ls.State)
|
|
assert.Contains(t, ls.Description, "replaying,")
|
|
assert.Greater(t, ls.LastUpdate, int64(0))
|
|
assert.True(t, ls.Up)
|
|
|
|
assert.Equal(t, ls, gms.SiteStatuses[0])
|
|
|
|
ss2 := gms.SiteStatuses[1]
|
|
assert.NotEqual(t, "", ss2.MirrorUUID)
|
|
assert.Equal(t, MirrorImageStatusStateStopped, ss2.State, ss2.State)
|
|
|
|
assert.Equal(t, "local image is primary", ss2.Description)
|
|
assert.Greater(t, ss2.LastUpdate, int64(0))
|
|
assert.True(t, ss2.Up)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMirrorImageStatusSummary(t *testing.T) {
|
|
t.Run("ioctxNil", func(t *testing.T) {
|
|
assert.Panics(t, func() {
|
|
MirrorImageStatusSummary(nil)
|
|
})
|
|
})
|
|
t.Run("emptyPool", func(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()
|
|
}()
|
|
|
|
ssum, err := MirrorImageStatusSummary(ioctx)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, ssum, 0)
|
|
})
|
|
t.Run("mirroredPool", testMirrorImageStatusSummaryMirroredPool)
|
|
}
|
|
|
|
func testMirrorImageStatusSummaryMirroredPool(t *testing.T) {
|
|
mconfig := mirrorConfig()
|
|
if mconfig == "" {
|
|
t.Skip("no mirror config env var set")
|
|
}
|
|
conn := radosConnect(t)
|
|
// this test assumes the rbd pool already exists and is mirrored
|
|
// this must be set up previously by the CI or manually
|
|
poolName := "rbd"
|
|
|
|
ioctx, err := conn.OpenIOContext(poolName)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
ioctx.Destroy()
|
|
}()
|
|
|
|
imgBase := GetUUID()
|
|
imgName1 := imgBase + "a"
|
|
imgName2 := imgBase + "b"
|
|
imgName3 := imgBase + "c"
|
|
imgName4 := imgBase + "d"
|
|
|
|
options := NewRbdImageOptions()
|
|
assert.NoError(t, options.SetUint64(ImageOptionOrder, uint64(testImageOrder)))
|
|
|
|
for _, n := range []string{imgName1, imgName2, imgName3, imgName4} {
|
|
err = CreateImage(ioctx, n, testImageSize, options)
|
|
require.NoError(t, err)
|
|
|
|
defer func(n string) {
|
|
err = RemoveImage(ioctx, n)
|
|
assert.NoError(t, err)
|
|
}(n)
|
|
}
|
|
|
|
mkMirror := func(n string) {
|
|
img, err := OpenImage(ioctx, n, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
assert.NoError(t, img.Close())
|
|
}()
|
|
|
|
err = img.MirrorEnable(ImageMirrorModeSnapshot)
|
|
assert.NoError(t, err)
|
|
|
|
mid, err := img.CreateMirrorSnapshot()
|
|
assert.NoError(t, err)
|
|
assert.NotEqual(t, 0, mid)
|
|
}
|
|
|
|
checkMirror := func(n string) {
|
|
img, err := OpenImage(ioctx, n, NoSnapshot)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
assert.NoError(t, img.Close())
|
|
}()
|
|
|
|
// wait for site statuses to get updated
|
|
for i := 0; i < 30; i++ {
|
|
gms, err := img.GetGlobalMirrorStatus()
|
|
assert.NoError(t, err)
|
|
if len(gms.SiteStatuses) > 1 {
|
|
break
|
|
}
|
|
time.Sleep(time.Second)
|
|
}
|
|
}
|
|
|
|
for _, n := range []string{imgName1, imgName3} {
|
|
mkMirror(n)
|
|
}
|
|
for _, n := range []string{imgName1, imgName3} {
|
|
checkMirror(n)
|
|
}
|
|
|
|
ssum, err := MirrorImageStatusSummary(ioctx)
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, ssum, 1) {
|
|
assert.Contains(t, ssum, MirrorImageStatusStateReplaying)
|
|
assert.GreaterOrEqual(t, ssum[MirrorImageStatusStateReplaying], uint(2))
|
|
}
|
|
|
|
// immediately going for status right after enabling mirroring and not
|
|
// waiting for things to settle should give us one unknown status
|
|
mkMirror(imgName2)
|
|
ssum, err = MirrorImageStatusSummary(ioctx)
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, ssum, 2) {
|
|
assert.Contains(t, ssum, MirrorImageStatusStateReplaying)
|
|
assert.GreaterOrEqual(t, ssum[MirrorImageStatusStateReplaying], uint(2))
|
|
assert.Contains(t, ssum, MirrorImageStatusStateUnknown)
|
|
assert.GreaterOrEqual(t, ssum[MirrorImageStatusStateUnknown], uint(1))
|
|
}
|
|
}
|
|
|
|
func TestMirrorSiteName(t *testing.T) {
|
|
t.Run("connNilGet", func(t *testing.T) {
|
|
assert.Panics(t, func() {
|
|
GetMirrorSiteName(nil)
|
|
})
|
|
})
|
|
t.Run("connNilSet", func(t *testing.T) {
|
|
assert.Panics(t, func() {
|
|
SetMirrorSiteName(nil, "foo")
|
|
})
|
|
})
|
|
t.Run("simple", func(t *testing.T) {
|
|
conn := radosConnect(t)
|
|
defer conn.Shutdown()
|
|
err := SetMirrorSiteName(conn, "rbd4eva")
|
|
assert.NoError(t, err)
|
|
n, err := GetMirrorSiteName(conn)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "rbd4eva", n)
|
|
|
|
err = SetMirrorSiteName(conn, "ceph_a")
|
|
assert.NoError(t, err)
|
|
n, err = GetMirrorSiteName(conn)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "ceph_a", n)
|
|
})
|
|
t.Run("twoCluster", func(t *testing.T) {
|
|
mconfig := mirrorConfig()
|
|
if mconfig == "" {
|
|
t.Skip("no mirror config env var set")
|
|
}
|
|
|
|
conn1 := radosConnect(t)
|
|
defer conn1.Shutdown()
|
|
conn2 := radosConnectConfig(t, mconfig)
|
|
defer conn2.Shutdown()
|
|
|
|
err := SetMirrorSiteName(conn1, "cluster_a")
|
|
assert.NoError(t, err)
|
|
err = SetMirrorSiteName(conn2, "cluster_b")
|
|
assert.NoError(t, err)
|
|
|
|
// verify the two conns are properly separate
|
|
n1, err := GetMirrorSiteName(conn1)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "cluster_a", n1)
|
|
|
|
n2, err := GetMirrorSiteName(conn2)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "cluster_b", n2)
|
|
})
|
|
}
|
|
|
|
func TestMirrorBootstrapToken(t *testing.T) {
|
|
t.Run("ioctxNilCreate", func(t *testing.T) {
|
|
assert.Panics(t, func() {
|
|
CreateMirrorPeerBootstrapToken(nil)
|
|
})
|
|
})
|
|
t.Run("ioctxNilImport", func(t *testing.T) {
|
|
assert.Panics(t, func() {
|
|
ImportMirrorPeerBootstrapToken(nil, MirrorPeerDirectionRxTx, "")
|
|
})
|
|
})
|
|
t.Run("justCreate", func(t *testing.T) {
|
|
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)
|
|
})
|
|
t.Run("roundTrip", func(t *testing.T) {
|
|
mconfig := mirrorConfig()
|
|
if mconfig == "" {
|
|
t.Skip("no mirror config env var set")
|
|
}
|
|
|
|
conn1 := radosConnect(t)
|
|
defer conn1.Shutdown()
|
|
|
|
poolName := GetUUID()
|
|
err := conn1.MakePool(poolName)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
assert.NoError(t, conn1.DeletePool(poolName))
|
|
}()
|
|
|
|
err = SetMirrorSiteName(conn1, "ceph_a")
|
|
require.NoError(t, err)
|
|
|
|
ioctx1, err := conn1.OpenIOContext(poolName)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
ioctx1.Destroy()
|
|
}()
|
|
|
|
err = SetMirrorMode(ioctx1, MirrorModeImage)
|
|
require.NoError(t, err)
|
|
|
|
token, err := CreateMirrorPeerBootstrapToken(ioctx1)
|
|
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))
|
|
}()
|
|
|
|
err = SetMirrorSiteName(conn2, "ceph_b")
|
|
require.NoError(t, err)
|
|
|
|
ioctx2, err := conn2.OpenIOContext(poolName)
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
ioctx2.Destroy()
|
|
}()
|
|
|
|
err = SetMirrorMode(ioctx2, MirrorModeImage)
|
|
require.NoError(t, err)
|
|
|
|
fmt.Printf("TOKEN: %s\n", string(token))
|
|
err = ImportMirrorPeerBootstrapToken(
|
|
ioctx2, MirrorPeerDirectionRxTx, token)
|
|
assert.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestMirrorImageLists(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
|
|
|
|
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)))
|
|
|
|
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())
|
|
}
|
|
|
|
for i := 5; i < 10; 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(ImageMirrorModeJournal)
|
|
assert.NoError(t, err)
|
|
require.NoError(t, img.Close())
|
|
}
|
|
|
|
t.Run("statusIterIoctxNil", func(t *testing.T) {
|
|
iter := NewMirrorImageGlobalStatusIter(nil)
|
|
defer iter.Close() //nolint:errcheck
|
|
assert.Panics(t, func() {
|
|
iter.Next() //nolint:errcheck
|
|
})
|
|
})
|
|
|
|
t.Run("infoIterIoctxNil", func(t *testing.T) {
|
|
iter := NewMirrorImageInfoIter(nil, nil)
|
|
assert.Panics(t, func() {
|
|
iter.Next() //nolint:errcheck
|
|
})
|
|
})
|
|
|
|
t.Run("getStatus", func(t *testing.T) {
|
|
lst := []*GlobalMirrorImageIDAndStatus{}
|
|
iter := NewMirrorImageGlobalStatusIter(ioctx)
|
|
for {
|
|
istatus, err := iter.Next()
|
|
assert.NoError(t, err)
|
|
if istatus == nil {
|
|
break
|
|
}
|
|
lst = append(lst, istatus)
|
|
}
|
|
assert.Len(t, lst, 10)
|
|
gms := lst[0].Status
|
|
assert.NoError(t, err)
|
|
assert.NotEqual(t, "", gms.Name)
|
|
assert.NotEqual(t, "", gms.Info.GlobalID)
|
|
assert.Equal(t, gms.Info.State, MirrorImageEnabled)
|
|
assert.Equal(t, gms.Info.Primary, false)
|
|
if assert.Len(t, gms.SiteStatuses, 1) {
|
|
ss := gms.SiteStatuses[0]
|
|
assert.Equal(t, "", ss.MirrorUUID)
|
|
assert.Equal(t, MirrorImageStatusStateUnknown, ss.State, ss.State)
|
|
assert.Equal(t, "status not found", ss.Description)
|
|
assert.Equal(t, int64(0), ss.LastUpdate)
|
|
assert.False(t, ss.Up)
|
|
ls, err := gms.LocalStatus()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, ss, ls)
|
|
}
|
|
})
|
|
|
|
t.Run("getStatusSlice", func(t *testing.T) {
|
|
lst, err := MirrorImageGlobalStatusList(ioctx, "", 0)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, lst, 10)
|
|
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 := MirrorImageGlobalStatusList(ioctx, "", i)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, lst, i)
|
|
}
|
|
lst, err = MirrorImageGlobalStatusList(ioctx, "", 3)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, lst, 3)
|
|
lst, err = MirrorImageGlobalStatusList(ioctx, lst[2].ID, 0)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, lst, 7)
|
|
})
|
|
|
|
t.Run("getInfo", func(t *testing.T) {
|
|
get := func(iter *MirrorImageInfoIter) []*MirrorImageInfoItem {
|
|
lst := []*MirrorImageInfoItem{}
|
|
for {
|
|
info, err := iter.Next()
|
|
assert.NoError(t, err)
|
|
if info == nil {
|
|
break
|
|
}
|
|
lst = append(lst, info)
|
|
}
|
|
return lst
|
|
}
|
|
iter := NewMirrorImageInfoIter(ioctx, nil)
|
|
lst := get(iter)
|
|
assert.Len(t, lst, 10)
|
|
item := lst[0]
|
|
assert.NotEqual(t, "", item.ID)
|
|
assert.NotEqual(t, "", item.Info.GlobalID)
|
|
assert.Equal(t, item.Info.State, MirrorImageEnabled)
|
|
assert.Equal(t, item.Info.Primary, true)
|
|
for i := 1; i < 10; i++ {
|
|
assert.NotEqual(t, lst[i-1].ID, lst[i].ID)
|
|
}
|
|
|
|
iter = NewMirrorImageInfoIter(ioctx, ImageMirrorModeJournal)
|
|
lst = get(iter)
|
|
assert.Len(t, lst, 5)
|
|
item = lst[0]
|
|
assert.Equal(t, item.Mode, ImageMirrorModeJournal)
|
|
assert.NotEqual(t, "", item.ID)
|
|
assert.NotEqual(t, "", item.Info.GlobalID)
|
|
assert.Equal(t, item.Info.State, MirrorImageEnabled)
|
|
assert.Equal(t, item.Info.Primary, true)
|
|
|
|
iter = NewMirrorImageInfoIter(ioctx, ImageMirrorModeSnapshot)
|
|
lst = get(iter)
|
|
assert.Len(t, lst, 5)
|
|
item = lst[0]
|
|
assert.Equal(t, item.Mode, ImageMirrorModeSnapshot)
|
|
assert.NotEqual(t, "", item.ID)
|
|
assert.NotEqual(t, "", item.Info.GlobalID)
|
|
assert.Equal(t, item.Info.State, MirrorImageEnabled)
|
|
assert.Equal(t, item.Info.Primary, true)
|
|
})
|
|
|
|
t.Run("getInfoSlice", func(t *testing.T) {
|
|
lst, err := MirrorImageInfoList(ioctx, nil, "", 0)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, lst, 10)
|
|
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 := MirrorImageInfoList(ioctx, nil, "", i)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, lst, i)
|
|
}
|
|
lst, err = MirrorImageInfoList(ioctx, ImageMirrorModeJournal, "", 0)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, lst, 5)
|
|
lst, err = MirrorImageInfoList(ioctx, ImageMirrorModeSnapshot, "", 0)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, lst, 5)
|
|
lst, err = MirrorImageInfoList(ioctx, nil, "", 3)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, lst, 3)
|
|
lst, err = MirrorImageInfoList(ioctx, nil, lst[2].ID, 0)
|
|
assert.NoError(t, err)
|
|
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)
|
|
})
|
|
}
|