rbd: add GetGlobalMirrorStatus implementing rbd_mirror_image_get_global_status

This function is added, along with required return types and some helper
functions, to support fetching the global mirroring status component
that makes up a major part of `rbd mirror image status` command's
output.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
John Mulligan 2021-02-22 17:34:31 -05:00 committed by mergify[bot]
parent dfa74510aa
commit 5c537e20ad
3 changed files with 208 additions and 5 deletions

View File

@ -71,6 +71,12 @@ var (
// revive:enable:exported
)
// Public general error
const (
// ErrNotExist indicates a non-specific missing resource.
ErrNotExist = rbdError(-C.ENOENT)
)
// Private errors:
const (

View File

@ -13,6 +13,8 @@ package rbd
import "C"
import (
"fmt"
"math"
"unsafe"
"github.com/ceph/go-ceph/internal/retry"
@ -228,6 +230,14 @@ type MirrorImageInfo struct {
Primary bool
}
func convertMirrorImageInfo(cInfo *C.rbd_mirror_image_info_t) MirrorImageInfo {
return MirrorImageInfo{
GlobalID: C.GoString(cInfo.global_id),
State: MirrorImageState(cInfo.state),
Primary: bool(cInfo.primary),
}
}
// GetMirrorImageInfo fetches the mirroring status information of a RBD image.
//
// Implements:
@ -249,11 +259,7 @@ func (image *Image) GetMirrorImageInfo() (*MirrorImageInfo, error) {
return nil, getError(ret)
}
mii := MirrorImageInfo{
GlobalID: C.GoString(cInfo.global_id),
State: MirrorImageState(cInfo.state),
Primary: bool(cInfo.primary),
}
mii := convertMirrorImageInfo(&cInfo)
// free C memory allocated by C.rbd_mirror_image_get_info call
C.rbd_mirror_image_get_info_cleanup(&cInfo)
@ -273,3 +279,132 @@ func (image *Image) GetImageMirrorMode() (ImageMirrorMode, error) {
ret := C.rbd_mirror_image_get_mode(image.image, &mode)
return ImageMirrorMode(mode), getError(ret)
}
// MirrorImageStatusState is used to indicate the state of a mirrored image
// within the site status info.
type MirrorImageStatusState int64
const (
// MirrorImageStatusStateUnknown is equivalent to MIRROR_IMAGE_STATUS_STATE_UNKNOWN
MirrorImageStatusStateUnknown = MirrorImageStatusState(C.MIRROR_IMAGE_STATUS_STATE_UNKNOWN)
// MirrorImageStatusStateError is equivalent to MIRROR_IMAGE_STATUS_STATE_ERROR
MirrorImageStatusStateError = MirrorImageStatusState(C.MIRROR_IMAGE_STATUS_STATE_ERROR)
// MirrorImageStatusStateSyncing is equivalent to MIRROR_IMAGE_STATUS_STATE_SYNCING
MirrorImageStatusStateSyncing = MirrorImageStatusState(C.MIRROR_IMAGE_STATUS_STATE_SYNCING)
// MirrorImageStatusStateStartingReplay is equivalent to MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY
MirrorImageStatusStateStartingReplay = MirrorImageStatusState(C.MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY)
// MirrorImageStatusStateReplaying is equivalent to MIRROR_IMAGE_STATUS_STATE_REPLAYING
MirrorImageStatusStateReplaying = MirrorImageStatusState(C.MIRROR_IMAGE_STATUS_STATE_REPLAYING)
// MirrorImageStatusStateStoppingReplay is equivalent to MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY
MirrorImageStatusStateStoppingReplay = MirrorImageStatusState(C.MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY)
// MirrorImageStatusStateStopped is equivalent to MIRROR_IMAGE_STATUS_STATE_STOPPED
MirrorImageStatusStateStopped = MirrorImageStatusState(C.MIRROR_IMAGE_STATUS_STATE_STOPPED)
)
// String represents the MirrorImageStatusState as a short string.
func (state MirrorImageStatusState) String() (s string) {
switch state {
case MirrorImageStatusStateUnknown:
s = "unknown"
case MirrorImageStatusStateError:
s = "error"
case MirrorImageStatusStateSyncing:
s = "syncing"
case MirrorImageStatusStateStartingReplay:
s = "starting_replay"
case MirrorImageStatusStateReplaying:
s = "replaying"
case MirrorImageStatusStateStoppingReplay:
s = "stopping_replay"
case MirrorImageStatusStateStopped:
s = "stopped"
default:
s = fmt.Sprintf("unknown(%d)", state)
}
return s
}
// SiteMirrorImageStatus contains information pertaining to the status of
// a mirrored image within a site.
type SiteMirrorImageStatus struct {
MirrorUUID string
State MirrorImageStatusState
Description string
LastUpdate int64
Up bool
}
// GlobalMirrorImageStatus contains information pertaining to the global
// status of a mirrored image. It contains general information as well
// as per-site information stored in the SiteStatuses slice.
type GlobalMirrorImageStatus struct {
Name string
Info MirrorImageInfo
SiteStatuses []SiteMirrorImageStatus
}
// LocalStatus returns one SiteMirrorImageStatus item from the SiteStatuses
// slice that corresponds to the local site's status. If the local status
// is not found than the error ErrNotExist will be returned.
func (gmis GlobalMirrorImageStatus) LocalStatus() (SiteMirrorImageStatus, error) {
var (
ss SiteMirrorImageStatus
err error = ErrNotExist
)
for i := range gmis.SiteStatuses {
// I couldn't find it explicitly documented, but a site mirror uuid
// of an empty string indicates that this is the local site.
// This pattern occurs in both the pybind code and ceph c++.
if gmis.SiteStatuses[i].MirrorUUID == "" {
ss = gmis.SiteStatuses[i]
err = nil
break
}
}
return ss, err
}
type siteArray [math.MaxInt32]C.rbd_mirror_image_site_status_t
// GetGlobalMirrorStatus returns status information pertaining to the state
// of the images's mirroring.
//
// Implements:
// int rbd_mirror_image_get_global_status(
// rbd_image_t image,
// rbd_mirror_image_global_status_t *mirror_image_global_status,
// size_t status_size);
func (image *Image) GetGlobalMirrorStatus() (GlobalMirrorImageStatus, error) {
if err := image.validate(imageIsOpen); err != nil {
return GlobalMirrorImageStatus{}, err
}
s := C.rbd_mirror_image_global_status_t{}
ret := C.rbd_mirror_image_get_global_status(
image.image,
&s,
C.sizeof_rbd_mirror_image_global_status_t)
if err := getError(ret); err != nil {
return GlobalMirrorImageStatus{}, err
}
defer C.rbd_mirror_image_global_status_cleanup(&s)
status := GlobalMirrorImageStatus{
Name: C.GoString(s.name),
Info: convertMirrorImageInfo(&s.info),
SiteStatuses: make([]SiteMirrorImageStatus, s.site_statuses_count),
}
// use the "Sven Technique" to treat the C pointer as a go slice temporarily
sscs := (*siteArray)(unsafe.Pointer(s.site_statuses))[:s.site_statuses_count:s.site_statuses_count]
for i := C.uint32_t(0); i < s.site_statuses_count; i++ {
ss := sscs[i]
status.SiteStatuses[i] = SiteMirrorImageStatus{
MirrorUUID: C.GoString(ss.mirror_uuid),
State: MirrorImageStatusState(ss.state),
Description: C.GoString(ss.description),
LastUpdate: int64(ss.last_update),
Up: bool(ss.up),
}
}
return status, nil
}

View File

@ -262,3 +262,65 @@ func TestMirrorConstantStrings(t *testing.T) {
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)
}
})
}