]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
librbd: added new 'migration_prepare_import' API methods
authorJason Dillaman <dillaman@redhat.com>
Wed, 21 Oct 2020 18:27:38 +0000 (14:27 -0400)
committerJason Dillaman <dillaman@redhat.com>
Sun, 1 Nov 2020 14:22:38 +0000 (09:22 -0500)
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>
src/include/rbd/librbd.h
src/include/rbd/librbd.hpp
src/librbd/api/Migration.cc
src/librbd/api/Migration.h
src/librbd/librbd.cc
src/pybind/rbd/rbd.pyx
src/test/pybind/test_rbd.py

index 5d2ee025a1b58e5b7104979938b9e3513699babd..bde2a43274dad83bae14ada7e6670a75917753e5 100644 (file)
@@ -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,
index da3e3b855d0ecdb3bb5be1f72aa38169232f28ac..63c1f759cd59ecb10885debaa408b8981437a0d6 100644 (file)
@@ -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);
index e27a7ebd8b3dbc3b1c29eef36d1bb761931418c7..d700bb36f4e898612f99636fa9bdc2060b9f7dfd 100644 (file)
@@ -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<I>::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<I>::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<I>::prepare(librados::IoCtx& io_ctx,
   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>
 int Migration<I>::execute(librados::IoCtx& io_ctx,
                           const std::string &image_name,
@@ -552,10 +639,15 @@ int Migration<I>::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<I>::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<I>::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<I>::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<I>::prepare() {
   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>
 int Migration<I>::execute() {
   ldout(m_cct, 10) << dendl;
@@ -847,21 +974,22 @@ int Migration<I>::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<I>::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<librbd::snap_info_t> snaps;
     r = Snapshot<I>::list(m_dst_image_ctx, snaps);
     if (r < 0) {
@@ -966,24 +1096,26 @@ int Migration<I>::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<I>::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<I>::status(image_migration_status_t *status) {
   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>
 int Migration<I>::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<I>::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<I>::create_dst_image(I** image_ctx) {
     return r;
   }
 
-  C_SaferCond on_metadata_copy;
-  auto metadata_copy_req = librbd::deep_copy::MetadataCopyRequest<I>::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<I>::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<I>::create_dst_image(I** image_ctx) {
     return r;
   }
 
-  r = update_group(m_src_image_ctx, dst_image_ctx);
-  if (r < 0) {
-    return r;
-  }
+  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(cls::rbd::MIGRATION_STATE_PREPARED, "");
-  if (r < 0) {
-    return r;
+    r = set_state(m_src_image_ctx, "source",
+                  cls::rbd::MIGRATION_STATE_PREPARED, "");
+    if (r < 0) {
+      return r;
+    }
   }
 
-  r = dst_image_ctx->state->refresh();
+  r = set_state(dst_image_ctx, "destination",
+                cls::rbd::MIGRATION_STATE_PREPARED, "");
   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;
+  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 = relink_children(m_src_image_ctx, dst_image_ctx);
+    if (r < 0) {
+      return r;
+    }
   }
 
   return 0;
index 7ef1cf241c7e9d6099d3d7254b9c0ad0ee91bb93..dd70dcc238e3c7dee609f874ded0640f6b97e473 100644 (file)
@@ -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,
index d1766cd4da9f876fadf8ecaa47694c7af539b716..2068048dc30b1ab4a5445f4e84c94e5dbdb7f969 100644 (file)
@@ -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<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;
 }
 
+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;
index f64bb30f36d1003e5651a05583dbd5b7a224fda7..dd3188323e0ab7864fe5b8b4c58cdd071db9d56a 100644 (file)
@@ -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.
index bcc6663602367234df53e995543adedfa8552834..e0b21a0a9ede5620486bb2c14c28a5d83fbc72a0 100644 (file)
@@ -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):