Journal<I>::promote(&m_image_ctx, ctx);
} 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, m_force, ctx);
+ &m_image_ctx, m_mirror_image.global_image_id, ctx);
req->send();
} else {
lderr(cct) << "unknown image mirror mode: " << m_mirror_image.mode << dendl;
if (!util::can_create_primary_snapshot(
m_image_ctx,
((m_flags & CREATE_PRIMARY_FLAG_DEMOTED) != 0),
- ((m_flags & CREATE_PRIMARY_FLAG_FORCE) != 0), nullptr)) {
+ ((m_flags & CREATE_PRIMARY_FLAG_FORCE) != 0), nullptr, nullptr)) {
finish(-EINVAL);
return;
}
template <typename I>
void PromoteRequest<I>::send() {
CephContext *cct = m_image_ctx->cct;
+ bool requires_orphan = false;
if (!util::can_create_primary_snapshot(m_image_ctx, false, true,
+ &requires_orphan,
&m_rollback_snap_id)) {
lderr(cct) << "cannot promote" << dendl;
finish(-EINVAL);
return;
- } else if (m_rollback_snap_id == CEPH_NOSNAP) {
+ } else if (m_rollback_snap_id == CEPH_NOSNAP && !requires_orphan) {
create_promote_snapshot();
return;
}
- if (!m_force) {
- lderr(cct) << "cannot promote: needs rollback and force not set" << dendl;
- finish(-EINVAL);
- return;
- }
-
create_orphan_snapshot();
}
public:
static PromoteRequest *create(ImageCtxT *image_ctx,
const std::string& global_image_id,
- bool force, Context *on_finish) {
- return new PromoteRequest(image_ctx, global_image_id, force, on_finish);
+ Context *on_finish) {
+ return new PromoteRequest(image_ctx, global_image_id, on_finish);
}
- PromoteRequest(ImageCtxT *image_ctx,
- const std::string& global_image_id, bool force,
+ PromoteRequest(ImageCtxT *image_ctx, const std::string& global_image_id,
Context *on_finish)
: m_image_ctx(image_ctx), m_global_image_id(global_image_id),
- m_force(force), m_on_finish(on_finish) {
+ m_on_finish(on_finish) {
}
void send();
* | (can promote)
* |\----------------------------------------\
* | |
- * | (needs rollback) |
- * v |
+ * | |
+ * v (skip if not needed) |
* CREATE_ORPHAN_SNAPSHOT |
* | |
* | /-- UNREGISTER_UPDATE_WATCHER <-\ |
ImageCtxT *m_image_ctx;
std::string m_global_image_id;
- bool m_force;
Context *m_on_finish;
uint64_t m_rollback_snap_id = CEPH_NOSNAP;
template <typename I>
bool can_create_primary_snapshot(I *image_ctx, bool demoted, bool force,
+ bool* requires_orphan,
uint64_t *rollback_snap_id) {
CephContext *cct = image_ctx->cct;
+ if (requires_orphan != nullptr) {
+ *requires_orphan = false;
+ }
if (rollback_snap_id) {
*rollback_snap_id = CEPH_NOSNAP;
}
}
ldout(cct, 20) << "previous snapshot snap_id=" << it->first << " "
<< *mirror_ns << dendl;
- if ((mirror_ns->state == cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED ||
- mirror_ns->state ==
- cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED) &&
- !force) {
+ if (mirror_ns->is_demoted() && !force) {
lderr(cct) << "trying to create primary snapshot without force "
<< "when previous primary snapshot is demoted"
<< dendl;
<< dendl;
return false;
}
+
+ if (requires_orphan != nullptr) {
+ *requires_orphan = !mirror_ns->is_demoted();
+ }
if (!mirror_ns->complete) {
ldout(cct, 20) << "needs rollback" << dendl;
if (!rollback_snap_id) {
template bool librbd::mirror::snapshot::util::can_create_primary_snapshot(
librbd::ImageCtx *image_ctx, bool demoted, bool force,
- uint64_t *rollback_snap_id);
+ bool* requires_orphan, uint64_t *rollback_snap_id);
template bool librbd::mirror::snapshot::util::can_create_non_primary_snapshot(
librbd::ImageCtx *image_ctx);
template <typename ImageCtxT = librbd::ImageCtx>
bool can_create_primary_snapshot(ImageCtxT *image_ctx, bool demoted, bool force,
+ bool* requires_orphan,
uint64_t *rollback_snap_id);
template <typename ImageCtxT = librbd::ImageCtx>
template<> bool can_create_primary_snapshot(librbd::MockTestImageCtx *image_ctx,
bool demoted, bool force,
+ bool* requires_orphan,
uint64_t *rollback_snap_id) {
return Mock::s_instance->can_create_primary_snapshot(image_ctx, demoted,
force, rollback_snap_id);
s_instance = this;
}
- MOCK_METHOD4(can_create_primary_snapshot,
- bool(librbd::MockTestImageCtx *, bool, bool, uint64_t *));
+ MOCK_METHOD5(can_create_primary_snapshot,
+ bool(librbd::MockTestImageCtx *, bool, bool, bool*, uint64_t *));
};
Mock *Mock::s_instance = nullptr;
template<> bool can_create_primary_snapshot(librbd::MockTestImageCtx *image_ctx,
bool demoted, bool force,
+ bool* requires_orphan,
uint64_t *rollback_snap_id) {
return Mock::s_instance->can_create_primary_snapshot(image_ctx, demoted,
- force, rollback_snap_id);
+ force, requires_orphan,
+ rollback_snap_id);
}
} // namespace util
using ::testing::Return;
using ::testing::StrEq;
using ::testing::WithArg;
+using ::testing::WithArgs;
class TestMockMirrorSnapshotPromoteRequest : public TestMockFixture {
public:
typedef util::Mock MockUtils;
void expect_can_create_primary_snapshot(MockUtils &mock_utils, bool force,
+ bool requires_orphan,
uint64_t rollback_snap_id,
bool result) {
EXPECT_CALL(mock_utils,
- can_create_primary_snapshot(_, false, force, _))
+ can_create_primary_snapshot(_, false, force, _, _))
.WillOnce(DoAll(
- WithArg<3>(Invoke([rollback_snap_id](uint64_t *snap_id) {
- *snap_id = rollback_snap_id;
- })),
+ WithArgs<3,4 >(Invoke(
+ [requires_orphan, rollback_snap_id]
+ (bool* orphan, uint64_t *snap_id) {
+ *orphan = requires_orphan;
+ *snap_id = rollback_snap_id;
+ })),
Return(result)));
}
MockTestImageCtx mock_image_ctx(*ictx);
- const bool force = false;
+ InSequence seq;
+
+ MockUtils mock_utils;
+ expect_can_create_primary_snapshot(mock_utils, true, false, CEPH_NOSNAP,
+ true);
+ MockCreatePrimaryRequest mock_create_primary_request;
+ expect_create_promote_snapshot(mock_image_ctx, mock_create_primary_request,
+ 0);
+ C_SaferCond ctx;
+ auto req = new MockPromoteRequest(&mock_image_ctx, "gid", &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotPromoteRequest, SuccessForce) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
InSequence seq;
MockUtils mock_utils;
- expect_can_create_primary_snapshot(mock_utils, true, CEPH_NOSNAP, true);
+ expect_can_create_primary_snapshot(mock_utils, true, true, CEPH_NOSNAP, true);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_orphan_snapshot(mock_image_ctx, mock_create_non_primary_request,
+ 0);
+ MockListWatchersRequest mock_list_watchers_request;
+ expect_list_watchers(mock_image_ctx, mock_list_watchers_request, {}, 0);
+ expect_acquire_lock(mock_image_ctx, 0);
+
+ SnapInfo snap_info = {"snap", cls::rbd::MirrorSnapshotNamespace{}, 0,
+ {}, 0, 0, {}};
MockCreatePrimaryRequest mock_create_primary_request;
expect_create_promote_snapshot(mock_image_ctx, mock_create_primary_request,
0);
+ expect_release_lock(mock_image_ctx, 0);
+
C_SaferCond ctx;
- auto req = new MockPromoteRequest(&mock_image_ctx, "gid", force, &ctx);
+ auto req = new MockPromoteRequest(&mock_image_ctx, "gid", &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
}
mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
}
- const bool force = true;
-
InSequence seq;
MockUtils mock_utils;
- expect_can_create_primary_snapshot(mock_utils, true, 123, true);
+ expect_can_create_primary_snapshot(mock_utils, true, false, 123, true);
MockCreateNonPrimaryRequest mock_create_non_primary_request;
expect_create_orphan_snapshot(mock_image_ctx, mock_create_non_primary_request,
0);
expect_release_lock(mock_image_ctx, 0);
C_SaferCond ctx;
- auto req = new MockPromoteRequest(&mock_image_ctx, "gid", force, &ctx);
+ auto req = new MockPromoteRequest(&mock_image_ctx, "gid", &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
}
MockTestImageCtx mock_image_ctx(*ictx);
expect_op_work_queue(mock_image_ctx);
- const bool force = false;
-
InSequence seq;
MockUtils mock_utils;
- expect_can_create_primary_snapshot(mock_utils, true, CEPH_NOSNAP, false);
+ expect_can_create_primary_snapshot(mock_utils, true, false, CEPH_NOSNAP,
+ false);
C_SaferCond ctx;
- auto req = new MockPromoteRequest(&mock_image_ctx, "gid", force, &ctx);
+ auto req = new MockPromoteRequest(&mock_image_ctx, "gid", &ctx);
req->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
#include "librbd/mirror/snapshot/Utils.cc"
template bool librbd::mirror::snapshot::util::can_create_primary_snapshot(
librbd::MockTestImageCtx *image_ctx, bool demoted, bool force,
- uint64_t *rollback_snap_id);
+ bool* requires_orphan, uint64_t *rollback_snap_id);
template bool librbd::mirror::snapshot::util::can_create_non_primary_snapshot(
librbd::MockTestImageCtx *image_ctx);
MockTestImageCtx mock_image_ctx(*ictx);
// no previous mirror snapshots found
+ bool requires_orphan;
uint64_t rollback_snap_id;
ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, false,
+ &requires_orphan,
&rollback_snap_id));
+ ASSERT_FALSE(requires_orphan);
ASSERT_EQ(rollback_snap_id, CEPH_NOSNAP);
cls::rbd::MirrorSnapshotNamespace nns{
// without force, previous snapshot is non-primary
ASSERT_FALSE(util::can_create_primary_snapshot(&mock_image_ctx, false, false,
- nullptr));
+ nullptr, nullptr));
// demoted, previous snapshot is non-primary
ASSERT_FALSE(util::can_create_primary_snapshot(&mock_image_ctx, true, true,
- nullptr));
+ nullptr, nullptr));
// previous non-primary snapshot is copied
ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
+ &requires_orphan,
&rollback_snap_id));
+ ASSERT_TRUE(requires_orphan);
ASSERT_EQ(rollback_snap_id, CEPH_NOSNAP);
nns.complete = false;
// previous non-primary snapshot is not copied yet
ASSERT_FALSE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
- nullptr));
+ nullptr, nullptr));
// can rollback
ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
- &rollback_snap_id));
+ nullptr, &rollback_snap_id));
ASSERT_EQ(rollback_snap_id, copied_snap_id);
nns.state = cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED;
// previous non-primary snapshot is orphan
ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
- nullptr));
+ nullptr, nullptr));
cls::rbd::MirrorSnapshotNamespace pns{
cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {"uuid"}, "", CEPH_NOSNAP};
// previous primary snapshot is demoted, no force
ASSERT_FALSE(util::can_create_primary_snapshot(&mock_image_ctx, false, false,
- nullptr));
+ nullptr, nullptr));
// previous primary snapshot is demoted, force
ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, true,
- nullptr));
+ nullptr, nullptr));
pns.state = cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY;
snap_create(mock_image_ctx, pns, "PS2");
// previous snapshot is not demoted primary
ASSERT_TRUE(util::can_create_primary_snapshot(&mock_image_ctx, false, false,
- nullptr));
+ nullptr, nullptr));
}
TEST_F(TestMockMirrorSnapshotUtils, CanCreateNonPrimarySnapshot) {