#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/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
return m_on_finish;
}
- if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
- // remove 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();
- }
-
- return nullptr;
- }
-
send_promote_image();
return nullptr;
}
template <typename I>
void DisableRequest<I>::send_promote_image() {
if (m_is_primary) {
- send_get_clients();
+ clean_mirror_state();
return;
}
CephContext *cct = m_image_ctx->cct;
ldout(cct, 10) << 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();
+ 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>
return m_on_finish;
}
- send_get_clients();
+ 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;
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,
#include "test/librbd/test_mock_fixture.h"
#include "test/librbd/test_support.h"
#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockImageState.h"
#include "test/librbd/mock/MockOperations.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "test/librados_test_stub/MockTestMemRadosClient.h"
#include "librbd/mirror/GetInfoRequest.h"
#include "librbd/mirror/ImageRemoveRequest.h"
#include "librbd/mirror/ImageStateUpdateRequest.h"
+#include "librbd/mirror/snapshot/PromoteRequest.h"
namespace librbd {
} // namespace journal
namespace mirror {
-
template <>
struct GetInfoRequest<librbd::MockTestImageCtx> {
cls::rbd::MirrorImage *mirror_image;
ImageRemoveRequest<librbd::MockTestImageCtx> *ImageRemoveRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
ImageStateUpdateRequest<librbd::MockTestImageCtx> *ImageStateUpdateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+namespace snapshot {
+
+template <>
+struct PromoteRequest<librbd::MockTestImageCtx> {
+ Context *on_finish = nullptr;
+ static PromoteRequest *s_instance;
+ static PromoteRequest *create(librbd::MockTestImageCtx*,
+ const std::string& global_image_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ PromoteRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+PromoteRequest<librbd::MockTestImageCtx> *PromoteRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
} // namespace mirror
} // namespace librbd
public:
typedef DisableRequest<MockTestImageCtx> MockDisableRequest;
typedef Journal<MockTestImageCtx> MockJournal;
- typedef journal::PromoteRequest<MockTestImageCtx> MockPromoteRequest;
+ typedef journal::PromoteRequest<MockTestImageCtx> MockJournalPromoteRequest;
typedef mirror::GetInfoRequest<MockTestImageCtx> MockGetInfoRequest;
typedef mirror::ImageRemoveRequest<MockTestImageCtx> MockImageRemoveRequest;
typedef mirror::ImageStateUpdateRequest<MockTestImageCtx> MockImageStateUpdateRequest;
+ typedef mirror::snapshot::PromoteRequest<MockTestImageCtx> MockSnapshotPromoteRequest;
void expect_get_mirror_info(MockTestImageCtx &mock_image_ctx,
MockGetInfoRequest &mock_get_info_request,
}
void expect_journal_promote(MockTestImageCtx &mock_image_ctx,
- MockPromoteRequest &mock_promote_request, int r) {
+ MockJournalPromoteRequest &mock_promote_request,
+ int r) {
+ EXPECT_CALL(mock_promote_request, send())
+ .WillOnce(FinishRequest(&mock_promote_request, r, &mock_image_ctx));
+ }
+
+ void expect_snapshot_promote(MockTestImageCtx &mock_image_ctx,
+ MockSnapshotPromoteRequest &mock_promote_request,
+ int r) {
EXPECT_CALL(mock_promote_request, send())
.WillOnce(FinishRequest(&mock_promote_request, r, &mock_image_ctx));
}
+ void expect_is_refresh_required(MockTestImageCtx &mock_image_ctx,
+ bool refresh_required) {
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillOnce(Return(refresh_required));
+ }
+
+ void expect_refresh_image(MockTestImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
void expect_snap_remove(MockTestImageCtx &mock_image_ctx,
const std::string &snap_name, int r) {
EXPECT_CALL(*mock_image_ctx.operations, snap_remove(_, StrEq(snap_name), _))
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockPromoteRequest mock_promote_request;
+ MockJournalPromoteRequest mock_promote_request;
expect_op_work_queue(mock_image_ctx);
expect_mirror_image_state_update(
mock_image_ctx, mock_image_state_update_request, 0);
expect_journal_promote(mock_image_ctx, mock_promote_request, 0);
+ expect_is_refresh_required(mock_image_ctx, false);
expect_journal_client_list(mock_image_ctx, {}, 0);
MockImageRemoveRequest mock_image_remove_request;
expect_mirror_image_remove(
}
TEST_F(TestMockMirrorDisableRequest, JournalPromoteError) {
- REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
-
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockPromoteRequest mock_promote_request;
+ MockJournalPromoteRequest mock_promote_request;
expect_op_work_queue(mock_image_ctx);
ASSERT_EQ(-EINVAL, ctx.wait());
}
+TEST_F(TestMockMirrorDisableRequest, SnapshotPromoteError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSnapshotPromoteRequest mock_promote_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_snapshot_promote(mock_image_ctx, mock_promote_request, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, true, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
+TEST_F(TestMockMirrorDisableRequest, RefreshError) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ MockSnapshotPromoteRequest mock_promote_request;
+
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
+ MockImageStateUpdateRequest mock_image_state_update_request;
+ expect_mirror_image_state_update(
+ mock_image_ctx, mock_image_state_update_request, 0);
+ expect_snapshot_promote(mock_image_ctx, mock_promote_request, 0);
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh_image(mock_image_ctx, -EPERM);
+
+ C_SaferCond ctx;
+ auto req = new MockDisableRequest(&mock_image_ctx, true, true, &ctx);
+ req->send();
+ ASSERT_EQ(-EPERM, ctx.wait());
+}
+
TEST_F(TestMockMirrorDisableRequest, MirrorImageRemoveError) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);