From d6133f9bf20ada2f8d31c94533ace08abda967b6 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Wed, 21 Oct 2020 14:27:38 -0400 Subject: [PATCH] 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 --- src/include/rbd/librbd.h | 3 + src/include/rbd/librbd.hpp | 2 + src/librbd/api/Migration.cc | 363 +++++++++++++++++++++++++----------- src/librbd/api/Migration.h | 8 + src/librbd/librbd.cc | 17 ++ src/pybind/rbd/rbd.pyx | 65 +++++++ src/test/pybind/test_rbd.py | 30 +++ 7 files changed, 382 insertions(+), 106 deletions(-) diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index 5d2ee025a1b..bde2a43274d 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -476,6 +476,9 @@ CEPH_RBD_API int rbd_migration_prepare(rados_ioctx_t ioctx, rados_ioctx_t dest_ioctx, const char *dest_image_name, 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, const char *image_name); CEPH_RBD_API int rbd_migration_execute_with_progress(rados_ioctx_t ioctx, diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index da3e3b855d0..63c1f759cd5 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -294,6 +294,8 @@ public: int migration_prepare(IoCtx& io_ctx, const char *image_name, IoCtx& dest_io_ctx, const char *dest_image_name, 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_with_progress(IoCtx& io_ctx, const char *image_name, ProgressContext &prog_ctx); diff --git a/src/librbd/api/Migration.cc b/src/librbd/api/Migration.cc index e27a7ebd8b3..d700bb36f4e 100644 --- a/src/librbd/api/Migration.cc +++ b/src/librbd/api/Migration.cc @@ -34,7 +34,9 @@ #include "librbd/image/RemoveRequest.h" #include "librbd/image/Types.h" #include "librbd/internal.h" +#include "librbd/migration/FormatInterface.h" #include "librbd/migration/NativeFormat.h" +#include "librbd/migration/SourceSpecBuilder.h" #include "librbd/mirror/DisableRequest.h" #include "librbd/mirror/EnableRequest.h" @@ -464,8 +466,7 @@ int Migration::prepare(librados::IoCtx& io_ctx, lderr(cct) << "librbd does not support requested features" << dendl; return -ENOSYS; } - features &= ~RBD_FEATURES_INTERNAL; - features &= ~RBD_FEATURE_DIRTY_CACHE; + features &= ~RBD_FEATURES_IMPLICIT_ENABLE; features |= RBD_FEATURE_MIGRATING; opts.set(RBD_IMAGE_OPTION_FEATURES, features); @@ -497,11 +498,15 @@ int Migration::prepare(librados::IoCtx& io_ctx, auto dst_image_ctx = I::create( dest_image_name, util::generate_image_id(dest_io_ctx), nullptr, dest_io_ctx, false); + src_image_ctx->image_lock.lock_shared(); cls::rbd::MigrationSpec dst_migration_spec{ - cls::rbd::MIGRATION_HEADER_TYPE_DST, dest_io_ctx.get_id(), - dest_io_ctx.get_namespace(), "", "", "", {}, 0, - false, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, flatten > 0, + cls::rbd::MIGRATION_HEADER_TYPE_DST, + src_image_ctx->md_ctx.get_id(), src_image_ctx->md_ctx.get_namespace(), + src_image_ctx->name, src_image_ctx->id, "", {}, 0, false, + cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, flatten > 0, cls::rbd::MIGRATION_STATE_PREPARING, ""}; + src_image_ctx->image_lock.unlock_shared(); + Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, opts, nullptr); r = migration.prepare(); @@ -512,6 +517,88 @@ int Migration::prepare(librados::IoCtx& io_ctx, return r; } +template +int Migration::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(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 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 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 int Migration::execute(librados::IoCtx& io_ctx, const std::string &image_name, @@ -552,10 +639,15 @@ int Migration::execute(librados::IoCtx& io_ctx, return -EINVAL; } - ldout(cct, 5) << "migrating " << src_image_ctx->md_ctx.get_pool_name() << "/" - << src_image_ctx->name << " -> " - << dst_image_ctx->md_ctx.get_pool_name() - << "/" << dst_image_ctx->name << dendl; + ldout(cct, 5) << "migrating "; + if (!dst_migration_spec.source_spec.empty()) { + *_dout << dst_migration_spec.source_spec; + } 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; Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, @@ -585,18 +677,24 @@ int Migration::abort(librados::IoCtx& io_ctx, const std::string &image_name, return r; } - ldout(cct, 5) << "canceling incomplete migration " - << src_image_ctx->md_ctx.get_pool_name() << "/" - << src_image_ctx->name - << " -> " << dst_image_ctx->md_ctx.get_pool_name() << "/" - << dst_image_ctx->name << dendl; + ldout(cct, 5) << "canceling incomplete migration "; + if (!dst_migration_spec.source_spec.empty()) { + *_dout << dst_migration_spec.source_spec; + } 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; Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, opts, &prog_ctx); r = migration.abort(); - src_image_ctx->state->close(); + if (src_image_ctx != nullptr) { + src_image_ctx->state->close(); + } if (r < 0) { return r; @@ -642,10 +740,15 @@ int Migration::commit(librados::IoCtx& io_ctx, return r; } - ldout(cct, 5) << "migrating " << src_image_ctx->md_ctx.get_pool_name() << "/" - << src_image_ctx->name << " -> " - << dst_image_ctx->md_ctx.get_pool_name() - << "/" << dst_image_ctx->name << dendl; + ldout(cct, 5) << "migrating "; + if (!dst_migration_spec.source_spec.empty()) { + *_dout << dst_migration_spec.source_spec; + } 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; Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, @@ -678,10 +781,15 @@ int Migration::status(librados::IoCtx& io_ctx, return r; } - ldout(cct, 5) << "migrating " << src_image_ctx->md_ctx.get_pool_name() << "/" - << src_image_ctx->name << " -> " - << dst_image_ctx->md_ctx.get_pool_name() - << "/" << dst_image_ctx->name << dendl; + ldout(cct, 5) << "migrating "; + if (!dst_migration_spec.source_spec.empty()) { + *_dout << dst_migration_spec.source_spec; + } 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; Migration migration(src_image_ctx, dst_image_ctx, dst_migration_spec, @@ -791,6 +899,25 @@ int Migration::prepare() { return 0; } +template +int Migration::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 int Migration::execute() { ldout(m_cct, 10) << dendl; @@ -847,21 +974,22 @@ int Migration::abort() { ldout(m_cct, 10) << dendl; int r; - - m_src_image_ctx->owner_lock.lock_shared(); - if (m_src_image_ctx->exclusive_lock != nullptr && - !m_src_image_ctx->exclusive_lock->is_lock_owner()) { - C_SaferCond ctx; - m_src_image_ctx->exclusive_lock->acquire_lock(&ctx); - m_src_image_ctx->owner_lock.unlock_shared(); - r = ctx.wait(); - if (r < 0) { - lderr(m_cct) << "error acquiring exclusive lock: " << cpp_strerror(r) - << dendl; - return r; + if (m_src_image_ctx != nullptr) { + m_src_image_ctx->owner_lock.lock_shared(); + if (m_src_image_ctx->exclusive_lock != nullptr && + !m_src_image_ctx->exclusive_lock->is_lock_owner()) { + C_SaferCond ctx; + m_src_image_ctx->exclusive_lock->acquire_lock(&ctx); + m_src_image_ctx->owner_lock.unlock_shared(); + r = ctx.wait(); + if (r < 0) { + lderr(m_cct) << "error acquiring exclusive lock: " << cpp_strerror(r) + << dendl; + 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; @@ -902,19 +1030,21 @@ int Migration::abort() { return r; } - // copy dst HEAD -> src HEAD - SteppedProgressContext progress_ctx(m_prog_ctx, 2); - revert_data(m_dst_image_ctx, m_src_image_ctx, &progress_ctx); - progress_ctx.next_step(); + SteppedProgressContext progress_ctx( + m_prog_ctx, (m_src_image_ctx != nullptr ? 2 : 1)); + if (m_src_image_ctx != nullptr) { + // 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; - r = relink_children(m_dst_image_ctx, m_src_image_ctx); - if (r < 0) { - return r; + ldout(m_cct, 10) << "relinking children" << dendl; + r = relink_children(m_dst_image_ctx, m_src_image_ctx); + if (r < 0) { + return r; + } } ldout(m_cct, 10) << "removing dst image snapshots" << dendl; - std::vector snaps; r = Snapshot::list(m_dst_image_ctx, snaps); if (r < 0) { @@ -966,24 +1096,26 @@ int Migration::abort() { } } - r = relink_src_image(m_src_image_ctx); - if (r < 0) { - return r; - } + if (m_src_image_ctx != nullptr) { + r = relink_src_image(m_src_image_ctx); + if (r < 0) { + return r; + } - r = add_group(m_src_image_ctx, group_info); - if (r < 0) { - return r; - } + r = add_group(m_src_image_ctx, group_info); + if (r < 0) { + return r; + } - r = remove_migration(m_src_image_ctx); - if (r < 0) { - return r; - } + r = remove_migration(m_src_image_ctx); + if (r < 0) { + return r; + } - r = enable_mirroring(m_src_image_ctx, m_mirroring, m_mirror_image_mode); - if (r < 0) { - return r; + r = enable_mirroring(m_src_image_ctx, m_mirroring, m_mirror_image_mode); + if (r < 0) { + return r; + } } ldout(m_cct, 10) << "succeeded" << dendl; @@ -1007,9 +1139,11 @@ int Migration::commit() { return r; } - r = remove_src_image(&m_src_image_ctx); - if (r < 0) { - return r; + if (m_src_image_ctx != nullptr) { + r = remove_src_image(&m_src_image_ctx); + if (r < 0) { + return r; + } } r = enable_mirroring(m_dst_image_ctx, m_mirroring, m_mirror_image_mode); @@ -1061,26 +1195,34 @@ int Migration::status(image_migration_status_t *status) { return 0; } +template +int Migration::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 int Migration::set_state(cls::rbd::MigrationState state, const std::string &description) { int r; if (m_src_image_ctx != nullptr) { - r = cls_client::migration_set_state(&m_src_image_ctx->md_ctx, - m_src_image_ctx->header_oid, - state, description); + r = set_state(m_src_image_ctx, "source", state, description); if (r < 0) { - lderr(m_cct) << "failed to set source migration header: " - << cpp_strerror(r) << dendl; return r; } } - r = cls_client::migration_set_state(&m_dst_io_ctx, m_dst_header_oid, state, - description); + r = set_state(m_dst_image_ctx, "destination", state, description); if (r < 0) { - lderr(m_cct) << "failed to set destination migration header: " - << cpp_strerror(r) << dendl; return r; } @@ -1348,7 +1490,7 @@ int Migration::create_dst_image(I** image_ctx) { m_src_image_ctx->op_work_queue, &on_create); req->send(); } 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_io_ctx); if (r < 0) { @@ -1410,25 +1552,23 @@ int Migration::create_dst_image(I** image_ctx) { return r; } - C_SaferCond on_metadata_copy; - auto metadata_copy_req = librbd::deep_copy::MetadataCopyRequest::create( - m_src_image_ctx, dst_image_ctx, &on_metadata_copy); - metadata_copy_req->send(); - r = on_metadata_copy.wait(); - if (r < 0) { - lderr(m_cct) << "failed to copy metadata: " << cpp_strerror(r) << dendl; - return r; + if (!m_src_image_ctx->header_oid.empty()) { + C_SaferCond on_metadata_copy; + auto metadata_copy_req = librbd::deep_copy::MetadataCopyRequest::create( + m_src_image_ctx, dst_image_ctx, &on_metadata_copy); + metadata_copy_req->send(); + r = on_metadata_copy.wait(); + if (r < 0) { + 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 = {cls::rbd::MIGRATION_HEADER_TYPE_DST, - m_src_image_ctx->md_ctx.get_id(), - m_src_image_ctx->md_ctx.get_namespace(), - m_src_image_ctx->name, m_src_image_ctx->id, - "", snap_seqs, size, m_mirroring, m_mirror_image_mode, - m_flatten, cls::rbd::MIGRATION_STATE_PREPARING, ""}; - m_src_image_ctx->image_lock.unlock_shared(); - + m_dst_migration_spec.snap_seqs = snap_seqs; + m_dst_migration_spec.overlap = size; + m_dst_migration_spec.mirroring = m_mirroring; + m_dst_migration_spec.mirror_image_mode = m_mirror_image_mode; + m_dst_migration_spec.flatten = m_flatten; r = cls_client::migration_set(&m_dst_io_ctx, m_dst_header_oid, m_dst_migration_spec); if (r < 0) { @@ -1437,26 +1577,37 @@ int Migration::create_dst_image(I** image_ctx) { 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) { return r; } - r = set_state(cls::rbd::MIGRATION_STATE_PREPARED, ""); - if (r < 0) { - return r; - } + if (m_dst_migration_spec.source_spec.empty()) { + r = dst_image_ctx->state->refresh(); + if (r < 0) { + lderr(m_cct) << "failed to refresh destination image: " << cpp_strerror(r) + << dendl; + return r; + } - r = dst_image_ctx->state->refresh(); - if (r < 0) { - lderr(m_cct) << "failed to refresh destination image: " << cpp_strerror(r) - << dendl; - return r; - } - - r = relink_children(m_src_image_ctx, dst_image_ctx); - if (r < 0) { - return r; + r = relink_children(m_src_image_ctx, dst_image_ctx); + if (r < 0) { + return r; + } } return 0; diff --git a/src/librbd/api/Migration.h b/src/librbd/api/Migration.h index 7ef1cf241c7..dd70dcc238e 100644 --- a/src/librbd/api/Migration.h +++ b/src/librbd/api/Migration.h @@ -23,6 +23,10 @@ public: static int prepare(librados::IoCtx& io_ctx, const std::string &image_name, librados::IoCtx& dest_io_ctx, 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, ProgressContext &prog_ctx); static int abort(librados::IoCtx& io_ctx, const std::string &image_name, @@ -56,11 +60,15 @@ private: ImageOptions& opts, ProgressContext *prog_ctx); int prepare(); + int prepare_import(); int execute(); int abort(); int commit(); 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 list_src_snaps(ImageCtxT* image_ctx, diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index d1766cd4da9..2068048dc30 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -923,6 +923,13 @@ namespace librbd { 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) { TracepointProvider::initialize(get_cct(io_ctx)); @@ -4370,6 +4377,16 @@ extern "C" int rbd_migration_prepare(rados_ioctx_t p, const char *image_name, 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) { librados::IoCtx io_ctx; diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index f64bb30f36d..dd3188323e0 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -370,6 +370,10 @@ cdef extern from "rbd/librbd.h" nogil: rados_ioctx_t dest_io_ctx, const char *dest_image_name, 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, const char *image_name, librbd_progress_fn_t cb, @@ -1688,6 +1692,67 @@ class RBD(object): if ret < 0: 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): """ Execute a prepared RBD image migration. diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index bcc66636023..e0b21a0a9ed 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -2619,6 +2619,36 @@ class TestMigration(object): RBD().migration_commit(ioctx, image_name) 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): d = {'received_callback': False} def progress_cb(current, total):