librbd: added new 'migration_prepare_import' API methods

These related methods accept a JSON-encoded source-spec that
describes how to read from the source image. The migration is
a one-way operation, so children/groups/etc won't be updated
to point to the new destination image.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
This commit is contained in:
Jason Dillaman 2020-10-21 14:27:38 -04:00
parent e5398d1429
commit d6133f9bf2
7 changed files with 382 additions and 106 deletions

View File

@ -476,6 +476,9 @@ CEPH_RBD_API int rbd_migration_prepare(rados_ioctx_t ioctx,
rados_ioctx_t dest_ioctx, rados_ioctx_t dest_ioctx,
const char *dest_image_name, const char *dest_image_name,
rbd_image_options_t opts); rbd_image_options_t opts);
CEPH_RBD_API int rbd_migration_prepare_import(
const char *source_spec, rados_ioctx_t dest_ioctx,
const char *dest_image_name, rbd_image_options_t opts);
CEPH_RBD_API int rbd_migration_execute(rados_ioctx_t ioctx, CEPH_RBD_API int rbd_migration_execute(rados_ioctx_t ioctx,
const char *image_name); const char *image_name);
CEPH_RBD_API int rbd_migration_execute_with_progress(rados_ioctx_t ioctx, CEPH_RBD_API int rbd_migration_execute_with_progress(rados_ioctx_t ioctx,

View File

@ -294,6 +294,8 @@ public:
int migration_prepare(IoCtx& io_ctx, const char *image_name, int migration_prepare(IoCtx& io_ctx, const char *image_name,
IoCtx& dest_io_ctx, const char *dest_image_name, IoCtx& dest_io_ctx, const char *dest_image_name,
ImageOptions& opts); ImageOptions& opts);
int migration_prepare_import(const char *source_spec, IoCtx& dest_io_ctx,
const char *dest_image_name, ImageOptions& opts);
int migration_execute(IoCtx& io_ctx, const char *image_name); int migration_execute(IoCtx& io_ctx, const char *image_name);
int migration_execute_with_progress(IoCtx& io_ctx, const char *image_name, int migration_execute_with_progress(IoCtx& io_ctx, const char *image_name,
ProgressContext &prog_ctx); ProgressContext &prog_ctx);

View File

@ -34,7 +34,9 @@
#include "librbd/image/RemoveRequest.h" #include "librbd/image/RemoveRequest.h"
#include "librbd/image/Types.h" #include "librbd/image/Types.h"
#include "librbd/internal.h" #include "librbd/internal.h"
#include "librbd/migration/FormatInterface.h"
#include "librbd/migration/NativeFormat.h" #include "librbd/migration/NativeFormat.h"
#include "librbd/migration/SourceSpecBuilder.h"
#include "librbd/mirror/DisableRequest.h" #include "librbd/mirror/DisableRequest.h"
#include "librbd/mirror/EnableRequest.h" #include "librbd/mirror/EnableRequest.h"
@ -464,8 +466,7 @@ int Migration<I>::prepare(librados::IoCtx& io_ctx,
lderr(cct) << "librbd does not support requested features" << dendl; lderr(cct) << "librbd does not support requested features" << dendl;
return -ENOSYS; return -ENOSYS;
} }
features &= ~RBD_FEATURES_INTERNAL; features &= ~RBD_FEATURES_IMPLICIT_ENABLE;
features &= ~RBD_FEATURE_DIRTY_CACHE;
features |= RBD_FEATURE_MIGRATING; features |= RBD_FEATURE_MIGRATING;
opts.set(RBD_IMAGE_OPTION_FEATURES, features); opts.set(RBD_IMAGE_OPTION_FEATURES, features);
@ -497,11 +498,15 @@ int Migration<I>::prepare(librados::IoCtx& io_ctx,
auto dst_image_ctx = I::create( auto dst_image_ctx = I::create(
dest_image_name, util::generate_image_id(dest_io_ctx), nullptr, dest_image_name, util::generate_image_id(dest_io_ctx), nullptr,
dest_io_ctx, false); dest_io_ctx, false);
src_image_ctx->image_lock.lock_shared();
cls::rbd::MigrationSpec dst_migration_spec{ cls::rbd::MigrationSpec dst_migration_spec{
cls::rbd::MIGRATION_HEADER_TYPE_DST, dest_io_ctx.get_id(), cls::rbd::MIGRATION_HEADER_TYPE_DST,
dest_io_ctx.get_namespace(), "", "", "", {}, 0, src_image_ctx->md_ctx.get_id(), src_image_ctx->md_ctx.get_namespace(),
false, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, flatten > 0, src_image_ctx->name, src_image_ctx->id, "", {}, 0, false,
cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, flatten > 0,
cls::rbd::MIGRATION_STATE_PREPARING, ""}; cls::rbd::MIGRATION_STATE_PREPARING, ""};
src_image_ctx->image_lock.unlock_shared();
Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec,
opts, nullptr); opts, nullptr);
r = migration.prepare(); r = migration.prepare();
@ -512,6 +517,88 @@ int Migration<I>::prepare(librados::IoCtx& io_ctx,
return r; return r;
} }
template <typename I>
int Migration<I>::prepare_import(
const std::string& source_spec, librados::IoCtx& dest_io_ctx,
const std::string &dest_image_name, ImageOptions& opts) {
if (source_spec.empty() || !dest_io_ctx.is_valid() ||
dest_image_name.empty()) {
return -EINVAL;
}
auto cct = reinterpret_cast<CephContext *>(dest_io_ctx.cct());
ldout(cct, 10) << source_spec << " -> "
<< dest_io_ctx.get_pool_name() << "/"
<< dest_image_name << ", opts=" << opts << dendl;
auto src_image_ctx = I::create("", "", nullptr, dest_io_ctx, true);
BOOST_SCOPE_EXIT_TPL(src_image_ctx) {
src_image_ctx->state->close();
} BOOST_SCOPE_EXIT_END;
migration::SourceSpecBuilder<I> source_spec_builder(src_image_ctx);
json_spirit::mObject source_spec_object;
int r = source_spec_builder.parse_source_spec(source_spec,
&source_spec_object);
if (r < 0) {
lderr(cct) << "failed to parse source spec: " << cpp_strerror(r)
<< dendl;
return r;
}
std::unique_ptr<migration::FormatInterface> format;
r = source_spec_builder.build_format(source_spec_object, &format);
if (r < 0) {
lderr(cct) << "failed to build migration format handler: "
<< cpp_strerror(r) << dendl;
return r;
}
C_SaferCond open_ctx;
format->open(&open_ctx);
r = open_ctx.wait();
if (r < 0) {
lderr(cct) << "failed to open migration source: " << cpp_strerror(r)
<< dendl;
return r;
}
uint64_t image_format = 2;
if (opts.get(RBD_IMAGE_OPTION_FORMAT, &image_format) != 0) {
opts.set(RBD_IMAGE_OPTION_FORMAT, image_format);
}
if (image_format != 2) {
lderr(cct) << "unsupported destination image format: " << image_format
<< dendl;
return -EINVAL;
}
uint64_t features_set = 0;
opts.get(RBD_IMAGE_OPTION_FEATURES_SET, &features_set);
opts.set(RBD_IMAGE_OPTION_FEATURES_SET, features_set | RBD_FEATURE_MIGRATING);
ldout(cct, 20) << "updated opts=" << opts << dendl;
auto dst_image_ctx = I::create(
dest_image_name, util::generate_image_id(dest_io_ctx), nullptr,
dest_io_ctx, false);
cls::rbd::MigrationSpec dst_migration_spec{
cls::rbd::MIGRATION_HEADER_TYPE_DST, -1, "", "", "",
json_spirit::write(source_spec_object), {},
0, false, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, true,
cls::rbd::MIGRATION_STATE_PREPARING, ""};
Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec,
opts, nullptr);
return migration.prepare_import();
if (r < 0) {
return r;
}
return 0;
}
template <typename I> template <typename I>
int Migration<I>::execute(librados::IoCtx& io_ctx, int Migration<I>::execute(librados::IoCtx& io_ctx,
const std::string &image_name, const std::string &image_name,
@ -552,10 +639,15 @@ int Migration<I>::execute(librados::IoCtx& io_ctx,
return -EINVAL; return -EINVAL;
} }
ldout(cct, 5) << "migrating " << src_image_ctx->md_ctx.get_pool_name() << "/" ldout(cct, 5) << "migrating ";
<< src_image_ctx->name << " -> " if (!dst_migration_spec.source_spec.empty()) {
<< dst_image_ctx->md_ctx.get_pool_name() *_dout << dst_migration_spec.source_spec;
<< "/" << dst_image_ctx->name << dendl; } else {
*_dout << src_image_ctx->md_ctx.get_pool_name() << "/"
<< src_image_ctx->name;
}
*_dout << " -> " << dst_image_ctx->md_ctx.get_pool_name() << "/"
<< dst_image_ctx->name << dendl;
ImageOptions opts; ImageOptions opts;
Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec,
@ -585,18 +677,24 @@ int Migration<I>::abort(librados::IoCtx& io_ctx, const std::string &image_name,
return r; return r;
} }
ldout(cct, 5) << "canceling incomplete migration " ldout(cct, 5) << "canceling incomplete migration ";
<< src_image_ctx->md_ctx.get_pool_name() << "/" if (!dst_migration_spec.source_spec.empty()) {
<< src_image_ctx->name *_dout << dst_migration_spec.source_spec;
<< " -> " << dst_image_ctx->md_ctx.get_pool_name() << "/" } else {
<< dst_image_ctx->name << dendl; *_dout << src_image_ctx->md_ctx.get_pool_name() << "/"
<< src_image_ctx->name;
}
*_dout << " -> " << dst_image_ctx->md_ctx.get_pool_name() << "/"
<< dst_image_ctx->name << dendl;
ImageOptions opts; ImageOptions opts;
Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec,
opts, &prog_ctx); opts, &prog_ctx);
r = migration.abort(); r = migration.abort();
src_image_ctx->state->close(); if (src_image_ctx != nullptr) {
src_image_ctx->state->close();
}
if (r < 0) { if (r < 0) {
return r; return r;
@ -642,10 +740,15 @@ int Migration<I>::commit(librados::IoCtx& io_ctx,
return r; return r;
} }
ldout(cct, 5) << "migrating " << src_image_ctx->md_ctx.get_pool_name() << "/" ldout(cct, 5) << "migrating ";
<< src_image_ctx->name << " -> " if (!dst_migration_spec.source_spec.empty()) {
<< dst_image_ctx->md_ctx.get_pool_name() *_dout << dst_migration_spec.source_spec;
<< "/" << dst_image_ctx->name << dendl; } else {
*_dout << src_image_ctx->md_ctx.get_pool_name() << "/"
<< src_image_ctx->name;
}
*_dout << " -> " << dst_image_ctx->md_ctx.get_pool_name() << "/"
<< dst_image_ctx->name << dendl;
ImageOptions opts; ImageOptions opts;
Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec,
@ -678,10 +781,15 @@ int Migration<I>::status(librados::IoCtx& io_ctx,
return r; return r;
} }
ldout(cct, 5) << "migrating " << src_image_ctx->md_ctx.get_pool_name() << "/" ldout(cct, 5) << "migrating ";
<< src_image_ctx->name << " -> " if (!dst_migration_spec.source_spec.empty()) {
<< dst_image_ctx->md_ctx.get_pool_name() *_dout << dst_migration_spec.source_spec;
<< "/" << dst_image_ctx->name << dendl; } else {
*_dout << src_image_ctx->md_ctx.get_pool_name() << "/"
<< src_image_ctx->name;
}
*_dout << " -> " << dst_image_ctx->md_ctx.get_pool_name() << "/"
<< dst_image_ctx->name << dendl;
ImageOptions opts; ImageOptions opts;
Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec,
@ -791,6 +899,25 @@ int Migration<I>::prepare() {
return 0; return 0;
} }
template <typename I>
int Migration<I>::prepare_import() {
ldout(m_cct, 10) << dendl;
BOOST_SCOPE_EXIT_TPL(&m_dst_image_ctx) {
if (m_dst_image_ctx != nullptr) {
m_dst_image_ctx->state->close();
}
} BOOST_SCOPE_EXIT_END;
int r = create_dst_image(&m_dst_image_ctx);
if (r < 0) {
abort();
return r;
}
return 0;
}
template <typename I> template <typename I>
int Migration<I>::execute() { int Migration<I>::execute() {
ldout(m_cct, 10) << dendl; ldout(m_cct, 10) << dendl;
@ -847,21 +974,22 @@ int Migration<I>::abort() {
ldout(m_cct, 10) << dendl; ldout(m_cct, 10) << dendl;
int r; int r;
if (m_src_image_ctx != nullptr) {
m_src_image_ctx->owner_lock.lock_shared(); m_src_image_ctx->owner_lock.lock_shared();
if (m_src_image_ctx->exclusive_lock != nullptr && if (m_src_image_ctx->exclusive_lock != nullptr &&
!m_src_image_ctx->exclusive_lock->is_lock_owner()) { !m_src_image_ctx->exclusive_lock->is_lock_owner()) {
C_SaferCond ctx; C_SaferCond ctx;
m_src_image_ctx->exclusive_lock->acquire_lock(&ctx); m_src_image_ctx->exclusive_lock->acquire_lock(&ctx);
m_src_image_ctx->owner_lock.unlock_shared(); m_src_image_ctx->owner_lock.unlock_shared();
r = ctx.wait(); r = ctx.wait();
if (r < 0) { if (r < 0) {
lderr(m_cct) << "error acquiring exclusive lock: " << cpp_strerror(r) lderr(m_cct) << "error acquiring exclusive lock: " << cpp_strerror(r)
<< dendl; << dendl;
return r; return r;
}
} else {
m_src_image_ctx->owner_lock.unlock_shared();
} }
} else {
m_src_image_ctx->owner_lock.unlock_shared();
} }
group_info_t group_info; group_info_t group_info;
@ -902,19 +1030,21 @@ int Migration<I>::abort() {
return r; return r;
} }
// copy dst HEAD -> src HEAD SteppedProgressContext progress_ctx(
SteppedProgressContext progress_ctx(m_prog_ctx, 2); m_prog_ctx, (m_src_image_ctx != nullptr ? 2 : 1));
revert_data(m_dst_image_ctx, m_src_image_ctx, &progress_ctx); if (m_src_image_ctx != nullptr) {
progress_ctx.next_step(); // copy dst HEAD -> src HEAD
revert_data(m_dst_image_ctx, m_src_image_ctx, &progress_ctx);
progress_ctx.next_step();
ldout(m_cct, 10) << "relinking children" << dendl; ldout(m_cct, 10) << "relinking children" << dendl;
r = relink_children(m_dst_image_ctx, m_src_image_ctx); r = relink_children(m_dst_image_ctx, m_src_image_ctx);
if (r < 0) { if (r < 0) {
return r; return r;
}
} }
ldout(m_cct, 10) << "removing dst image snapshots" << dendl; ldout(m_cct, 10) << "removing dst image snapshots" << dendl;
std::vector<librbd::snap_info_t> snaps; std::vector<librbd::snap_info_t> snaps;
r = Snapshot<I>::list(m_dst_image_ctx, snaps); r = Snapshot<I>::list(m_dst_image_ctx, snaps);
if (r < 0) { if (r < 0) {
@ -966,24 +1096,26 @@ int Migration<I>::abort() {
} }
} }
r = relink_src_image(m_src_image_ctx); if (m_src_image_ctx != nullptr) {
if (r < 0) { r = relink_src_image(m_src_image_ctx);
return r; if (r < 0) {
} return r;
}
r = add_group(m_src_image_ctx, group_info); r = add_group(m_src_image_ctx, group_info);
if (r < 0) { if (r < 0) {
return r; return r;
} }
r = remove_migration(m_src_image_ctx); r = remove_migration(m_src_image_ctx);
if (r < 0) { if (r < 0) {
return r; return r;
} }
r = enable_mirroring(m_src_image_ctx, m_mirroring, m_mirror_image_mode); r = enable_mirroring(m_src_image_ctx, m_mirroring, m_mirror_image_mode);
if (r < 0) { if (r < 0) {
return r; return r;
}
} }
ldout(m_cct, 10) << "succeeded" << dendl; ldout(m_cct, 10) << "succeeded" << dendl;
@ -1007,9 +1139,11 @@ int Migration<I>::commit() {
return r; return r;
} }
r = remove_src_image(&m_src_image_ctx); if (m_src_image_ctx != nullptr) {
if (r < 0) { r = remove_src_image(&m_src_image_ctx);
return r; if (r < 0) {
return r;
}
} }
r = enable_mirroring(m_dst_image_ctx, m_mirroring, m_mirror_image_mode); r = enable_mirroring(m_dst_image_ctx, m_mirroring, m_mirror_image_mode);
@ -1061,26 +1195,34 @@ int Migration<I>::status(image_migration_status_t *status) {
return 0; return 0;
} }
template <typename I>
int Migration<I>::set_state(I* image_ctx, const std::string& image_description,
cls::rbd::MigrationState state,
const std::string &description) {
int r = cls_client::migration_set_state(&image_ctx->md_ctx,
image_ctx->header_oid,
state, description);
if (r < 0) {
lderr(m_cct) << "failed to set " << image_description << " "
<< "migration header: " << cpp_strerror(r) << dendl;
return r;
}
return 0;
}
template <typename I> template <typename I>
int Migration<I>::set_state(cls::rbd::MigrationState state, int Migration<I>::set_state(cls::rbd::MigrationState state,
const std::string &description) { const std::string &description) {
int r; int r;
if (m_src_image_ctx != nullptr) { if (m_src_image_ctx != nullptr) {
r = cls_client::migration_set_state(&m_src_image_ctx->md_ctx, r = set_state(m_src_image_ctx, "source", state, description);
m_src_image_ctx->header_oid,
state, description);
if (r < 0) { if (r < 0) {
lderr(m_cct) << "failed to set source migration header: "
<< cpp_strerror(r) << dendl;
return r; return r;
} }
} }
r = cls_client::migration_set_state(&m_dst_io_ctx, m_dst_header_oid, state, r = set_state(m_dst_image_ctx, "destination", state, description);
description);
if (r < 0) { if (r < 0) {
lderr(m_cct) << "failed to set destination migration header: "
<< cpp_strerror(r) << dendl;
return r; return r;
} }
@ -1348,7 +1490,7 @@ int Migration<I>::create_dst_image(I** image_ctx) {
m_src_image_ctx->op_work_queue, &on_create); m_src_image_ctx->op_work_queue, &on_create);
req->send(); req->send();
} else { } else {
r = util::create_ioctx(m_src_image_ctx->md_ctx, "destination image", r = util::create_ioctx(m_src_image_ctx->md_ctx, "parent image",
parent_spec.pool_id, parent_spec.pool_namespace, parent_spec.pool_id, parent_spec.pool_namespace,
&parent_io_ctx); &parent_io_ctx);
if (r < 0) { if (r < 0) {
@ -1410,25 +1552,23 @@ int Migration<I>::create_dst_image(I** image_ctx) {
return r; return r;
} }
C_SaferCond on_metadata_copy; if (!m_src_image_ctx->header_oid.empty()) {
auto metadata_copy_req = librbd::deep_copy::MetadataCopyRequest<I>::create( C_SaferCond on_metadata_copy;
m_src_image_ctx, dst_image_ctx, &on_metadata_copy); auto metadata_copy_req = librbd::deep_copy::MetadataCopyRequest<I>::create(
metadata_copy_req->send(); m_src_image_ctx, dst_image_ctx, &on_metadata_copy);
r = on_metadata_copy.wait(); metadata_copy_req->send();
if (r < 0) { r = on_metadata_copy.wait();
lderr(m_cct) << "failed to copy metadata: " << cpp_strerror(r) << dendl; if (r < 0) {
return r; lderr(m_cct) << "failed to copy metadata: " << cpp_strerror(r) << dendl;
return r;
}
} }
m_src_image_ctx->image_lock.lock_shared(); m_dst_migration_spec.snap_seqs = snap_seqs;
m_dst_migration_spec = {cls::rbd::MIGRATION_HEADER_TYPE_DST, m_dst_migration_spec.overlap = size;
m_src_image_ctx->md_ctx.get_id(), m_dst_migration_spec.mirroring = m_mirroring;
m_src_image_ctx->md_ctx.get_namespace(), m_dst_migration_spec.mirror_image_mode = m_mirror_image_mode;
m_src_image_ctx->name, m_src_image_ctx->id, m_dst_migration_spec.flatten = m_flatten;
"", snap_seqs, size, m_mirroring, m_mirror_image_mode,
m_flatten, cls::rbd::MIGRATION_STATE_PREPARING, ""};
m_src_image_ctx->image_lock.unlock_shared();
r = cls_client::migration_set(&m_dst_io_ctx, m_dst_header_oid, r = cls_client::migration_set(&m_dst_io_ctx, m_dst_header_oid,
m_dst_migration_spec); m_dst_migration_spec);
if (r < 0) { if (r < 0) {
@ -1437,26 +1577,37 @@ int Migration<I>::create_dst_image(I** image_ctx) {
return r; return r;
} }
r = update_group(m_src_image_ctx, dst_image_ctx); if (m_dst_migration_spec.source_spec.empty()) {
r = update_group(m_src_image_ctx, dst_image_ctx);
if (r < 0) {
return r;
}
r = set_state(m_src_image_ctx, "source",
cls::rbd::MIGRATION_STATE_PREPARED, "");
if (r < 0) {
return r;
}
}
r = set_state(dst_image_ctx, "destination",
cls::rbd::MIGRATION_STATE_PREPARED, "");
if (r < 0) { if (r < 0) {
return r; return r;
} }
r = set_state(cls::rbd::MIGRATION_STATE_PREPARED, ""); if (m_dst_migration_spec.source_spec.empty()) {
if (r < 0) { r = dst_image_ctx->state->refresh();
return r; if (r < 0) {
} lderr(m_cct) << "failed to refresh destination image: " << cpp_strerror(r)
<< dendl;
return r;
}
r = dst_image_ctx->state->refresh(); r = relink_children(m_src_image_ctx, dst_image_ctx);
if (r < 0) { if (r < 0) {
lderr(m_cct) << "failed to refresh destination image: " << cpp_strerror(r) return r;
<< dendl; }
return r;
}
r = relink_children(m_src_image_ctx, dst_image_ctx);
if (r < 0) {
return r;
} }
return 0; return 0;

View File

@ -23,6 +23,10 @@ public:
static int prepare(librados::IoCtx& io_ctx, const std::string &image_name, static int prepare(librados::IoCtx& io_ctx, const std::string &image_name,
librados::IoCtx& dest_io_ctx, librados::IoCtx& dest_io_ctx,
const std::string &dest_image_name, ImageOptions& opts); const std::string &dest_image_name, ImageOptions& opts);
static int prepare_import(const std::string& source_spec,
librados::IoCtx& dest_io_ctx,
const std::string &dest_image_name,
ImageOptions& opts);
static int execute(librados::IoCtx& io_ctx, const std::string &image_name, static int execute(librados::IoCtx& io_ctx, const std::string &image_name,
ProgressContext &prog_ctx); ProgressContext &prog_ctx);
static int abort(librados::IoCtx& io_ctx, const std::string &image_name, static int abort(librados::IoCtx& io_ctx, const std::string &image_name,
@ -56,11 +60,15 @@ private:
ImageOptions& opts, ProgressContext *prog_ctx); ImageOptions& opts, ProgressContext *prog_ctx);
int prepare(); int prepare();
int prepare_import();
int execute(); int execute();
int abort(); int abort();
int commit(); int commit();
int status(image_migration_status_t *status); int status(image_migration_status_t *status);
int set_state(ImageCtxT* image_ctx, const std::string& image_description,
cls::rbd::MigrationState state,
const std::string &description);
int set_state(cls::rbd::MigrationState state, const std::string &description); int set_state(cls::rbd::MigrationState state, const std::string &description);
int list_src_snaps(ImageCtxT* image_ctx, int list_src_snaps(ImageCtxT* image_ctx,

View File

@ -923,6 +923,13 @@ namespace librbd {
return r; return r;
} }
int RBD::migration_prepare_import(const char *source_spec, IoCtx& dest_io_ctx,
const char *dest_image_name,
ImageOptions& opts) {
return librbd::api::Migration<>::prepare_import(source_spec, dest_io_ctx,
dest_image_name, opts);
}
int RBD::migration_execute(IoCtx& io_ctx, const char *image_name) int RBD::migration_execute(IoCtx& io_ctx, const char *image_name)
{ {
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx)); TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
@ -4370,6 +4377,16 @@ extern "C" int rbd_migration_prepare(rados_ioctx_t p, const char *image_name,
return r; return r;
} }
extern "C" int rbd_migration_prepare_import(
const char *source_spec, rados_ioctx_t dest_p,
const char *dest_image_name, rbd_image_options_t opts_) {
librados::IoCtx dest_io_ctx;
librados::IoCtx::from_rados_ioctx_t(dest_p, dest_io_ctx);
librbd::ImageOptions opts(opts_);
return librbd::api::Migration<>::prepare_import(source_spec, dest_io_ctx,
dest_image_name, opts);
}
extern "C" int rbd_migration_execute(rados_ioctx_t p, const char *image_name) extern "C" int rbd_migration_execute(rados_ioctx_t p, const char *image_name)
{ {
librados::IoCtx io_ctx; librados::IoCtx io_ctx;

View File

@ -370,6 +370,10 @@ cdef extern from "rbd/librbd.h" nogil:
rados_ioctx_t dest_io_ctx, rados_ioctx_t dest_io_ctx,
const char *dest_image_name, const char *dest_image_name,
rbd_image_options_t opts) rbd_image_options_t opts)
int rbd_migration_prepare_import(const char *source_spec,
rados_ioctx_t dest_io_ctx,
const char *dest_image_name,
rbd_image_options_t opts)
int rbd_migration_execute_with_progress(rados_ioctx_t io_ctx, int rbd_migration_execute_with_progress(rados_ioctx_t io_ctx,
const char *image_name, const char *image_name,
librbd_progress_fn_t cb, librbd_progress_fn_t cb,
@ -1688,6 +1692,67 @@ class RBD(object):
if ret < 0: if ret < 0:
raise make_ex(ret, 'error migrating image %s' % (image_name)) raise make_ex(ret, 'error migrating image %s' % (image_name))
def migration_prepare_import(self, source_spec, dest_ioctx, dest_image_name,
features=None, order=None, stripe_unit=None,
stripe_count=None, data_pool=None):
"""
Prepare an RBD image migration.
:param source_spec: JSON-encoded source-spec
:type source_spec: str
:param dest_ioctx: determines which pool to migration into
:type dest_ioctx: :class:`rados.Ioctx`
:param dest_image_name: the name of the destination image (may be the same image)
:type dest_image_name: str
:param features: bitmask of features to enable; if set, must include layering
:type features: int
:param order: the image is split into (2**order) byte objects
:type order: int
:param stripe_unit: stripe unit in bytes (default None to let librbd decide)
:type stripe_unit: int
:param stripe_count: objects to stripe over before looping
:type stripe_count: int
:param data_pool: optional separate pool for data blocks
:type data_pool: str
:raises: :class:`TypeError`
:raises: :class:`InvalidArgument`
:raises: :class:`ImageExists`
:raises: :class:`FunctionNotSupported`
:raises: :class:`ArgumentOutOfRange`
"""
source_spec = cstr(source_spec, 'source_spec')
dest_image_name = cstr(dest_image_name, 'dest_image_name')
cdef:
char *_source_spec = source_spec
rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx)
char *_dest_image_name = dest_image_name
rbd_image_options_t opts
rbd_image_options_create(&opts)
try:
if features is not None:
rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
features)
if order is not None:
rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
order)
if stripe_unit is not None:
rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
stripe_unit)
if stripe_count is not None:
rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
stripe_count)
if data_pool is not None:
rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
data_pool)
with nogil:
ret = rbd_migration_prepare_import(_source_spec, _dest_ioctx,
_dest_image_name, opts)
finally:
rbd_image_options_destroy(opts)
if ret < 0:
raise make_ex(ret, 'error migrating image %s' % (source_spec))
def migration_execute(self, ioctx, image_name, on_progress=None): def migration_execute(self, ioctx, image_name, on_progress=None):
""" """
Execute a prepared RBD image migration. Execute a prepared RBD image migration.

