rbd: implement live migration functions

Signed-off-by: Omid Yoosefi <omidyoosefi@ibm.com>
This commit is contained in:
Omid Yoosefi 2022-12-14 15:37:36 -05:00 committed by mergify[bot]
parent 2d1d51b762
commit a6a42e9c7b
4 changed files with 419 additions and 1 deletions

View File

@ -1675,7 +1675,44 @@
"became_stable_version": "v0.18.0"
}
],
"preview_api": []
"preview_api": [
{
"name": "MigrationPrepare",
"comment": "MigrationPrepare prepares a migration\ncreating a target image with a link\nto source and making source read-only.\n\nImplements:\n int rbd_migration_prepare(rados_ioctx_t ioctx,\n const char *image_name,\n rados_ioctx_t dest_ioctx,\n const char *dest_image_name,\n rbd_image_options_t opts);\n",
"added_in_version": "$NEXT_RELEASE",
"expected_stable_version": "$NEXT_RELEASE_STABLE"
},
{
"name": "MigrationPrepareImport",
"comment": "MigrationPrepareImport prepares a migration for import\nfrom a specified source to a new target image.\n\nImplements:\n int rbd_migration_prepare_import(const char *source_spec,\n rados_ioctx_t dest_ioctx,\n const char *dest_image_name,\n rbd_image_options_t opts);\n",
"added_in_version": "$NEXT_RELEASE",
"expected_stable_version": "$NEXT_RELEASE_STABLE"
},
{
"name": "MigrationExecute",
"comment": "MigrationExecute starts copying the image blocks\nfrom the source image to the target image.\n\nImplements:\n int rbd_migration_execute(rados_ioctx_t ioctx,\n const char *image_name);\n",
"added_in_version": "$NEXT_RELEASE",
"expected_stable_version": "$NEXT_RELEASE_STABLE"
},
{
"name": "MigrationCommit",
"comment": "MigrationCommit commits a migration after execution\nbreaking the relationship of image to the source.\n\nImplements:\n int rbd_migration_commit(rados_ioctx_t ioctx,\n const char *image_name);\n",
"added_in_version": "$NEXT_RELEASE",
"expected_stable_version": "$NEXT_RELEASE_STABLE"
},
{
"name": "MigrationAbort",
"comment": "MigrationAbort aborts a migration in progress\nbreaking the relationship of image to the source.\n\nImplements:\n int rbd_migration_abort(rados_ioctx_t ioctx,\n const char *image_name);\n",
"added_in_version": "$NEXT_RELEASE",
"expected_stable_version": "$NEXT_RELEASE_STABLE"
},
{
"name": "MigrationStatus",
"comment": "MigrationStatus retrieve status of a live migration\nfor the specified image.\n\nImplements:\n int rbd_migration_status(rados_ioctx_t ioctx,\n const char *image_name,\n rbd_image_migration_status_t *status,\n size_t status_size);\n",
"added_in_version": "$NEXT_RELEASE",
"expected_stable_version": "$NEXT_RELEASE_STABLE"
}
]
},
"rbd/admin": {
"stable_api": [

View File

@ -16,6 +16,17 @@ No Preview/Deprecated APIs found. All APIs are considered stable.
## Package: rbd
### Preview APIs
Name | Added in Version | Expected Stable Version |
---- | ---------------- | ----------------------- |
MigrationPrepare | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
MigrationPrepareImport | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
MigrationExecute | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
MigrationCommit | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
MigrationAbort | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
MigrationStatus | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
### Deprecated APIs
Name | Deprecated in Version | Expected Removal Version |

219
rbd/migration.go Normal file
View File

@ -0,0 +1,219 @@
//go:build !(octopus || nautilus) && ceph_preview
// +build !octopus,!nautilus,ceph_preview
package rbd
// #cgo LDFLAGS: -lrbd
// #include <stdlib.h>
// #include <rados/librados.h>
// #include <rbd/librbd.h>
import "C"
import (
"unsafe"
"github.com/ceph/go-ceph/rados"
)
// MigrationImageState denotes the current migration status of a given image.
type MigrationImageState int
const (
// MigrationImageUnknown is the representation of
// RBD_IMAGE_MIGRATION_STATE_UNKNOWN from librbd.
MigrationImageUnknown = MigrationImageState(C.RBD_IMAGE_MIGRATION_STATE_UNKNOWN)
// MigrationImageError is the representation of
// RBD_IMAGE_MIGRATION_STATE_ERROR from librbd.
MigrationImageError = MigrationImageState(C.RBD_IMAGE_MIGRATION_STATE_ERROR)
// MigrationImagePreparing is the representation of
// RBD_IMAGE_MIGRATION_STATE_PREPARING from librbd.
MigrationImagePreparing = MigrationImageState(C.RBD_IMAGE_MIGRATION_STATE_PREPARING)
// MigrationImagePrepared is the representation of
// RBD_IMAGE_MIGRATION_STATE_PREPARED from librbd.
MigrationImagePrepared = MigrationImageState(C.RBD_IMAGE_MIGRATION_STATE_PREPARED)
// MigrationImageExecuting is the representation of
// RBD_IMAGE_MIGRATION_STATE_EXECUTING from librbd.
MigrationImageExecuting = MigrationImageState(C.RBD_IMAGE_MIGRATION_STATE_EXECUTING)
// MigrationImageExecuted is the representation of
// RBD_IMAGE_MIGRATION_STATE_EXECUTED from librbd.
MigrationImageExecuted = MigrationImageState(C.RBD_IMAGE_MIGRATION_STATE_EXECUTED)
// MigrationImageAborting is the representation of
// RBD_IMAGE_MIGRATION_STATE_ABORTING from librbd.
MigrationImageAborting = MigrationImageState(C.RBD_IMAGE_MIGRATION_STATE_ABORTING)
)
// MigrationImageStatus provides information about the
// live migration progress of an image.
type MigrationImageStatus struct {
SourcePoolID int
SourcePoolNamespace string
SourceImageName string
SourceImageID string
DestPoolID int
DestPoolNamespace string
DestImageName string
DestImageID string
State MigrationImageState
StateDescription string
}
// MigrationPrepare prepares a migration
// creating a target image with a link
// to source and making source read-only.
//
// Implements:
// int rbd_migration_prepare(rados_ioctx_t ioctx,
// const char *image_name,
// rados_ioctx_t dest_ioctx,
// const char *dest_image_name,
// rbd_image_options_t opts);
//
func MigrationPrepare(ioctx *rados.IOContext, sourceImageName string, destIoctx *rados.IOContext, destImageName string, rio *ImageOptions) error {
cSourceImageName := C.CString(sourceImageName)
cDestImageName := C.CString(destImageName)
defer func() {
C.free(unsafe.Pointer(cSourceImageName))
C.free(unsafe.Pointer(cDestImageName))
}()
ret := C.rbd_migration_prepare(
cephIoctx(ioctx),
cSourceImageName,
cephIoctx(destIoctx),
cDestImageName,
C.rbd_image_options_t(rio.options))
return getError(ret)
}
// MigrationPrepareImport prepares a migration for import
// from a specified source to a new target image.
//
// Implements:
// int rbd_migration_prepare_import(const char *source_spec,
// rados_ioctx_t dest_ioctx,
// const char *dest_image_name,
// rbd_image_options_t opts);
//
func MigrationPrepareImport(sourceSpec string, ioctx *rados.IOContext, destImageName string, rio *ImageOptions) error {
cSourceSpec := C.CString(sourceSpec)
cDestImageName := C.CString(destImageName)
defer func() {
C.free(unsafe.Pointer(cSourceSpec))
C.free(unsafe.Pointer(cDestImageName))
}()
ret := C.rbd_migration_prepare_import(
cSourceSpec,
cephIoctx(ioctx),
cDestImageName,
C.rbd_image_options_t(rio.options))
return getError(ret)
}
// MigrationExecute starts copying the image blocks
// from the source image to the target image.
//
// Implements:
// int rbd_migration_execute(rados_ioctx_t ioctx,
// const char *image_name);
//
func MigrationExecute(ioctx *rados.IOContext, name string) error {
cName := C.CString(name)
defer func() {
C.free(unsafe.Pointer(cName))
}()
ret := C.rbd_migration_execute(
cephIoctx(ioctx),
cName)
return getError(ret)
}
// MigrationCommit commits a migration after execution
// breaking the relationship of image to the source.
//
// Implements:
// int rbd_migration_commit(rados_ioctx_t ioctx,
// const char *image_name);
//
func MigrationCommit(ioctx *rados.IOContext, name string) error {
cName := C.CString(name)
defer func() {
C.free(unsafe.Pointer(cName))
}()
ret := C.rbd_migration_commit(
cephIoctx(ioctx),
cName)
return getError(ret)
}
// MigrationAbort aborts a migration in progress
// breaking the relationship of image to the source.
//
// Implements:
// int rbd_migration_abort(rados_ioctx_t ioctx,
// const char *image_name);
//
func MigrationAbort(ioctx *rados.IOContext, name string) error {
cName := C.CString(name)
defer func() {
C.free(unsafe.Pointer(cName))
}()
ret := C.rbd_migration_abort(
cephIoctx(ioctx),
cName)
return getError(ret)
}
// MigrationStatus retrieve status of a live migration
// for the specified image.
//
// Implements:
// int rbd_migration_status(rados_ioctx_t ioctx,
// const char *image_name,
// rbd_image_migration_status_t *status,
// size_t status_size);
//
func MigrationStatus(ioctx *rados.IOContext, name string) (*MigrationImageStatus, error) {
cName := C.CString(name)
defer func() {
C.free(unsafe.Pointer(cName))
}()
var status C.rbd_image_migration_status_t
ret := C.rbd_migration_status(
cephIoctx(ioctx),
cName,
&status,
C.sizeof_rbd_image_migration_status_t)
if ret != 0 {
return nil, getError(ret)
}
defer func() {
C.rbd_migration_status_cleanup(&status)
}()
return &MigrationImageStatus{
SourcePoolID: int(status.source_pool_id),
SourcePoolNamespace: C.GoString(status.source_pool_namespace),
SourceImageName: C.GoString(status.source_image_name),
SourceImageID: C.GoString(status.source_image_id),
DestPoolID: int(status.dest_pool_id),
DestPoolNamespace: C.GoString(status.dest_pool_namespace),
DestImageName: C.GoString(status.dest_image_name),
DestImageID: C.GoString(status.dest_image_id),
State: MigrationImageState(status.state),
StateDescription: C.GoString(status.state_description),
}, nil
}

151
rbd/migration_test.go Normal file
View File

@ -0,0 +1,151 @@
//go:build !(octopus || nautilus) && ceph_preview
// +build !octopus,!nautilus,ceph_preview
package rbd
import (
"testing"
"github.com/ceph/go-ceph/rados"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func createAndWriteDataToImage(t *testing.T, ioctx *rados.IOContext) string {
opts := NewRbdImageOptions()
defer opts.Destroy()
assert.NoError(t,
opts.SetUint64(ImageOptionOrder, uint64(testImageOrder)))
name := GetUUID()
err := CreateImage(ioctx, name, 1<<25, opts)
require.NoError(t, err)
img, err := OpenImage(ioctx, name, NoSnapshot)
require.NoError(t, err)
img.CreateSnapshot("snap1")
_, err = img.WriteAt([]byte("sometimes you feel like a nut"), 0)
assert.NoError(t, err)
err = img.Close()
assert.NoError(t, err)
return name
}
func TestMigration(t *testing.T) {
conn := radosConnect(t)
pool := GetUUID()
err := conn.MakePool(pool)
require.NoError(t, err)
ioctx, err := conn.OpenIOContext(pool)
require.NoError(t, err)
name := createAndWriteDataToImage(t, ioctx)
destImage := GetUUID()
err = MigrationPrepare(ioctx, name, ioctx, destImage, NewRbdImageOptions())
require.NoError(t, err)
status, err := MigrationStatus(ioctx, destImage)
require.NoError(t, err)
assert.Equal(t, status.State, MigrationImagePrepared)
err = MigrationExecute(ioctx, destImage)
require.NoError(t, err)
status, err = MigrationStatus(ioctx, destImage)
require.NoError(t, err)
assert.Equal(t, status.State, MigrationImageExecuted)
err = MigrationCommit(ioctx, destImage)
require.NoError(t, err)
_, err = OpenImage(ioctx, destImage, NoSnapshot)
assert.NoError(t, err)
// original image is moved to trash as part of migration prepare
_, err = OpenImage(ioctx, name, NoSnapshot)
assert.Error(t, err)
ioctx.Destroy()
conn.DeletePool(pool)
conn.Shutdown()
}
func TestMigrationPrepareImport(t *testing.T) {
conn := radosConnect(t)
pool := GetUUID()
err := conn.MakePool(pool)
require.NoError(t, err)
ioctx, err := conn.OpenIOContext(pool)
require.NoError(t, err)
name := createAndWriteDataToImage(t, ioctx)
destImage := GetUUID()
sourceSpec := `{"type": "native", "snap_name": "snap1", "pool_name":"` + pool + `", "image_name": "` + name + `"}}`
err = MigrationPrepareImport(sourceSpec, ioctx, destImage, NewRbdImageOptions())
require.NoError(t, err)
status, err := MigrationStatus(ioctx, destImage)
require.NoError(t, err)
assert.Equal(t, status.State, MigrationImagePrepared)
img, err := OpenImage(ioctx, destImage, NoSnapshot)
assert.NoError(t, err)
err = img.Remove()
assert.Error(t, err)
ioctx.Destroy()
conn.DeletePool(pool)
conn.Shutdown()
}
func TestMigrationAbort(t *testing.T) {
conn := radosConnect(t)
pool := GetUUID()
err := conn.MakePool(pool)
require.NoError(t, err)
ioctx, err := conn.OpenIOContext(pool)
require.NoError(t, err)
name := createAndWriteDataToImage(t, ioctx)
destImage := GetUUID()
err = MigrationPrepare(ioctx, name, ioctx, destImage, NewRbdImageOptions())
require.NoError(t, err)
status, err := MigrationStatus(ioctx, destImage)
require.NoError(t, err)
assert.Equal(t, status.State, MigrationImagePrepared)
err = MigrationAbort(ioctx, destImage)
require.NoError(t, err)
_, err = OpenImage(ioctx, destImage, NoSnapshot)
assert.Error(t, err)
// original image is retrievable
_, err = OpenImage(ioctx, name, NoSnapshot)
assert.NoError(t, err)
ioctx.Destroy()
conn.DeletePool(pool)
conn.Shutdown()
}