// -*- 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 CloneRequest::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(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(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 void CloneRequest::send() { ldout(m_cct, 20) << dendl; validate_options(); } template void CloneRequest::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( "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 void CloneRequest::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, &CloneRequest::handle_open_parent>(this); m_parent_image_ctx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT, ctx); } template void CloneRequest::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 void CloneRequest::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 void CloneRequest::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; 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 void CloneRequest::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 void CloneRequest::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; Context *ctx = create_context_callback< klass, &klass::handle_create_child>(this); RWLock::RLocker snap_locker(m_parent_image_ctx->snap_lock); CreateRequest *req = CreateRequest::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 void CloneRequest::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 void CloneRequest::open_child() { ldout(m_cct, 15) << dendl; m_imctx = I::create(m_name, "", nullptr, m_ioctx, false); using klass = CloneRequest; 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 void CloneRequest::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 void CloneRequest::attach_parent() { ldout(m_cct, 15) << dendl; auto ctx = create_context_callback< CloneRequest, &CloneRequest::handle_attach_parent>(this); auto req = AttachParentRequest::create( *m_imctx, m_pspec, m_size, false, ctx); req->send(); } template void CloneRequest::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 void CloneRequest::attach_child() { ldout(m_cct, 15) << dendl; auto ctx = create_context_callback< CloneRequest, &CloneRequest::handle_attach_child>(this); auto req = AttachChildRequest::create( m_imctx, m_parent_image_ctx, m_parent_image_ctx->snap_id, nullptr, 0, m_clone_format, ctx); req->send(); } template void CloneRequest::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 void CloneRequest::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; librados::AioCompletion *comp = create_rados_callback(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 void CloneRequest::handle_metadata_list(int r) { ldout(m_cct, 15) << "r=" << r << dendl; map 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 void CloneRequest::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; librados::AioCompletion *comp = create_rados_callback(this); int r = m_ioctx.aio_operate(m_imctx->header_oid, comp, &op); ceph_assert(r == 0); comp->release(); } template void CloneRequest::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 void CloneRequest::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; librados::AioCompletion *comp = create_rados_callback(this); m_out_bl.clear(); m_imctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl); comp->release(); } template void CloneRequest::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 void CloneRequest::enable_mirror() { ldout(m_cct, 15) << dendl; using klass = CloneRequest; Context *ctx = create_context_callback< klass, &klass::handle_enable_mirror>(this); mirror::EnableRequest *req = mirror::EnableRequest::create( m_imctx->md_ctx, m_id, m_non_primary_global_image_id, m_imctx->op_work_queue, ctx); req->send(); } template void CloneRequest::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 void CloneRequest::close_child() { ldout(m_cct, 15) << dendl; ceph_assert(m_imctx != nullptr); using klass = CloneRequest; Context *ctx = create_async_context_callback( *m_imctx, create_context_callback< klass, &klass::handle_close_child>(this)); m_imctx->state->close(ctx); } template void CloneRequest::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 void CloneRequest::remove_child() { ldout(m_cct, 15) << dendl; using klass = CloneRequest; Context *ctx = create_context_callback< klass, &klass::handle_remove_child>(this); auto req = librbd::image::RemoveRequest::create( m_ioctx, m_name, m_id, false, false, m_no_op, m_op_work_queue, ctx); req->send(); } template void CloneRequest::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 void CloneRequest::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, &CloneRequest::handle_close_parent>(this)); m_parent_image_ctx->state->close(ctx); } template void CloneRequest::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 void CloneRequest::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;