View File

@ -2619,6 +2619,36 @@ class TestMigration(object):
RBD().migration_commit(ioctx, image_name) RBD().migration_commit(ioctx, image_name)
remove_image() remove_image()
def test_migration_import(self):
create_image()
with Image(ioctx, image_name) as image:
image_id = image.id()
source_spec = json.dumps(
{'type': 'native',
'pool_id': ioctx.get_pool_id(),
'pool_namespace': '',
'image_name': image_name,
'image_id': image_id})
dst_image_name = get_temp_image_name()
RBD().migration_prepare_import(source_spec, ioctx, dst_image_name,
features=63, order=23, stripe_unit=1<<23,
stripe_count=1, data_pool=None)
status = RBD().migration_status(ioctx, dst_image_name)
eq('', status['source_image_name'])
eq(dst_image_name, status['dest_image_name'])
eq(RBD_IMAGE_MIGRATION_STATE_PREPARED, status['state'])
with Image(ioctx, dst_image_name) as image:
source_spec = image.migration_source_spec()
eq("native", source_spec["type"])
RBD().migration_execute(ioctx, dst_image_name)
RBD().migration_commit(ioctx, dst_image_name)
RBD().remove(ioctx, dst_image_name)
RBD().remove(ioctx, image_name)
def test_migration_with_progress(self): def test_migration_with_progress(self):
d = {'received_callback': False} d = {'received_callback': False}
def progress_cb(current, total): def progress_cb(current, total):