diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
commit | 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch) | |
tree | e5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/librbd/mirror | |
parent | Initial commit. (diff) | |
download | ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip |
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/librbd/mirror/DemoteRequest.cc | 198 | ||||
-rw-r--r-- | src/librbd/mirror/DemoteRequest.h | 85 | ||||
-rw-r--r-- | src/librbd/mirror/DisableRequest.cc | 492 | ||||
-rw-r--r-- | src/librbd/mirror/DisableRequest.h | 141 | ||||
-rw-r--r-- | src/librbd/mirror/EnableRequest.cc | 190 | ||||
-rw-r--r-- | src/librbd/mirror/EnableRequest.h | 96 | ||||
-rw-r--r-- | src/librbd/mirror/GetInfoRequest.cc | 115 | ||||
-rw-r--r-- | src/librbd/mirror/GetInfoRequest.h | 81 | ||||
-rw-r--r-- | src/librbd/mirror/GetStatusRequest.cc | 112 | ||||
-rw-r--r-- | src/librbd/mirror/GetStatusRequest.h | 85 | ||||
-rw-r--r-- | src/librbd/mirror/PromoteRequest.cc | 103 | ||||
-rw-r--r-- | src/librbd/mirror/PromoteRequest.h | 75 | ||||
-rw-r--r-- | src/librbd/mirror/Types.h | 20 | ||||
-rw-r--r-- | src/librbd/mirroring_watcher/Types.cc | 136 | ||||
-rw-r--r-- | src/librbd/mirroring_watcher/Types.h | 102 |
15 files changed, 2031 insertions, 0 deletions
diff --git a/src/librbd/mirror/DemoteRequest.cc b/src/librbd/mirror/DemoteRequest.cc new file mode 100644 index 00000000..c5d38752 --- /dev/null +++ b/src/librbd/mirror/DemoteRequest.cc @@ -0,0 +1,198 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/mirror/DemoteRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "cls/rbd/cls_rbd_client.h" +#include "librbd/ExclusiveLock.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" +#include "librbd/Journal.h" +#include "librbd/Utils.h" +#include "librbd/mirror/GetInfoRequest.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::mirror::DemoteRequest: " << this \ + << " " << __func__ << ": " + +namespace librbd { +namespace mirror { + +using librbd::util::create_context_callback; + +template <typename I> +void DemoteRequest<I>::send() { + get_info(); +} + +template <typename I> +void DemoteRequest<I>::get_info() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << dendl; + + auto ctx = create_context_callback< + DemoteRequest<I>, &DemoteRequest<I>::handle_get_info>(this); + auto req = GetInfoRequest<I>::create(m_image_ctx, &m_mirror_image, + &m_promotion_state, ctx); + req->send(); +} + +template <typename I> +void DemoteRequest<I>::handle_get_info(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to retrieve mirroring state: " << cpp_strerror(r) + << dendl; + finish(r); + return; + } else if (m_mirror_image.state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) { + lderr(cct) << "mirroring is not currently enabled" << dendl; + finish(-EINVAL); + return; + } else if (m_promotion_state != PROMOTION_STATE_PRIMARY) { + lderr(cct) << "image is not primary" << dendl; + finish(-EINVAL); + return; + } + + acquire_lock(); +} + +template <typename I> +void DemoteRequest<I>::acquire_lock() { + CephContext *cct = m_image_ctx.cct; + + m_image_ctx.owner_lock.get_read(); + if (m_image_ctx.exclusive_lock == nullptr) { + m_image_ctx.owner_lock.put_read(); + lderr(cct) << "exclusive lock is not active" << dendl; + finish(-EINVAL); + return; + } + + // avoid accepting new requests from peers while we demote + // the image + m_image_ctx.exclusive_lock->block_requests(0); + m_blocked_requests = true; + + if (m_image_ctx.exclusive_lock->is_lock_owner()) { + m_image_ctx.owner_lock.put_read(); + demote(); + return; + } + + ldout(cct, 20) << dendl; + + auto ctx = create_context_callback< + DemoteRequest<I>, &DemoteRequest<I>::handle_acquire_lock>(this); + m_image_ctx.exclusive_lock->acquire_lock(ctx); + m_image_ctx.owner_lock.put_read(); +} + +template <typename I> +void DemoteRequest<I>::handle_acquire_lock(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to lock image: " << cpp_strerror(r) << dendl; + finish(r); + return; + } + + m_image_ctx.owner_lock.get_read(); + if (m_image_ctx.exclusive_lock != nullptr && + !m_image_ctx.exclusive_lock->is_lock_owner()) { + r = m_image_ctx.exclusive_lock->get_unlocked_op_error(); + m_image_ctx.owner_lock.put_read(); + lderr(cct) << "failed to acquire exclusive lock" << dendl; + finish(r); + return; + } + m_image_ctx.owner_lock.put_read(); + + demote(); +} + +template <typename I> +void DemoteRequest<I>::demote() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << dendl; + + auto ctx = create_context_callback< + DemoteRequest<I>, &DemoteRequest<I>::handle_demote>(this); + Journal<I>::demote(&m_image_ctx, ctx); +} + +template <typename I> +void DemoteRequest<I>::handle_demote(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + m_ret_val = r; + lderr(cct) << "failed to demote image: " << cpp_strerror(r) << dendl; + } + + release_lock(); +} + +template <typename I> +void DemoteRequest<I>::release_lock() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << dendl; + + m_image_ctx.owner_lock.get_read(); + if (m_image_ctx.exclusive_lock == nullptr) { + m_image_ctx.owner_lock.put_read(); + finish(0); + return; + } + + auto ctx = create_context_callback< + DemoteRequest<I>, &DemoteRequest<I>::handle_release_lock>(this); + m_image_ctx.exclusive_lock->release_lock(ctx); + m_image_ctx.owner_lock.put_read(); +} + +template <typename I> +void DemoteRequest<I>::handle_release_lock(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to release exclusive lock: " << cpp_strerror(r) + << dendl; + } + + finish(r); +} + +template <typename I> +void DemoteRequest<I>::finish(int r) { + if (m_ret_val < 0) { + r = m_ret_val; + } + + { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (m_blocked_requests && m_image_ctx.exclusive_lock != nullptr) { + m_image_ctx.exclusive_lock->unblock_requests(); + } + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace mirror +} // namespace librbd + +template class librbd::mirror::DemoteRequest<librbd::ImageCtx>; diff --git a/src/librbd/mirror/DemoteRequest.h b/src/librbd/mirror/DemoteRequest.h new file mode 100644 index 00000000..7dc0585e --- /dev/null +++ b/src/librbd/mirror/DemoteRequest.h @@ -0,0 +1,85 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIRROR_DEMOTE_REQUEST_H +#define CEPH_LIBRBD_MIRROR_DEMOTE_REQUEST_H + +#include "cls/rbd/cls_rbd_types.h" +#include "librbd/mirror/Types.h" + +struct Context; + +namespace librbd { + +struct ImageCtx; + +namespace mirror { + +template <typename ImageCtxT = librbd::ImageCtx> +class DemoteRequest { +public: + static DemoteRequest *create(ImageCtxT &image_ctx, Context *on_finish) { + return new DemoteRequest(image_ctx, on_finish); + } + + DemoteRequest(ImageCtxT &image_ctx, Context *on_finish) + : m_image_ctx(image_ctx), m_on_finish(on_finish) { + } + + void send(); + +private: + /** + * @verbatim + * + * <start> + * | + * v + * GET_INFO + * | + * v + * ACQUIRE_LOCK * * * * + * | * + * v * + * DEMOTE * + * | * + * v * + * RELEASE_LOCK * + * | * + * v * + * <finish> < * * * * * + * + * @endverbatim + */ + + ImageCtxT &m_image_ctx; + Context *m_on_finish; + + int m_ret_val = 0; + bool m_blocked_requests = false; + + cls::rbd::MirrorImage m_mirror_image; + PromotionState m_promotion_state = PROMOTION_STATE_PRIMARY; + + void get_info(); + void handle_get_info(int r); + + void acquire_lock(); + void handle_acquire_lock(int r); + + void demote(); + void handle_demote(int r); + + void release_lock(); + void handle_release_lock(int r); + + void finish(int r); + +}; + +} // namespace mirror +} // namespace librbd + +extern template class librbd::mirror::DemoteRequest<librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_MIRROR_DEMOTE_REQUEST_H diff --git a/src/librbd/mirror/DisableRequest.cc b/src/librbd/mirror/DisableRequest.cc new file mode 100644 index 00000000..6a21c560 --- /dev/null +++ b/src/librbd/mirror/DisableRequest.cc @@ -0,0 +1,492 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/mirror/DisableRequest.h" +#include "common/WorkQueue.h" +#include "common/dout.h" +#include "common/errno.h" +#include "cls/journal/cls_journal_client.h" +#include "cls/rbd/cls_rbd_client.h" +#include "journal/Journaler.h" +#include "librbd/ImageState.h" +#include "librbd/Journal.h" +#include "librbd/MirroringWatcher.h" +#include "librbd/Operations.h" +#include "librbd/Utils.h" +#include "librbd/journal/PromoteRequest.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::mirror::DisableRequest: " + +namespace librbd { +namespace mirror { + +using util::create_rados_callback; + +template <typename I> +DisableRequest<I>::DisableRequest(I *image_ctx, bool force, bool remove, + Context *on_finish) + : m_image_ctx(image_ctx), m_force(force), m_remove(remove), + m_on_finish(on_finish), m_lock("mirror::DisableRequest::m_lock") { +} + +template <typename I> +void DisableRequest<I>::send() { + send_get_mirror_image(); +} + +template <typename I> +void DisableRequest<I>::send_get_mirror_image() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + librados::ObjectReadOperation op; + cls_client::mirror_image_get_start(&op, m_image_ctx->id); + + using klass = DisableRequest<I>; + librados::AioCompletion *comp = + create_rados_callback<klass, &klass::handle_get_mirror_image>(this); + m_out_bl.clear(); + int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl); + ceph_assert(r == 0); + comp->release(); +} + +template <typename I> +Context *DisableRequest<I>::handle_get_mirror_image(int *result) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result == 0) { + auto iter = m_out_bl.cbegin(); + *result = cls_client::mirror_image_get_finish(&iter, &m_mirror_image); + } + + if (*result < 0) { + if (*result == -ENOENT) { + ldout(cct, 20) << this << " " << __func__ + << ": mirroring is not enabled for this image" << dendl; + *result = 0; + } else if (*result == -EOPNOTSUPP) { + ldout(cct, 5) << this << " " << __func__ + << ": mirroring is not supported by OSD" << dendl; + } else { + lderr(cct) << "failed to retrieve mirror image: " << cpp_strerror(*result) + << dendl; + } + return m_on_finish; + } + + send_get_tag_owner(); + return nullptr; +} + +template <typename I> +void DisableRequest<I>::send_get_tag_owner() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + using klass = DisableRequest<I>; + Context *ctx = util::create_context_callback< + klass, &klass::handle_get_tag_owner>(this); + + Journal<I>::is_tag_owner(m_image_ctx, &m_is_primary, ctx); +} + +template <typename I> +Context *DisableRequest<I>::handle_get_tag_owner(int *result) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result < 0) { + lderr(cct) << "failed to check tag ownership: " << cpp_strerror(*result) + << dendl; + return m_on_finish; + } + + if (!m_is_primary && !m_force) { + lderr(cct) << "mirrored image is not primary, " + << "add force option to disable mirroring" << dendl; + *result = -EINVAL; + return m_on_finish; + } + + send_set_mirror_image(); + return nullptr; +} + +template <typename I> +void DisableRequest<I>::send_set_mirror_image() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + m_mirror_image.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING; + + librados::ObjectWriteOperation op; + cls_client::mirror_image_set(&op, m_image_ctx->id, m_mirror_image); + + using klass = DisableRequest<I>; + librados::AioCompletion *comp = + create_rados_callback<klass, &klass::handle_set_mirror_image>(this); + m_out_bl.clear(); + int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op); + ceph_assert(r == 0); + comp->release(); +} + +template <typename I> +Context *DisableRequest<I>::handle_set_mirror_image(int *result) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result < 0) { + lderr(cct) << "failed to disable mirroring: " << cpp_strerror(*result) + << dendl; + return m_on_finish; + } + + send_notify_mirroring_watcher(); + return nullptr; +} + +template <typename I> +void DisableRequest<I>::send_notify_mirroring_watcher() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + using klass = DisableRequest<I>; + Context *ctx = util::create_context_callback< + klass, &klass::handle_notify_mirroring_watcher>(this); + + MirroringWatcher<I>::notify_image_updated( + m_image_ctx->md_ctx, cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + m_image_ctx->id, m_mirror_image.global_image_id, ctx); +} + +template <typename I> +Context *DisableRequest<I>::handle_notify_mirroring_watcher(int *result) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result < 0) { + lderr(cct) << "failed to send update notification: " + << cpp_strerror(*result) << dendl; + *result = 0; + } + + send_promote_image(); + return nullptr; +} + +template <typename I> +void DisableRequest<I>::send_promote_image() { + if (m_is_primary) { + send_get_clients(); + return; + } + + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + // Not primary -- shouldn't have the journal open + ceph_assert(m_image_ctx->journal == nullptr); + + using klass = DisableRequest<I>; + Context *ctx = util::create_context_callback< + klass, &klass::handle_promote_image>(this); + auto req = journal::PromoteRequest<I>::create(m_image_ctx, true, ctx); + req->send(); +} + +template <typename I> +Context *DisableRequest<I>::handle_promote_image(int *result) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result < 0) { + lderr(cct) << "failed to promote image: " << cpp_strerror(*result) << dendl; + return m_on_finish; + } + + send_get_clients(); + return nullptr; +} + +template <typename I> +void DisableRequest<I>::send_get_clients() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + using klass = DisableRequest<I>; + Context *ctx = util::create_context_callback< + klass, &klass::handle_get_clients>(this); + + std::string header_oid = ::journal::Journaler::header_oid(m_image_ctx->id); + m_clients.clear(); + cls::journal::client::client_list(m_image_ctx->md_ctx, header_oid, &m_clients, + ctx); +} + +template <typename I> +Context *DisableRequest<I>::handle_get_clients(int *result) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result < 0) { + lderr(cct) << "failed to get registered clients: " << cpp_strerror(*result) + << dendl; + return m_on_finish; + } + + Mutex::Locker locker(m_lock); + + ceph_assert(m_current_ops.empty()); + + for (auto client : m_clients) { + journal::ClientData client_data; + auto bl_it = client.data.cbegin(); + try { + using ceph::decode; + decode(client_data, bl_it); + } catch (const buffer::error &err) { + lderr(cct) << "failed to decode client data" << dendl; + m_error_result = -EBADMSG; + continue; + } + + journal::ClientMetaType type = client_data.get_client_meta_type(); + if (type != journal::ClientMetaType::MIRROR_PEER_CLIENT_META_TYPE) { + continue; + } + + if (m_current_ops.find(client.id) != m_current_ops.end()) { + // Should not happen. + lderr(cct) << this << " " << __func__ << ": clients with the same id " + << client.id << dendl; + continue; + } + + m_current_ops[client.id] = 0; + m_ret[client.id] = 0; + + journal::MirrorPeerClientMeta client_meta = + boost::get<journal::MirrorPeerClientMeta>(client_data.client_meta); + + for (const auto& sync : client_meta.sync_points) { + send_remove_snap(client.id, sync.snap_namespace, sync.snap_name); + } + + if (m_current_ops[client.id] == 0) { + // no snaps to remove + send_unregister_client(client.id); + } + } + + if (m_current_ops.empty()) { + if (m_error_result < 0) { + *result = m_error_result; + return m_on_finish; + } else if (!m_remove) { + return m_on_finish; + } + + // no mirror clients to unregister + send_remove_mirror_image(); + } + + return nullptr; +} + +template <typename I> +void DisableRequest<I>::send_remove_snap(const std::string &client_id, + const cls::rbd::SnapshotNamespace &snap_namespace, + const std::string &snap_name) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": client_id=" << client_id + << ", snap_name=" << snap_name << dendl; + + ceph_assert(m_lock.is_locked()); + + m_current_ops[client_id]++; + + Context *ctx = create_context_callback( + &DisableRequest<I>::handle_remove_snap, client_id); + + ctx = new FunctionContext([this, snap_namespace, snap_name, ctx](int r) { + m_image_ctx->operations->snap_remove(snap_namespace, + snap_name.c_str(), + ctx); + }); + + m_image_ctx->op_work_queue->queue(ctx, 0); +} + +template <typename I> +Context *DisableRequest<I>::handle_remove_snap(int *result, + const std::string &client_id) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + Mutex::Locker locker(m_lock); + + ceph_assert(m_current_ops[client_id] > 0); + m_current_ops[client_id]--; + + if (*result < 0 && *result != -ENOENT) { + lderr(cct) << + "failed to remove temporary snapshot created by remote peer: " + << cpp_strerror(*result) << dendl; + m_ret[client_id] = *result; + } + + if (m_current_ops[client_id] == 0) { + send_unregister_client(client_id); + } + + return nullptr; +} + +template <typename I> +void DisableRequest<I>::send_unregister_client( + const std::string &client_id) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + ceph_assert(m_lock.is_locked()); + ceph_assert(m_current_ops[client_id] == 0); + + Context *ctx = create_context_callback( + &DisableRequest<I>::handle_unregister_client, client_id); + + if (m_ret[client_id] < 0) { + m_image_ctx->op_work_queue->queue(ctx, m_ret[client_id]); + return; + } + + librados::ObjectWriteOperation op; + cls::journal::client::client_unregister(&op, client_id); + std::string header_oid = ::journal::Journaler::header_oid(m_image_ctx->id); + librados::AioCompletion *comp = create_rados_callback(ctx); + + int r = m_image_ctx->md_ctx.aio_operate(header_oid, comp, &op); + ceph_assert(r == 0); + comp->release(); +} + +template <typename I> +Context *DisableRequest<I>::handle_unregister_client( + int *result, const std::string &client_id) { + + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + Mutex::Locker locker(m_lock); + ceph_assert(m_current_ops[client_id] == 0); + m_current_ops.erase(client_id); + + if (*result < 0 && *result != -ENOENT) { + lderr(cct) << "failed to unregister remote journal client: " + << cpp_strerror(*result) << dendl; + m_error_result = *result; + } + + if (!m_current_ops.empty()) { + return nullptr; + } + + if (m_error_result < 0) { + *result = m_error_result; + return m_on_finish; + } + + send_get_clients(); + return nullptr; +} + +template <typename I> +void DisableRequest<I>::send_remove_mirror_image() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + librados::ObjectWriteOperation op; + cls_client::mirror_image_remove(&op, m_image_ctx->id); + + using klass = DisableRequest<I>; + librados::AioCompletion *comp = + create_rados_callback<klass, &klass::handle_remove_mirror_image>(this); + m_out_bl.clear(); + int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op); + ceph_assert(r == 0); + comp->release(); +} + +template <typename I> +Context *DisableRequest<I>::handle_remove_mirror_image(int *result) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result == -ENOENT) { + *result = 0; + } + + if (*result < 0) { + lderr(cct) << "failed to remove mirror image: " << cpp_strerror(*result) + << dendl; + return m_on_finish; + } + + ldout(cct, 20) << this << " " << __func__ + << ": removed image state from rbd_mirroring object" << dendl; + + send_notify_mirroring_watcher_removed(); + return nullptr; +} + +template <typename I> +void DisableRequest<I>::send_notify_mirroring_watcher_removed() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + using klass = DisableRequest<I>; + Context *ctx = util::create_context_callback< + klass, &klass::handle_notify_mirroring_watcher_removed>(this); + + MirroringWatcher<I>::notify_image_updated( + m_image_ctx->md_ctx, cls::rbd::MIRROR_IMAGE_STATE_DISABLED, m_image_ctx->id, + m_mirror_image.global_image_id, ctx); +} + +template <typename I> +Context *DisableRequest<I>::handle_notify_mirroring_watcher_removed( + int *result) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result < 0) { + lderr(cct) << "failed to send update notification: " + << cpp_strerror(*result) << dendl; + *result = 0; + } + + return m_on_finish; +} + +template <typename I> +Context *DisableRequest<I>::create_context_callback( + Context*(DisableRequest<I>::*handle)(int*, const std::string &client_id), + const std::string &client_id) { + + return new FunctionContext([this, handle, client_id](int r) { + Context *on_finish = (this->*handle)(&r, client_id); + if (on_finish != nullptr) { + on_finish->complete(r); + delete this; + } + }); +} + +} // namespace mirror +} // namespace librbd + +template class librbd::mirror::DisableRequest<librbd::ImageCtx>; diff --git a/src/librbd/mirror/DisableRequest.h b/src/librbd/mirror/DisableRequest.h new file mode 100644 index 00000000..1a3b1223 --- /dev/null +++ b/src/librbd/mirror/DisableRequest.h @@ -0,0 +1,141 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIRROR_DISABLE_REQUEST_H +#define CEPH_LIBRBD_MIRROR_DISABLE_REQUEST_H + +#include "include/buffer.h" +#include "common/Mutex.h" +#include "cls/journal/cls_journal_types.h" +#include "cls/rbd/cls_rbd_types.h" +#include <map> +#include <string> + +class Context; + +namespace librbd { + +class ImageCtx; + +namespace mirror { + +template <typename ImageCtxT = ImageCtx> +class DisableRequest { +public: + static DisableRequest *create(ImageCtxT *image_ctx, bool force, + bool remove, Context *on_finish) { + return new DisableRequest(image_ctx, force, remove, on_finish); + } + + DisableRequest(ImageCtxT *image_ctx, bool force, bool remove, + Context *on_finish); + + void send(); + +private: + /** + * @verbatim + * + * <start> + * | + * v + * GET_MIRROR_IMAGE * * * * * * * * * * * * * * * * * * * * * * * + * | * + * v * + * GET_TAG_OWNER * * * * * * * * * * * * * * * * * * * * * * * * + * | * + * v * + * SET_MIRROR_IMAGE * * * * * * * * * * * * * * * * * * * * * * * + * | * + * v * + * NOTIFY_MIRRORING_WATCHER * + * | * + * v * + * PROMOTE_IMAGE (skip if primary) * + * | * + * v * + * GET_CLIENTS <----------------------------------------\ * * * * + * | | (unregister clients) | * (on error) + * | |/----------------------------\ | * + * | | | | * + * | | /-----------\ (repeat | (repeat | (repeat + * | | | | as needed) | as needed) | as needed) + * | v v | | | * + * | REMOVE_SYNC_SNAP --/ * * * * * * | * * * * * * | * * * * + * | | | | * + * | v | | * + * | UNREGISTER_CLIENT ---------------/-------------/ * * * * + * | * + * | (no more clients * + * | to unregister) * + * v * + * REMOVE_MIRROR_IMAGE * * * * * * * * * * * * * * * * * * * * * + * | (skip if no remove) * + * v * + * NOTIFY_MIRRORING_WATCHER_REMOVED * + * | (skip if not primary or no remove) * + * v * + * <finish> < * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * @endverbatim + */ + + ImageCtxT *m_image_ctx; + bool m_force; + bool m_remove; + Context *m_on_finish; + + bool m_is_primary = false; + bufferlist m_out_bl; + cls::rbd::MirrorImage m_mirror_image; + std::set<cls::journal::Client> m_clients; + std::map<std::string, int> m_ret; + std::map<std::string, int> m_current_ops; + int m_error_result = 0; + mutable Mutex m_lock; + + void send_get_mirror_image(); + Context *handle_get_mirror_image(int *result); + + void send_get_tag_owner(); + Context *handle_get_tag_owner(int *result); + + void send_set_mirror_image(); + Context *handle_set_mirror_image(int *result); + + void send_notify_mirroring_watcher(); + Context *handle_notify_mirroring_watcher(int *result); + + void send_promote_image(); + Context *handle_promote_image(int *result); + + void send_get_clients(); + Context *handle_get_clients(int *result); + + void send_remove_snap(const std::string &client_id, + const cls::rbd::SnapshotNamespace &snap_namespace, + const std::string &snap_name); + Context *handle_remove_snap(int *result, const std::string &client_id); + + void send_unregister_client(const std::string &client_id); + Context *handle_unregister_client(int *result, const std::string &client_id); + + void send_remove_mirror_image(); + Context *handle_remove_mirror_image(int *result); + + void send_notify_mirroring_watcher_removed(); + Context *handle_notify_mirroring_watcher_removed(int *result); + + Context *create_context_callback( + Context*(DisableRequest<ImageCtxT>::*handle)( + int*, const std::string &client_id), + const std::string &client_id); + +}; + +} // namespace mirror +} // namespace librbd + +extern template class librbd::mirror::DisableRequest<librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_MIRROR_DISABLE_REQUEST_H diff --git a/src/librbd/mirror/EnableRequest.cc b/src/librbd/mirror/EnableRequest.cc new file mode 100644 index 00000000..a5c5b125 --- /dev/null +++ b/src/librbd/mirror/EnableRequest.cc @@ -0,0 +1,190 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/mirror/EnableRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "cls/rbd/cls_rbd_client.h" +#include "librbd/ImageState.h" +#include "librbd/Journal.h" +#include "librbd/MirroringWatcher.h" +#include "librbd/Utils.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::mirror::EnableRequest: " + +namespace librbd { +namespace mirror { + +using util::create_context_callback; +using util::create_rados_callback; + +template <typename I> +EnableRequest<I>::EnableRequest(librados::IoCtx &io_ctx, + const std::string &image_id, + const std::string &non_primary_global_image_id, + ContextWQ *op_work_queue, Context *on_finish) + : m_io_ctx(io_ctx), m_image_id(image_id), + m_non_primary_global_image_id(non_primary_global_image_id), + m_op_work_queue(op_work_queue), m_on_finish(on_finish), + m_cct(reinterpret_cast<CephContext*>(io_ctx.cct())) { +} + +template <typename I> +void EnableRequest<I>::send() { + send_get_mirror_image(); +} + +template <typename I> +void EnableRequest<I>::send_get_mirror_image() { + ldout(m_cct, 10) << this << " " << __func__ << dendl; + + librados::ObjectReadOperation op; + cls_client::mirror_image_get_start(&op, m_image_id); + + using klass = EnableRequest<I>; + librados::AioCompletion *comp = + create_rados_callback<klass, &klass::handle_get_mirror_image>(this); + m_out_bl.clear(); + int r = m_io_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl); + ceph_assert(r == 0); + comp->release(); +} + +template <typename I> +Context *EnableRequest<I>::handle_get_mirror_image(int *result) { + ldout(m_cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result == 0) { + auto iter = m_out_bl.cbegin(); + *result = cls_client::mirror_image_get_finish(&iter, &m_mirror_image); + } + + if (*result == 0) { + if (m_mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_ENABLED) { + ldout(m_cct, 10) << this << " " << __func__ + << ": mirroring is already enabled" << dendl; + } else { + lderr(m_cct) << "currently disabling" << dendl; + *result = -EINVAL; + } + return m_on_finish; + } + + if (*result != -ENOENT) { + lderr(m_cct) << "failed to retrieve mirror image: " << cpp_strerror(*result) + << dendl; + return m_on_finish; + } + + *result = 0; + m_mirror_image.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED; + if (m_non_primary_global_image_id.empty()) { + uuid_d uuid_gen; + uuid_gen.generate_random(); + m_mirror_image.global_image_id = uuid_gen.to_string(); + } else { + m_mirror_image.global_image_id = m_non_primary_global_image_id; + } + + send_get_tag_owner(); + return nullptr; +} + +template <typename I> +void EnableRequest<I>::send_get_tag_owner() { + if (!m_non_primary_global_image_id.empty()) { + send_set_mirror_image(); + return; + } + ldout(m_cct, 10) << this << " " << __func__ << dendl; + + using klass = EnableRequest<I>; + Context *ctx = create_context_callback< + klass, &klass::handle_get_tag_owner>(this); + librbd::Journal<>::is_tag_owner(m_io_ctx, m_image_id, &m_is_primary, + m_op_work_queue, ctx); +} + +template <typename I> +Context *EnableRequest<I>::handle_get_tag_owner(int *result) { + ldout(m_cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result < 0) { + lderr(m_cct) << "failed to check tag ownership: " << cpp_strerror(*result) + << dendl; + return m_on_finish; + } + + if (!m_is_primary) { + lderr(m_cct) << "last journal tag not owned by local cluster" << dendl; + *result = -EINVAL; + return m_on_finish; + } + + send_set_mirror_image(); + return nullptr; +} + +template <typename I> +void EnableRequest<I>::send_set_mirror_image() { + ldout(m_cct, 10) << this << " " << __func__ << dendl; + + librados::ObjectWriteOperation op; + cls_client::mirror_image_set(&op, m_image_id, m_mirror_image); + + using klass = EnableRequest<I>; + librados::AioCompletion *comp = + create_rados_callback<klass, &klass::handle_set_mirror_image>(this); + m_out_bl.clear(); + int r = m_io_ctx.aio_operate(RBD_MIRRORING, comp, &op); + ceph_assert(r == 0); + comp->release(); +} + +template <typename I> +Context *EnableRequest<I>::handle_set_mirror_image(int *result) { + ldout(m_cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result < 0) { + lderr(m_cct) << "failed to enable mirroring: " << cpp_strerror(*result) + << dendl; + return m_on_finish; + } + + send_notify_mirroring_watcher(); + return nullptr; +} + +template <typename I> +void EnableRequest<I>::send_notify_mirroring_watcher() { + ldout(m_cct, 10) << this << " " << __func__ << dendl; + + using klass = EnableRequest<I>; + Context *ctx = create_context_callback< + klass, &klass::handle_notify_mirroring_watcher>(this); + + MirroringWatcher<>::notify_image_updated(m_io_ctx, + cls::rbd::MIRROR_IMAGE_STATE_ENABLED, + m_image_id, + m_mirror_image.global_image_id, ctx); +} + +template <typename I> +Context *EnableRequest<I>::handle_notify_mirroring_watcher(int *result) { + ldout(m_cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result < 0) { + lderr(m_cct) << "failed to send update notification: " + << cpp_strerror(*result) << dendl; + *result = 0; + } + + return m_on_finish; +} + +} // namespace mirror +} // namespace librbd + +template class librbd::mirror::EnableRequest<librbd::ImageCtx>; diff --git a/src/librbd/mirror/EnableRequest.h b/src/librbd/mirror/EnableRequest.h new file mode 100644 index 00000000..965c2a36 --- /dev/null +++ b/src/librbd/mirror/EnableRequest.h @@ -0,0 +1,96 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIRROR_ENABLE_REQUEST_H +#define CEPH_LIBRBD_MIRROR_ENABLE_REQUEST_H + +#include "include/buffer_fwd.h" +#include "include/rados/librados_fwd.hpp" +#include "cls/rbd/cls_rbd_types.h" +#include <map> +#include <string> + +class Context; +class ContextWQ; + +namespace librbd { + +class ImageCtx; + +namespace mirror { + +template <typename ImageCtxT = ImageCtx> +class EnableRequest { +public: + static EnableRequest *create(ImageCtxT *image_ctx, Context *on_finish) { + return create(image_ctx->md_ctx, image_ctx->id, "", + image_ctx->op_work_queue, on_finish); + } + static EnableRequest *create(librados::IoCtx &io_ctx, + const std::string &image_id, + const std::string &non_primary_global_image_id, + ContextWQ *op_work_queue, Context *on_finish) { + return new EnableRequest(io_ctx, image_id, non_primary_global_image_id, + op_work_queue, on_finish); + } + + void send(); + +private: + /** + * @verbatim + * + * <start> + * | + * v + * GET_MIRROR_IMAGE * * * * * * * + * | * (on error) + * v * + * GET_TAG_OWNER * * * * * * * * + * | * + * v * + * SET_MIRROR_IMAGE * * * * * * * + * | * + * v * + * NOTIFY_MIRRORING_WATCHER * * * + * | * + * v * + * <finish> < * * * * * * * * * + * + * @endverbatim + */ + + EnableRequest(librados::IoCtx &io_ctx, const std::string &image_id, + const std::string &non_primary_global_image_id, + ContextWQ *op_work_queue, Context *on_finish); + + librados::IoCtx &m_io_ctx; + std::string m_image_id; + std::string m_non_primary_global_image_id; + ContextWQ *m_op_work_queue; + Context *m_on_finish; + + CephContext *m_cct = nullptr; + bool m_is_primary = false; + bufferlist m_out_bl; + cls::rbd::MirrorImage m_mirror_image; + + void send_get_mirror_image(); + Context *handle_get_mirror_image(int *result); + + void send_get_tag_owner(); + Context *handle_get_tag_owner(int *result); + + void send_set_mirror_image(); + Context *handle_set_mirror_image(int *result); + + void send_notify_mirroring_watcher(); + Context *handle_notify_mirroring_watcher(int *result); +}; + +} // namespace mirror +} // namespace librbd + +extern template class librbd::mirror::EnableRequest<librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_MIRROR_ENABLE_REQUEST_H diff --git a/src/librbd/mirror/GetInfoRequest.cc b/src/librbd/mirror/GetInfoRequest.cc new file mode 100644 index 00000000..460c8cb1 --- /dev/null +++ b/src/librbd/mirror/GetInfoRequest.cc @@ -0,0 +1,115 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/mirror/GetInfoRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "cls/rbd/cls_rbd_client.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" +#include "librbd/Journal.h" +#include "librbd/Utils.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::mirror::GetInfoRequest: " << this \ + << " " << __func__ << ": " + +namespace librbd { +namespace mirror { + +using librbd::util::create_context_callback; +using librbd::util::create_rados_callback; + +template <typename I> +void GetInfoRequest<I>::send() { + get_mirror_image(); +} + +template <typename I> +void GetInfoRequest<I>::get_mirror_image() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << dendl; + + librados::ObjectReadOperation op; + cls_client::mirror_image_get_start(&op, m_image_ctx.id); + + librados::AioCompletion *comp = create_rados_callback< + GetInfoRequest<I>, &GetInfoRequest<I>::handle_get_mirror_image>(this); + int r = m_image_ctx.md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl); + ceph_assert(r == 0); + comp->release(); +} + +template <typename I> +void GetInfoRequest<I>::handle_get_mirror_image(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + m_mirror_image->state = cls::rbd::MIRROR_IMAGE_STATE_DISABLED; + *m_promotion_state = PROMOTION_STATE_NON_PRIMARY; + if (r == 0) { + auto iter = m_out_bl.cbegin(); + r = cls_client::mirror_image_get_finish(&iter, m_mirror_image); + } + + if (r == -ENOENT || + m_mirror_image->state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) { + ldout(cct, 20) << "mirroring is disabled" << dendl; + finish(0); + return; + } else if (r < 0) { + lderr(cct) << "failed to retrieve mirroring state: " << cpp_strerror(r) + << dendl; + finish(r); + return; + } + + get_tag_owner(); +} + +template <typename I> +void GetInfoRequest<I>::get_tag_owner() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << dendl; + + auto ctx = create_context_callback< + GetInfoRequest<I>, &GetInfoRequest<I>::handle_get_tag_owner>(this); + Journal<I>::get_tag_owner(m_image_ctx.md_ctx, m_image_ctx.id, + &m_mirror_uuid, m_image_ctx.op_work_queue, ctx); +} + +template <typename I> +void GetInfoRequest<I>::handle_get_tag_owner(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to determine tag ownership: " << cpp_strerror(r) + << dendl; + finish(r); + return; + } + + if (m_mirror_uuid == Journal<>::LOCAL_MIRROR_UUID) { + *m_promotion_state = PROMOTION_STATE_PRIMARY; + } else if (m_mirror_uuid == Journal<>::ORPHAN_MIRROR_UUID) { + *m_promotion_state = PROMOTION_STATE_ORPHAN; + } + + finish(0); +} + +template <typename I> +void GetInfoRequest<I>::finish(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace mirror +} // namespace librbd + +template class librbd::mirror::GetInfoRequest<librbd::ImageCtx>; diff --git a/src/librbd/mirror/GetInfoRequest.h b/src/librbd/mirror/GetInfoRequest.h new file mode 100644 index 00000000..db8073c6 --- /dev/null +++ b/src/librbd/mirror/GetInfoRequest.h @@ -0,0 +1,81 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIRROR_GET_INFO_REQUEST_H +#define CEPH_LIBRBD_MIRROR_GET_INFO_REQUEST_H + +#include "include/buffer.h" +#include "librbd/mirror/Types.h" +#include <string> + +struct Context; +namespace cls { namespace rbd { struct MirrorImage; } } + +namespace librbd { + +struct ImageCtx; + +namespace mirror { + +template <typename ImageCtxT = librbd::ImageCtx> +class GetInfoRequest { +public: + static GetInfoRequest *create(ImageCtxT &image_ctx, + cls::rbd::MirrorImage *mirror_image, + PromotionState *promotion_state, + Context *on_finish) { + return new GetInfoRequest(image_ctx, mirror_image, promotion_state, + on_finish); + } + + GetInfoRequest(ImageCtxT &image_ctx, cls::rbd::MirrorImage *mirror_image, + PromotionState *promotion_state, Context *on_finish) + : m_image_ctx(image_ctx), m_mirror_image(mirror_image), + m_promotion_state(promotion_state), m_on_finish(on_finish) { + } + + void send(); + +private: + /** + * @verbatim + * + * <start> + * | + * v + * GET_MIRROR_IMAGE + * | + * v + * GET_TAG_OWNER + * | + * v + * <finish> + * + * @endverbatim + */ + + ImageCtxT &m_image_ctx; + cls::rbd::MirrorImage *m_mirror_image; + PromotionState *m_promotion_state; + Context *m_on_finish; + + bufferlist m_out_bl; + std::string m_mirror_uuid; + + void get_mirror_image(); + void handle_get_mirror_image(int r); + + void get_tag_owner(); + void handle_get_tag_owner(int r); + + void finish(int r); + +}; + +} // namespace mirror +} // namespace librbd + +extern template class librbd::mirror::GetInfoRequest<librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_MIRROR_GET_INFO_REQUEST_H + diff --git a/src/librbd/mirror/GetStatusRequest.cc b/src/librbd/mirror/GetStatusRequest.cc new file mode 100644 index 00000000..57096883 --- /dev/null +++ b/src/librbd/mirror/GetStatusRequest.cc @@ -0,0 +1,112 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/mirror/GetStatusRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "cls/rbd/cls_rbd_client.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" +#include "librbd/Journal.h" +#include "librbd/Utils.h" +#include "librbd/mirror/GetInfoRequest.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::mirror::GetStatusRequest: " << this \ + << " " << __func__ << ": " + +namespace librbd { +namespace mirror { + +using librbd::util::create_context_callback; +using librbd::util::create_rados_callback; + +template <typename I> +void GetStatusRequest<I>::send() { + *m_mirror_image_status = cls::rbd::MirrorImageStatus( + cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN, "status not found"); + + get_info(); +} + +template <typename I> +void GetStatusRequest<I>::get_info() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << dendl; + + auto ctx = create_context_callback< + GetStatusRequest<I>, &GetStatusRequest<I>::handle_get_info>(this); + auto req = GetInfoRequest<I>::create(m_image_ctx, m_mirror_image, + m_promotion_state, ctx); + req->send(); +} + +template <typename I> +void GetStatusRequest<I>::handle_get_info(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to retrieve mirroring state: " << cpp_strerror(r) + << dendl; + finish(r); + return; + } else if (m_mirror_image->state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) { + finish(0); + return; + } + + get_status(); +} + +template <typename I> +void GetStatusRequest<I>::get_status() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << dendl; + + librados::ObjectReadOperation op; + cls_client::mirror_image_status_get_start( + &op, m_mirror_image->global_image_id); + + librados::AioCompletion *comp = create_rados_callback< + GetStatusRequest<I>, &GetStatusRequest<I>::handle_get_status>(this); + int r = m_image_ctx.md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl); + ceph_assert(r == 0); + comp->release(); +} + +template <typename I> +void GetStatusRequest<I>::handle_get_status(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r == 0) { + auto iter = m_out_bl.cbegin(); + r = cls_client::mirror_image_status_get_finish(&iter, + m_mirror_image_status); + } + + if (r < 0 && r != -ENOENT) { + lderr(cct) << "failed to retrieve mirror image status: " << cpp_strerror(r) + << dendl; + finish(r); + return; + } + + finish(0); +} + +template <typename I> +void GetStatusRequest<I>::finish(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace mirror +} // namespace librbd + +template class librbd::mirror::GetStatusRequest<librbd::ImageCtx>; diff --git a/src/librbd/mirror/GetStatusRequest.h b/src/librbd/mirror/GetStatusRequest.h new file mode 100644 index 00000000..4c1a81f0 --- /dev/null +++ b/src/librbd/mirror/GetStatusRequest.h @@ -0,0 +1,85 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIRROR_GET_STATUS_REQUEST_H +#define CEPH_LIBRBD_MIRROR_GET_STATUS_REQUEST_H + +#include "include/buffer.h" +#include "librbd/mirror/Types.h" +#include <string> + +struct Context; +namespace cls { namespace rbd { struct MirrorImage; } } +namespace cls { namespace rbd { struct MirrorImageStatus; } } + +namespace librbd { + +struct ImageCtx; + +namespace mirror { + +template <typename ImageCtxT = librbd::ImageCtx> +class GetStatusRequest { +public: + static GetStatusRequest *create(ImageCtxT &image_ctx, + cls::rbd::MirrorImageStatus *status, + cls::rbd::MirrorImage *mirror_image, + PromotionState *promotion_state, + Context *on_finish) { + return new GetStatusRequest(image_ctx, status, mirror_image, + promotion_state, on_finish); + } + + GetStatusRequest(ImageCtxT &image_ctx, cls::rbd::MirrorImageStatus *status, + cls::rbd::MirrorImage *mirror_image, + PromotionState *promotion_state, Context *on_finish) + : m_image_ctx(image_ctx), m_mirror_image_status(status), + m_mirror_image(mirror_image), m_promotion_state(promotion_state), + m_on_finish(on_finish) { + } + + void send(); + +private: + /** + * @verbatim + * + * <start> + * | + * v + * GET_INFO + * | + * v + * GET_STATUS + * | + * v + * <finish> + * + * @endverbatim + */ + + ImageCtxT &m_image_ctx; + cls::rbd::MirrorImageStatus *m_mirror_image_status; + cls::rbd::MirrorImage *m_mirror_image; + PromotionState *m_promotion_state; + Context *m_on_finish; + + bufferlist m_out_bl; + + void get_info(); + void handle_get_info(int r); + + void get_status(); + void handle_get_status(int r); + + void finish(int r); + +}; + +} // namespace mirror +} // namespace librbd + +extern template class librbd::mirror::GetStatusRequest<librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_MIRROR_GET_STATUS_REQUEST_H + diff --git a/src/librbd/mirror/PromoteRequest.cc b/src/librbd/mirror/PromoteRequest.cc new file mode 100644 index 00000000..5603cb13 --- /dev/null +++ b/src/librbd/mirror/PromoteRequest.cc @@ -0,0 +1,103 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/mirror/PromoteRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "cls/rbd/cls_rbd_client.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" +#include "librbd/Journal.h" +#include "librbd/Utils.h" +#include "librbd/mirror/GetInfoRequest.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::mirror::PromoteRequest: " << this \ + << " " << __func__ << ": " + +namespace librbd { +namespace mirror { + +using librbd::util::create_context_callback; + +template <typename I> +void PromoteRequest<I>::send() { + get_info(); +} + +template <typename I> +void PromoteRequest<I>::get_info() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << dendl; + + auto ctx = create_context_callback< + PromoteRequest<I>, &PromoteRequest<I>::handle_get_info>(this); + auto req = GetInfoRequest<I>::create(m_image_ctx, &m_mirror_image, + &m_promotion_state, ctx); + req->send(); +} + +template <typename I> +void PromoteRequest<I>::handle_get_info(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to retrieve mirroring state: " << cpp_strerror(r) + << dendl; + finish(r); + return; + } else if (m_mirror_image.state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) { + lderr(cct) << "mirroring is not currently enabled" << dendl; + finish(-EINVAL); + return; + } else if (m_promotion_state == PROMOTION_STATE_PRIMARY) { + lderr(cct) << "image is already primary" << dendl; + finish(-EINVAL); + return; + } else if (m_promotion_state == PROMOTION_STATE_NON_PRIMARY && !m_force) { + lderr(cct) << "image is still primary within a remote cluster" << dendl; + finish(-EBUSY); + return; + } + + promote(); +} + +template <typename I> +void PromoteRequest<I>::promote() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << dendl; + + auto ctx = create_context_callback< + PromoteRequest<I>, &PromoteRequest<I>::handle_promote>(this); + Journal<I>::promote(&m_image_ctx, ctx); +} + +template <typename I> +void PromoteRequest<I>::handle_promote(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to promote image: " << cpp_strerror(r) + << dendl; + } + + finish(r); +} + +template <typename I> +void PromoteRequest<I>::finish(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace mirror +} // namespace librbd + +template class librbd::mirror::PromoteRequest<librbd::ImageCtx>; diff --git a/src/librbd/mirror/PromoteRequest.h b/src/librbd/mirror/PromoteRequest.h new file mode 100644 index 00000000..17609c5f --- /dev/null +++ b/src/librbd/mirror/PromoteRequest.h @@ -0,0 +1,75 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIRROR_PROMOTE_REQUEST_H +#define CEPH_LIBRBD_MIRROR_PROMOTE_REQUEST_H + +#include "cls/rbd/cls_rbd_types.h" +#include "librbd/mirror/Types.h" + +struct Context; + +namespace librbd { + +struct ImageCtx; + +namespace mirror { + +template <typename ImageCtxT = librbd::ImageCtx> +class PromoteRequest { +public: + static PromoteRequest *create(ImageCtxT &image_ctx, bool force, + Context *on_finish) { + return new PromoteRequest(image_ctx, force, on_finish); + } + + PromoteRequest(ImageCtxT &image_ctx, bool force, Context *on_finish) + : m_image_ctx(image_ctx), m_force(force), m_on_finish(on_finish) { + } + + void send(); + +private: + /** + * @verbatim + * + * <start> + * | + * v + * GET_INFO + * | + * v + * GET_TAG_OWNER + * | + * v + * PROMOTE + * | + * v + * <finish> + * + * @endverbatim + */ + + ImageCtxT &m_image_ctx; + bool m_force; + Context *m_on_finish; + + cls::rbd::MirrorImage m_mirror_image; + PromotionState m_promotion_state = PROMOTION_STATE_PRIMARY; + + void get_info(); + void handle_get_info(int r); + + void promote(); + void handle_promote(int r); + + void finish(int r); + +}; + +} // namespace mirror +} // namespace librbd + +extern template class librbd::mirror::PromoteRequest<librbd::ImageCtx>; + +#endif // CEPH_LIBRBD_MIRROR_PROMOTE_REQUEST_H diff --git a/src/librbd/mirror/Types.h b/src/librbd/mirror/Types.h new file mode 100644 index 00000000..38511bdb --- /dev/null +++ b/src/librbd/mirror/Types.h @@ -0,0 +1,20 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIRROR_TYPES_H +#define CEPH_LIBRBD_MIRROR_TYPES_H + +namespace librbd { +namespace mirror { + +enum PromotionState { + PROMOTION_STATE_PRIMARY, + PROMOTION_STATE_NON_PRIMARY, + PROMOTION_STATE_ORPHAN +}; + +} // namespace mirror +} // namespace librbd + +#endif // CEPH_LIBRBD_MIRROR_TYPES_H + diff --git a/src/librbd/mirroring_watcher/Types.cc b/src/librbd/mirroring_watcher/Types.cc new file mode 100644 index 00000000..3226b635 --- /dev/null +++ b/src/librbd/mirroring_watcher/Types.cc @@ -0,0 +1,136 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "common/Formatter.h" +#include "include/ceph_assert.h" +#include "include/stringify.h" +#include "librbd/mirroring_watcher/Types.h" +#include "librbd/watcher/Utils.h" + +namespace librbd { +namespace mirroring_watcher { + +namespace { + +class DumpPayloadVisitor : public boost::static_visitor<void> { +public: + explicit DumpPayloadVisitor(Formatter *formatter) : m_formatter(formatter) {} + + template <typename Payload> + inline void operator()(const Payload &payload) const { + NotifyOp notify_op = Payload::NOTIFY_OP; + m_formatter->dump_string("notify_op", stringify(notify_op)); + payload.dump(m_formatter); + } + +private: + ceph::Formatter *m_formatter; +}; + +} // anonymous namespace + +void ModeUpdatedPayload::encode(bufferlist &bl) const { + using ceph::encode; + encode(static_cast<uint32_t>(mirror_mode), bl); +} + +void ModeUpdatedPayload::decode(__u8 version, bufferlist::const_iterator &iter) { + using ceph::decode; + uint32_t mirror_mode_decode; + decode(mirror_mode_decode, iter); + mirror_mode = static_cast<cls::rbd::MirrorMode>(mirror_mode_decode); +} + +void ModeUpdatedPayload::dump(Formatter *f) const { + f->dump_stream("mirror_mode") << mirror_mode; +} + +void ImageUpdatedPayload::encode(bufferlist &bl) const { + using ceph::encode; + encode(static_cast<uint32_t>(mirror_image_state), bl); + encode(image_id, bl); + encode(global_image_id, bl); +} + +void ImageUpdatedPayload::decode(__u8 version, bufferlist::const_iterator &iter) { + using ceph::decode; + uint32_t mirror_image_state_decode; + decode(mirror_image_state_decode, iter); + mirror_image_state = static_cast<cls::rbd::MirrorImageState>( + mirror_image_state_decode); + decode(image_id, iter); + decode(global_image_id, iter); +} + +void ImageUpdatedPayload::dump(Formatter *f) const { + f->dump_stream("mirror_image_state") << mirror_image_state; + f->dump_string("image_id", image_id); + f->dump_string("global_image_id", global_image_id); +} + +void UnknownPayload::encode(bufferlist &bl) const { + ceph_abort(); +} + +void UnknownPayload::decode(__u8 version, bufferlist::const_iterator &iter) { +} + +void UnknownPayload::dump(Formatter *f) const { +} + +void NotifyMessage::encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + boost::apply_visitor(watcher::util::EncodePayloadVisitor(bl), payload); + ENCODE_FINISH(bl); +} + +void NotifyMessage::decode(bufferlist::const_iterator& iter) { + DECODE_START(1, iter); + + uint32_t notify_op; + decode(notify_op, iter); + + // select the correct payload variant based upon the encoded op + switch (notify_op) { + case NOTIFY_OP_MODE_UPDATED: + payload = ModeUpdatedPayload(); + break; + case NOTIFY_OP_IMAGE_UPDATED: + payload = ImageUpdatedPayload(); + break; + default: + payload = UnknownPayload(); + break; + } + + apply_visitor(watcher::util::DecodePayloadVisitor(struct_v, iter), payload); + DECODE_FINISH(iter); +} + +void NotifyMessage::dump(Formatter *f) const { + apply_visitor(DumpPayloadVisitor(f), payload); +} + +void NotifyMessage::generate_test_instances(std::list<NotifyMessage *> &o) { + o.push_back(new NotifyMessage(ModeUpdatedPayload(cls::rbd::MIRROR_MODE_DISABLED))); + o.push_back(new NotifyMessage(ImageUpdatedPayload(cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + "image id", "global image id"))); +} + +std::ostream &operator<<(std::ostream &out, const NotifyOp &op) { + switch (op) { + case NOTIFY_OP_MODE_UPDATED: + out << "ModeUpdated"; + break; + case NOTIFY_OP_IMAGE_UPDATED: + out << "ImageUpdated"; + break; + default: + out << "Unknown (" << static_cast<uint32_t>(op) << ")"; + break; + } + return out; +} + +} // namespace mirroring_watcher +} // namespace librbd diff --git a/src/librbd/mirroring_watcher/Types.h b/src/librbd/mirroring_watcher/Types.h new file mode 100644 index 00000000..1e096a9d --- /dev/null +++ b/src/librbd/mirroring_watcher/Types.h @@ -0,0 +1,102 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIRRORING_WATCHER_TYPES_H +#define CEPH_LIBRBD_MIRRORING_WATCHER_TYPES_H + +#include "include/int_types.h" +#include "include/buffer_fwd.h" +#include "include/encoding.h" +#include "cls/rbd/cls_rbd_types.h" +#include <iosfwd> +#include <list> +#include <string> +#include <boost/variant.hpp> + +namespace ceph { class Formatter; } + +namespace librbd { +namespace mirroring_watcher { + +enum NotifyOp { + NOTIFY_OP_MODE_UPDATED = 0, + NOTIFY_OP_IMAGE_UPDATED = 1 +}; + +struct ModeUpdatedPayload { + static const NotifyOp NOTIFY_OP = NOTIFY_OP_MODE_UPDATED; + + cls::rbd::MirrorMode mirror_mode = cls::rbd::MIRROR_MODE_DISABLED; + + ModeUpdatedPayload() { + } + ModeUpdatedPayload(cls::rbd::MirrorMode mirror_mode) + : mirror_mode(mirror_mode) { + } + + void encode(bufferlist &bl) const; + void decode(__u8 version, bufferlist::const_iterator &iter); + void dump(Formatter *f) const; +}; + +struct ImageUpdatedPayload { + static const NotifyOp NOTIFY_OP = NOTIFY_OP_IMAGE_UPDATED; + + cls::rbd::MirrorImageState mirror_image_state = + cls::rbd::MIRROR_IMAGE_STATE_ENABLED; + std::string image_id; + std::string global_image_id; + + ImageUpdatedPayload() { + } + ImageUpdatedPayload(cls::rbd::MirrorImageState mirror_image_state, + const std::string &image_id, + const std::string &global_image_id) + : mirror_image_state(mirror_image_state), image_id(image_id), + global_image_id(global_image_id) { + } + + void encode(bufferlist &bl) const; + void decode(__u8 version, bufferlist::const_iterator &iter); + void dump(Formatter *f) const; +}; + +struct UnknownPayload { + static const NotifyOp NOTIFY_OP = static_cast<NotifyOp>(-1); + + UnknownPayload() { + } + + void encode(bufferlist &bl) const; + void decode(__u8 version, bufferlist::const_iterator &iter); + void dump(Formatter *f) const; +}; + +typedef boost::variant<ModeUpdatedPayload, + ImageUpdatedPayload, + UnknownPayload> Payload; + +struct NotifyMessage { + NotifyMessage(const Payload &payload = UnknownPayload()) : payload(payload) { + } + + Payload payload; + + void encode(bufferlist& bl) const; + void decode(bufferlist::const_iterator& it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list<NotifyMessage *> &o); +}; + +WRITE_CLASS_ENCODER(NotifyMessage); + +std::ostream &operator<<(std::ostream &out, const NotifyOp &op); + +} // namespace mirroring_watcher +} // namespace librbd + +using librbd::mirroring_watcher::encode; +using librbd::mirroring_watcher::decode; + +#endif // CEPH_LIBRBD_MIRRORING_WATCHER_TYPES_H |