summaryrefslogtreecommitdiffstats
path: root/src/librbd/image/CloneRequest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/librbd/image/CloneRequest.cc')
-rw-r--r--src/librbd/image/CloneRequest.cc647
1 files changed, 647 insertions, 0 deletions
diff --git a/src/librbd/image/CloneRequest.cc b/src/librbd/image/CloneRequest.cc
new file mode 100644
index 00000000..b7b07410
--- /dev/null
+++ b/src/librbd/image/CloneRequest.cc
@@ -0,0 +1,647 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls/rbd/cls_rbd_client.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "include/ceph_assert.h"
+#include "librbd/ImageState.h"
+#include "librbd/Utils.h"
+#include "librbd/image/AttachChildRequest.h"
+#include "librbd/image/AttachParentRequest.h"
+#include "librbd/image/CloneRequest.h"
+#include "librbd/image/CreateRequest.h"
+#include "librbd/image/RemoveRequest.h"
+#include "librbd/mirror/EnableRequest.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::image::CloneRequest: " << this << " " \
+ << __func__ << ": "
+
+#define MAX_KEYS 64
+
+namespace librbd {
+namespace image {
+
+using util::create_rados_callback;
+using util::create_context_callback;
+using util::create_async_context_callback;
+
+template <typename I>
+CloneRequest<I>::CloneRequest(ConfigProxy& config,
+ IoCtx& parent_io_ctx,
+ const std::string& parent_image_id,
+ const std::string& parent_snap_name,
+ uint64_t parent_snap_id,
+ IoCtx &c_ioctx,
+ const std::string &c_name,
+ const std::string &c_id,
+ ImageOptions c_options,
+ const std::string &non_primary_global_image_id,
+ const std::string &primary_mirror_uuid,
+ ContextWQ *op_work_queue, Context *on_finish)
+ : m_config(config), m_parent_io_ctx(parent_io_ctx),
+ m_parent_image_id(parent_image_id), m_parent_snap_name(parent_snap_name),
+ m_parent_snap_id(parent_snap_id), m_ioctx(c_ioctx), m_name(c_name),
+ m_id(c_id), m_opts(c_options),
+ m_non_primary_global_image_id(non_primary_global_image_id),
+ m_primary_mirror_uuid(primary_mirror_uuid),
+ m_op_work_queue(op_work_queue), m_on_finish(on_finish),
+ m_use_p_features(true) {
+
+ m_cct = reinterpret_cast<CephContext *>(m_ioctx.cct());
+
+ bool default_format_set;
+ m_opts.is_set(RBD_IMAGE_OPTION_FORMAT, &default_format_set);
+ if (!default_format_set) {
+ m_opts.set(RBD_IMAGE_OPTION_FORMAT, static_cast<uint64_t>(2));
+ }
+
+ ldout(m_cct, 20) << "parent_pool_id=" << parent_io_ctx.get_id() << ", "
+ << "parent_image_id=" << parent_image_id << ", "
+ << "parent_snap=" << parent_snap_name << "/"
+ << parent_snap_id << " clone to "
+ << "pool_id=" << m_ioctx.get_id() << ", "
+ << "name=" << m_name << ", "
+ << "opts=" << m_opts << dendl;
+}
+
+template <typename I>
+void CloneRequest<I>::send() {
+ ldout(m_cct, 20) << dendl;
+ validate_options();
+}
+
+template <typename I>
+void CloneRequest<I>::validate_options() {
+ ldout(m_cct, 20) << dendl;
+
+ uint64_t format = 0;
+ m_opts.get(RBD_IMAGE_OPTION_FORMAT, &format);
+ if (format < 2) {
+ lderr(m_cct) << "format 2 or later required for clone" << dendl;
+ complete(-EINVAL);
+ return;
+ }
+
+ if (m_opts.get(RBD_IMAGE_OPTION_FEATURES, &m_features) == 0) {
+ if (m_features & ~RBD_FEATURES_ALL) {
+ lderr(m_cct) << "librbd does not support requested features" << dendl;
+ complete(-ENOSYS);
+ return;
+ }
+ m_use_p_features = false;
+ }
+
+ if (m_opts.get(RBD_IMAGE_OPTION_CLONE_FORMAT, &m_clone_format) < 0) {
+ std::string default_clone_format = m_config.get_val<std::string>(
+ "rbd_default_clone_format");
+ if (default_clone_format == "1") {
+ m_clone_format = 1;
+ } else if (default_clone_format == "auto") {
+ librados::Rados rados(m_ioctx);
+ int8_t min_compat_client;
+ int8_t require_min_compat_client;
+ int r = rados.get_min_compatible_client(&min_compat_client,
+ &require_min_compat_client);
+ if (r < 0) {
+ complete(r);
+ return;
+ }
+ if (std::max(min_compat_client, require_min_compat_client) <
+ CEPH_RELEASE_MIMIC) {
+ m_clone_format = 1;
+ }
+ }
+ }
+
+ if (m_clone_format == 1 &&
+ m_parent_io_ctx.get_namespace() != m_ioctx.get_namespace()) {
+ ldout(m_cct, 1) << "clone v2 required for cross-namespace clones" << dendl;
+ complete(-EXDEV);
+ return;
+ }
+
+ open_parent();
+}
+
+template <typename I>
+void CloneRequest<I>::open_parent() {
+ ldout(m_cct, 20) << dendl;
+ ceph_assert(m_parent_snap_name.empty() ^ (m_parent_snap_id == CEPH_NOSNAP));
+
+ if (m_parent_snap_id != CEPH_NOSNAP) {
+ m_parent_image_ctx = I::create("", m_parent_image_id, m_parent_snap_id,
+ m_parent_io_ctx, true);
+ } else {
+ m_parent_image_ctx = I::create("", m_parent_image_id,
+ m_parent_snap_name.c_str(), m_parent_io_ctx,
+ true);
+ }
+
+ Context *ctx = create_context_callback<
+ CloneRequest<I>, &CloneRequest<I>::handle_open_parent>(this);
+ m_parent_image_ctx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT, ctx);
+}
+
+template <typename I>
+void CloneRequest<I>::handle_open_parent(int r) {
+ ldout(m_cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ m_parent_image_ctx->destroy();
+ m_parent_image_ctx = nullptr;
+
+ lderr(m_cct) << "failed to open parent image: " << cpp_strerror(r) << dendl;
+ complete(r);
+ return;
+ }
+
+ m_parent_snap_id = m_parent_image_ctx->snap_id;
+ m_pspec = {m_parent_io_ctx.get_id(), m_parent_io_ctx.get_namespace(),
+ m_parent_image_id, m_parent_snap_id};
+ validate_parent();
+}
+
+template <typename I>
+void CloneRequest<I>::validate_parent() {
+ ldout(m_cct, 20) << dendl;
+
+ if (m_parent_image_ctx->operations_disabled) {
+ lderr(m_cct) << "image operations disabled due to unsupported op features"
+ << dendl;
+ m_r_saved = -EROFS;
+ close_parent();
+ return;
+ }
+
+ if (m_parent_image_ctx->snap_id == CEPH_NOSNAP) {
+ lderr(m_cct) << "image to be cloned must be a snapshot" << dendl;
+ m_r_saved = -EINVAL;
+ close_parent();
+ return;
+ }
+
+ if (m_parent_image_ctx->old_format) {
+ lderr(m_cct) << "parent image must be in new format" << dendl;
+ m_r_saved = -EINVAL;
+ close_parent();
+ return;
+ }
+
+ m_parent_image_ctx->snap_lock.get_read();
+ uint64_t p_features = m_parent_image_ctx->features;
+ m_size = m_parent_image_ctx->get_image_size(m_parent_image_ctx->snap_id);
+
+ bool snap_protected;
+ int r = m_parent_image_ctx->is_snap_protected(m_parent_image_ctx->snap_id, &snap_protected);
+ m_parent_image_ctx->snap_lock.put_read();
+
+ if ((p_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
+ lderr(m_cct) << "parent image must support layering" << dendl;
+ m_r_saved = -ENOSYS;
+ close_parent();
+ return;
+ }
+ if (m_use_p_features) {
+ m_features = (p_features & ~RBD_FEATURES_IMPLICIT_ENABLE);
+ }
+
+ if (r < 0) {
+ lderr(m_cct) << "unable to locate parent's snapshot" << dendl;
+ m_r_saved = r;
+ close_parent();
+ return;
+ }
+
+ if (m_clone_format == 1 && !snap_protected) {
+ lderr(m_cct) << "parent snapshot must be protected" << dendl;
+ m_r_saved = -EINVAL;
+ close_parent();
+ return;
+ }
+
+ validate_child();
+}
+
+template <typename I>
+void CloneRequest<I>::validate_child() {
+ ldout(m_cct, 15) << dendl;
+
+ if ((m_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
+ lderr(m_cct) << "cloning image must support layering" << dendl;
+ m_r_saved = -ENOSYS;
+ close_parent();
+ return;
+ }
+
+ using klass = CloneRequest<I>;
+ librados::AioCompletion *comp = create_rados_callback<
+ klass, &klass::handle_validate_child>(this);
+
+ librados::ObjectReadOperation op;
+ op.stat(NULL, NULL, NULL);
+
+ int r = m_ioctx.aio_operate(util::old_header_name(m_name), comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_validate_child(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ if (r != -ENOENT) {
+ lderr(m_cct) << "rbd image " << m_name << " already exists" << dendl;
+ m_r_saved = r;
+ close_parent();
+ return;
+ }
+
+ create_child();
+}
+
+template <typename I>
+void CloneRequest<I>::create_child() {
+ ldout(m_cct, 15) << dendl;
+
+ uint64_t order = m_parent_image_ctx->order;
+ if (m_opts.get(RBD_IMAGE_OPTION_ORDER, &order) != 0) {
+ m_opts.set(RBD_IMAGE_OPTION_ORDER, order);
+ }
+ m_opts.set(RBD_IMAGE_OPTION_FEATURES, m_features);
+
+ using klass = CloneRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_create_child>(this);
+
+ RWLock::RLocker snap_locker(m_parent_image_ctx->snap_lock);
+ CreateRequest<I> *req = CreateRequest<I>::create(
+ m_config, m_ioctx, m_name, m_id, m_size, m_opts,
+ m_non_primary_global_image_id, m_primary_mirror_uuid, true,
+ m_op_work_queue, ctx);
+ req->send();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_create_child(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ if (r == -EBADF) {
+ ldout(m_cct, 5) << "image id already in-use" << dendl;
+ complete(r);
+ return;
+ } else if (r < 0) {
+ lderr(m_cct) << "error creating child: " << cpp_strerror(r) << dendl;
+ m_r_saved = r;
+ close_parent();
+ return;
+ }
+ open_child();
+}
+
+template <typename I>
+void CloneRequest<I>::open_child() {
+ ldout(m_cct, 15) << dendl;
+
+ m_imctx = I::create(m_name, "", nullptr, m_ioctx, false);
+
+ using klass = CloneRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_open_child>(this);
+
+ uint64_t flags = OPEN_FLAG_SKIP_OPEN_PARENT;
+ if ((m_features & RBD_FEATURE_MIGRATING) != 0) {
+ flags |= OPEN_FLAG_IGNORE_MIGRATING;
+ }
+
+ m_imctx->state->open(flags, ctx);
+}
+
+template <typename I>
+void CloneRequest<I>::handle_open_child(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ if (r < 0) {
+ m_imctx->destroy();
+ m_imctx = nullptr;
+
+ lderr(m_cct) << "Error opening new image: " << cpp_strerror(r) << dendl;
+ m_r_saved = r;
+ remove_child();
+ return;
+ }
+
+ attach_parent();
+}
+
+template <typename I>
+void CloneRequest<I>::attach_parent() {
+ ldout(m_cct, 15) << dendl;
+
+ auto ctx = create_context_callback<
+ CloneRequest<I>, &CloneRequest<I>::handle_attach_parent>(this);
+ auto req = AttachParentRequest<I>::create(
+ *m_imctx, m_pspec, m_size, false, ctx);
+ req->send();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_attach_parent(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "failed to attach parent: " << cpp_strerror(r) << dendl;
+ m_r_saved = r;
+ close_child();
+ return;
+ }
+
+ attach_child();
+}
+
+template <typename I>
+void CloneRequest<I>::attach_child() {
+ ldout(m_cct, 15) << dendl;
+
+ auto ctx = create_context_callback<
+ CloneRequest<I>, &CloneRequest<I>::handle_attach_child>(this);
+ auto req = AttachChildRequest<I>::create(
+ m_imctx, m_parent_image_ctx, m_parent_image_ctx->snap_id, nullptr, 0,
+ m_clone_format, ctx);
+ req->send();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_attach_child(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "failed to attach parent: " << cpp_strerror(r) << dendl;
+ m_r_saved = r;
+ close_child();
+ return;
+ }
+
+ metadata_list();
+}
+
+template <typename I>
+void CloneRequest<I>::metadata_list() {
+ ldout(m_cct, 15) << "start_key=" << m_last_metadata_key << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::metadata_list_start(&op, m_last_metadata_key, 0);
+
+ using klass = CloneRequest<I>;
+ librados::AioCompletion *comp =
+ create_rados_callback<klass, &klass::handle_metadata_list>(this);
+ m_out_bl.clear();
+ m_parent_image_ctx->md_ctx.aio_operate(m_parent_image_ctx->header_oid,
+ comp, &op, &m_out_bl);
+ comp->release();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_metadata_list(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ map<string, bufferlist> metadata;
+ if (r == 0) {
+ auto it = m_out_bl.cbegin();
+ r = cls_client::metadata_list_finish(&it, &metadata);
+ }
+
+ if (r < 0) {
+ if (r == -EOPNOTSUPP || r == -EIO) {
+ ldout(m_cct, 10) << "config metadata not supported by OSD" << dendl;
+ get_mirror_mode();
+ } else {
+ lderr(m_cct) << "couldn't list metadata: " << cpp_strerror(r) << dendl;
+ m_r_saved = r;
+ close_child();
+ }
+ return;
+ }
+
+ if (!metadata.empty()) {
+ m_pairs.insert(metadata.begin(), metadata.end());
+ m_last_metadata_key = m_pairs.rbegin()->first;
+ }
+
+ if (metadata.size() == MAX_KEYS) {
+ metadata_list();
+ } else {
+ metadata_set();
+ }
+}
+
+template <typename I>
+void CloneRequest<I>::metadata_set() {
+ if (m_pairs.empty()) {
+ get_mirror_mode();
+ return;
+ }
+
+ ldout(m_cct, 15) << dendl;
+
+ librados::ObjectWriteOperation op;
+ cls_client::metadata_set(&op, m_pairs);
+
+ using klass = CloneRequest<I>;
+ librados::AioCompletion *comp =
+ create_rados_callback<klass, &klass::handle_metadata_set>(this);
+ int r = m_ioctx.aio_operate(m_imctx->header_oid, comp, &op);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_metadata_set(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "couldn't set metadata: " << cpp_strerror(r) << dendl;
+ m_r_saved = r;
+ close_child();
+ } else {
+ get_mirror_mode();
+ }
+}
+
+template <typename I>
+void CloneRequest<I>::get_mirror_mode() {
+ ldout(m_cct, 15) << dendl;
+
+ if (!m_imctx->test_features(RBD_FEATURE_JOURNALING)) {
+ close_child();
+ return;
+ }
+
+ librados::ObjectReadOperation op;
+ cls_client::mirror_mode_get_start(&op);
+
+ using klass = CloneRequest<I>;
+ librados::AioCompletion *comp =
+ create_rados_callback<klass, &klass::handle_get_mirror_mode>(this);
+ m_out_bl.clear();
+ m_imctx->md_ctx.aio_operate(RBD_MIRRORING,
+ comp, &op, &m_out_bl);
+ comp->release();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_get_mirror_mode(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ if (r == 0) {
+ auto it = m_out_bl.cbegin();
+ r = cls_client::mirror_mode_get_finish(&it, &m_mirror_mode);
+ }
+
+ if (r < 0 && r != -ENOENT) {
+ lderr(m_cct) << "failed to retrieve mirror mode: " << cpp_strerror(r)
+ << dendl;
+
+ m_r_saved = r;
+ close_child();
+ } else {
+ if (m_mirror_mode == cls::rbd::MIRROR_MODE_POOL ||
+ !m_non_primary_global_image_id.empty()) {
+ enable_mirror();
+ } else {
+ close_child();
+ }
+ }
+}
+
+template <typename I>
+void CloneRequest<I>::enable_mirror() {
+ ldout(m_cct, 15) << dendl;
+
+ using klass = CloneRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_enable_mirror>(this);
+
+ mirror::EnableRequest<I> *req = mirror::EnableRequest<I>::create(
+ m_imctx->md_ctx, m_id, m_non_primary_global_image_id,
+ m_imctx->op_work_queue, ctx);
+ req->send();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_enable_mirror(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "failed to enable mirroring: " << cpp_strerror(r)
+ << dendl;
+ m_r_saved = r;
+ }
+ close_child();
+}
+
+template <typename I>
+void CloneRequest<I>::close_child() {
+ ldout(m_cct, 15) << dendl;
+
+ ceph_assert(m_imctx != nullptr);
+
+ using klass = CloneRequest<I>;
+ Context *ctx = create_async_context_callback(
+ *m_imctx, create_context_callback<
+ klass, &klass::handle_close_child>(this));
+ m_imctx->state->close(ctx);
+}
+
+template <typename I>
+void CloneRequest<I>::handle_close_child(int r) {
+ ldout(m_cct, 15) << dendl;
+
+ m_imctx->destroy();
+ m_imctx = nullptr;
+
+ if (r < 0) {
+ lderr(m_cct) << "couldn't close image: " << cpp_strerror(r) << dendl;
+ if (m_r_saved == 0) {
+ m_r_saved = r;
+ }
+ }
+
+ if (m_r_saved < 0) {
+ remove_child();
+ return;
+ }
+
+ close_parent();
+}
+
+template <typename I>
+void CloneRequest<I>::remove_child() {
+ ldout(m_cct, 15) << dendl;
+
+ using klass = CloneRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_remove_child>(this);
+
+ auto req = librbd::image::RemoveRequest<I>::create(
+ m_ioctx, m_name, m_id, false, false, m_no_op, m_op_work_queue, ctx);
+ req->send();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_remove_child(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "Error removing failed clone: "
+ << cpp_strerror(r) << dendl;
+ }
+
+ close_parent();
+}
+
+template <typename I>
+void CloneRequest<I>::close_parent() {
+ ldout(m_cct, 20) << dendl;
+ ceph_assert(m_parent_image_ctx != nullptr);
+
+ Context *ctx = create_async_context_callback(
+ *m_parent_image_ctx, create_context_callback<
+ CloneRequest<I>, &CloneRequest<I>::handle_close_parent>(this));
+ m_parent_image_ctx->state->close(ctx);
+}
+
+template <typename I>
+void CloneRequest<I>::handle_close_parent(int r) {
+ ldout(m_cct, 20) << "r=" << r << dendl;
+
+ m_parent_image_ctx->destroy();
+ m_parent_image_ctx = nullptr;
+
+ if (r < 0) {
+ lderr(m_cct) << "failed to close parent image: "
+ << cpp_strerror(r) << dendl;
+ if (m_r_saved == 0) {
+ m_r_saved = r;
+ }
+ }
+
+ complete(m_r_saved);
+}
+
+template <typename I>
+void CloneRequest<I>::complete(int r) {
+ ldout(m_cct, 15) << "r=" << r << dendl;
+
+ m_on_finish->complete(r);
+ delete this;
+}
+
+} //namespace image
+} //namespace librbd
+
+template class librbd::image::CloneRequest<librbd::ImageCtx>;