summaryrefslogtreecommitdiffstats
path: root/src/librbd/mirror/DisableRequest.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/librbd/mirror/DisableRequest.cc479
1 files changed, 479 insertions, 0 deletions
diff --git a/src/librbd/mirror/DisableRequest.cc b/src/librbd/mirror/DisableRequest.cc
new file mode 100644
index 000000000..09378ce58
--- /dev/null
+++ b/src/librbd/mirror/DisableRequest.cc
@@ -0,0 +1,479 @@
+// -*- 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/dout.h"
+#include "common/errno.h"
+#include "cls/journal/cls_journal_client.h"
+#include "journal/Journaler.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Journal.h"
+#include "librbd/Operations.h"
+#include "librbd/Utils.h"
+#include "librbd/asio/ContextWQ.h"
+#include "librbd/journal/PromoteRequest.h"
+#include "librbd/mirror/GetInfoRequest.h"
+#include "librbd/mirror/ImageRemoveRequest.h"
+#include "librbd/mirror/ImageStateUpdateRequest.h"
+#include "librbd/mirror/snapshot/PromoteRequest.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::mirror::DisableRequest: " \
+ << this << " " << __func__ << ": "
+
+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) {
+}
+
+template <typename I>
+void DisableRequest<I>::send() {
+ send_get_mirror_info();
+}
+
+template <typename I>
+void DisableRequest<I>::send_get_mirror_info() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+
+ using klass = DisableRequest<I>;
+ Context *ctx = util::create_context_callback<
+ klass, &klass::handle_get_mirror_info>(this);
+
+ auto req = GetInfoRequest<I>::create(*m_image_ctx, &m_mirror_image,
+ &m_promotion_state,
+ &m_primary_mirror_uuid, ctx);
+ req->send();
+}
+
+template <typename I>
+Context *DisableRequest<I>::handle_get_mirror_info(int *result) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << "r=" << *result << dendl;
+
+ if (*result < 0) {
+ if (*result == -ENOENT) {
+ ldout(cct, 20) << "mirroring is not enabled for this image" << dendl;
+ *result = 0;
+ } else {
+ lderr(cct) << "failed to get mirroring info: " << cpp_strerror(*result)
+ << dendl;
+ }
+ return m_on_finish;
+ }
+
+ m_is_primary = (m_promotion_state == PROMOTION_STATE_PRIMARY ||
+ m_promotion_state == PROMOTION_STATE_UNKNOWN);
+
+ 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_image_state_update();
+ return nullptr;
+}
+
+template <typename I>
+void DisableRequest<I>::send_image_state_update() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ auto ctx = util::create_context_callback<
+ DisableRequest<I>,
+ &DisableRequest<I>::handle_image_state_update>(this);
+ auto req = ImageStateUpdateRequest<I>::create(
+ m_image_ctx->md_ctx, m_image_ctx->id,
+ cls::rbd::MIRROR_IMAGE_STATE_DISABLING, m_mirror_image, ctx);
+ req->send();
+}
+
+template <typename I>
+Context *DisableRequest<I>::handle_image_state_update(int *result) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << "r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to disable mirroring: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ send_promote_image();
+ return nullptr;
+}
+
+template <typename I>
+void DisableRequest<I>::send_promote_image() {
+ if (m_is_primary) {
+ clean_mirror_state();
+ return;
+ }
+
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ auto ctx = util::create_context_callback<
+ DisableRequest<I>, &DisableRequest<I>::handle_promote_image>(this);
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ // Not primary -- shouldn't have the journal open
+ ceph_assert(m_image_ctx->journal == nullptr);
+
+ auto req = journal::PromoteRequest<I>::create(m_image_ctx, true, ctx);
+ req->send();
+ } else if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ auto req = mirror::snapshot::PromoteRequest<I>::create(
+ m_image_ctx, m_mirror_image.global_image_id, ctx);
+ req->send();
+ } else {
+ lderr(cct) << "unknown image mirror mode: " << m_mirror_image.mode << dendl;
+ ctx->complete(-EOPNOTSUPP);
+ }
+}
+
+template <typename I>
+Context *DisableRequest<I>::handle_promote_image(int *result) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << "r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to promote image: " << cpp_strerror(*result) << dendl;
+ return m_on_finish;
+ }
+
+ send_refresh_image();
+ return nullptr;
+}
+
+template <typename I>
+void DisableRequest<I>::send_refresh_image() {
+ if (!m_image_ctx->state->is_refresh_required()) {
+ clean_mirror_state();
+ return;
+ }
+
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ auto ctx = util::create_context_callback<
+ DisableRequest<I>,
+ &DisableRequest<I>::handle_refresh_image>(this);
+ m_image_ctx->state->refresh(ctx);
+}
+
+template <typename I>
+Context *DisableRequest<I>::handle_refresh_image(int* result) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << "r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to refresh image: " << cpp_strerror(*result) << dendl;
+ return m_on_finish;
+ }
+
+ clean_mirror_state();
+ return nullptr;
+}
+
+template <typename I>
+void DisableRequest<I>::clean_mirror_state() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ remove_mirror_snapshots();
+ } else {
+ send_get_clients();
+ }
+}
+
+template <typename I>
+void DisableRequest<I>::send_get_clients() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << 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) << "r=" << *result << dendl;
+
+ std::unique_lock locker{m_lock};
+ ceph_assert(m_current_ops.empty());
+
+ if (*result < 0) {
+ lderr(cct) << "failed to get registered clients: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ 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) << "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;
+ }
+ locker.unlock();
+
+ // no mirror clients to unregister
+ send_remove_mirror_image();
+ }
+
+ return nullptr;
+}
+
+template <typename I>
+void DisableRequest<I>::remove_mirror_snapshots() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ // remove snapshot-based mirroring snapshots
+ bool removing_snapshots = false;
+ {
+ std::lock_guard locker{m_lock};
+ std::shared_lock image_locker{m_image_ctx->image_lock};
+
+ for (auto &it : m_image_ctx->snap_info) {
+ auto &snap_info = it.second;
+ auto type = cls::rbd::get_snap_namespace_type(
+ snap_info.snap_namespace);
+ if (type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR) {
+ send_remove_snap("", snap_info.snap_namespace, snap_info.name);
+ removing_snapshots = true;
+ }
+ }
+ }
+
+ if (!removing_snapshots) {
+ send_remove_mirror_image();
+ }
+}
+
+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) << "client_id=" << client_id
+ << ", snap_name=" << snap_name << dendl;
+
+ ceph_assert(ceph_mutex_is_locked(m_lock));
+
+ m_current_ops[client_id]++;
+
+ Context *ctx = create_context_callback(
+ &DisableRequest<I>::handle_remove_snap, client_id);
+
+ ctx = new LambdaContext([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) << "r=" << *result << dendl;
+
+ std::unique_lock 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 mirroring snapshot: "
+ << cpp_strerror(*result) << dendl;
+ m_ret[client_id] = *result;
+ }
+
+ if (m_current_ops[client_id] == 0) {
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ ceph_assert(client_id.empty());
+ m_current_ops.erase(client_id);
+ if (m_ret[client_id] < 0) {
+ return m_on_finish;
+ }
+ locker.unlock();
+
+ send_remove_mirror_image();
+ return nullptr;
+ }
+
+ 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) << dendl;
+
+ ceph_assert(ceph_mutex_is_locked(m_lock));
+ 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) << "r=" << *result << dendl;
+
+ std::unique_lock 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;
+ }
+ locker.unlock();
+
+ send_get_clients();
+ return nullptr;
+}
+
+template <typename I>
+void DisableRequest<I>::send_remove_mirror_image() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ auto ctx = util::create_context_callback<
+ DisableRequest<I>,
+ &DisableRequest<I>::handle_remove_mirror_image>(this);
+ auto req = ImageRemoveRequest<I>::create(
+ m_image_ctx->md_ctx, m_mirror_image.global_image_id, m_image_ctx->id,
+ ctx);
+ req->send();
+}
+
+template <typename I>
+Context *DisableRequest<I>::handle_remove_mirror_image(int *result) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << "r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to remove mirror image: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ ldout(cct, 20) << "removed image state from rbd_mirroring object" << dendl;
+ 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 LambdaContext([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>;