summaryrefslogtreecommitdiffstats
path: root/src/librbd/mirror
diff options
context:
space:
mode:
Diffstat (limited to 'src/librbd/mirror')
-rw-r--r--src/librbd/mirror/DemoteRequest.cc198
-rw-r--r--src/librbd/mirror/DemoteRequest.h85
-rw-r--r--src/librbd/mirror/DisableRequest.cc492
-rw-r--r--src/librbd/mirror/DisableRequest.h141
-rw-r--r--src/librbd/mirror/EnableRequest.cc190
-rw-r--r--src/librbd/mirror/EnableRequest.h96
-rw-r--r--src/librbd/mirror/GetInfoRequest.cc115
-rw-r--r--src/librbd/mirror/GetInfoRequest.h81
-rw-r--r--src/librbd/mirror/GetStatusRequest.cc112
-rw-r--r--src/librbd/mirror/GetStatusRequest.h85
-rw-r--r--src/librbd/mirror/PromoteRequest.cc103
-rw-r--r--src/librbd/mirror/PromoteRequest.h75
-rw-r--r--src/librbd/mirror/Types.h20
13 files changed, 1793 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
+