mirror of
https://github.com/ceph/go-ceph
synced 2024-12-25 15:42:30 +00:00
rbd: implement live migration functions
Signed-off-by: Omid Yoosefi <omidyoosefi@ibm.com>
This commit is contained in:
parent
2d1d51b762
commit
a6a42e9c7b
@ -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": [
|
||||
|
@ -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
219
rbd/migration.go
Normal 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
151
rbd/migration_test.go
Normal 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()
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user