#include "librbd/ImageState.h"
#include "librbd/Operations.h"
#include "librbd/Utils.h"
+#include "librbd/mirror/snapshot/WriteImageStateRequest.h"
#define dout_subsys ceph_subsys_rbd
return;
}
- if (m_snap_id != nullptr) {
+ write_image_state();
+}
+
+template <typename I>
+void CreateNonPrimaryRequest<I>::write_image_state() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << dendl;
+
+ uint64_t snap_id;
+ {
std::shared_lock image_locker{m_image_ctx->image_lock};
cls::rbd::MirrorNonPrimarySnapshotNamespace ns{m_primary_mirror_uuid,
m_primary_snap_id};
- *m_snap_id = m_image_ctx->get_snap_id(ns, m_snap_name);
+ snap_id = m_image_ctx->get_snap_id(ns, m_snap_name);
+ }
+
+ if (m_snap_id != nullptr) {
+ *m_snap_id = snap_id;
+ }
+
+ auto ctx = create_context_callback<
+ CreateNonPrimaryRequest<I>,
+ &CreateNonPrimaryRequest<I>::handle_write_image_state>(this);
+
+ auto req = WriteImageStateRequest<I>::create(m_image_ctx, snap_id,
+ m_image_state, ctx);
+ req->send();
+}
+
+template <typename I>
+void CreateNonPrimaryRequest<I>::handle_write_image_state(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to write image state: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
}
finish(0);
#include "include/buffer.h"
#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/mirror/snapshot/Types.h"
#include <string>
#include <set>
static CreateNonPrimaryRequest *create(ImageCtxT *image_ctx,
const std::string &primary_mirror_uuid,
uint64_t primary_snap_id,
+ const ImageState &image_state,
uint64_t *snap_id,
Context *on_finish) {
return new CreateNonPrimaryRequest(image_ctx, primary_mirror_uuid,
- primary_snap_id, snap_id, on_finish);
+ primary_snap_id, image_state, snap_id,
+ on_finish);
}
CreateNonPrimaryRequest(ImageCtxT *image_ctx,
const std::string &primary_mirror_uuid,
- uint64_t primary_snap_id, uint64_t *snap_id,
+ uint64_t primary_snap_id,
+ const ImageState &image_state, uint64_t *snap_id,
Context *on_finish)
: m_image_ctx(image_ctx), m_primary_mirror_uuid(primary_mirror_uuid),
- m_primary_snap_id(primary_snap_id), m_snap_id(snap_id),
- m_on_finish(on_finish) {
+ m_primary_snap_id(primary_snap_id), m_image_state(image_state),
+ m_snap_id(snap_id), m_on_finish(on_finish) {
}
void send();
* CREATE_SNAPSHOT
* |
* v
+ * WRITE_IMAGE_STATE
+ * |
+ * v
* <finish>
*
* @endverbatim
ImageCtxT *m_image_ctx;
std::string m_primary_mirror_uuid;
uint64_t m_primary_snap_id;
+ ImageState m_image_state;
uint64_t *m_snap_id;
Context *m_on_finish;
void create_snapshot();
void handle_create_snapshot(int r);
+ void write_image_state();
+ void handle_write_image_state(int r);
+
void finish(int r);
bool validate_snapshot();
#include "librbd/ObjectMap.h"
#include "librbd/Utils.h"
#include "librbd/io/ImageRequestWQ.h"
+#include "librbd/mirror/snapshot/SetImageStateRequest.h"
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
if (image_ctx.object_map == nullptr || m_skip_object_map) {
image_ctx.image_lock.unlock_shared();
- update_snap_context();
- image_ctx.io_work_queue->unblock_writes();
- return this->create_context_finisher(0);
+ return send_create_image_state();
}
CephContext *cct = image_ctx.cct;
CephContext *cct = image_ctx.cct;
ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl;
+ if (*result < 0) {
+ lderr(cct) << this << " " << __func__ << ": failed to snapshot object map: "
+ << cpp_strerror(*result) << dendl;
+
+ update_snap_context();
+ image_ctx.io_work_queue->unblock_writes();
+ return this->create_context_finisher(*result);
+ }
+
+ return send_create_image_state();
+}
+
+template <typename I>
+Context *SnapshotCreateRequest<I>::send_create_image_state() {
+ I &image_ctx = this->m_image_ctx;
+ auto type = cls::rbd::get_snap_namespace_type(m_snap_namespace);
+
+ if (type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY) {
+ update_snap_context();
+ image_ctx.io_work_queue->unblock_writes();
+ return this->create_context_finisher(0);
+ }
+
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << this << " " << __func__ << dendl;
+
+ auto req = mirror::snapshot::SetImageStateRequest<I>::create(
+ &image_ctx, m_snap_id, create_context_callback<
+ SnapshotCreateRequest<I>,
+ &SnapshotCreateRequest<I>::handle_create_image_state>(this));
+ req->send();
+ return nullptr;
+}
+
+template <typename I>
+Context *SnapshotCreateRequest<I>::handle_create_image_state(int *result) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl;
+
update_snap_context();
image_ctx.io_work_queue->unblock_writes();
if (*result < 0) {
* . . . > STATE_ALLOCATE_SNAP_ID *
* . | *
* . v *
- * . . . . STATE_CREATE_SNAP * * * * * * * * * * *
- * | * *
- * v * *
- * STATE_CREATE_OBJECT_MAP (skip if * *
- * | disabled) * *
- * | * *
- * | v *
+ * . . . . STATE_CREATE_SNAP * * * * * * * * * * * *
+ * | * *
+ * v * *
+ * STATE_CREATE_OBJECT_MAP (skip if * *
+ * | disabled) * *
+ * v * *
+ * STATE_CREATE_IMAGE_STATE (skip if * *
+ * | not mirror * *
+ * | snapshot) * *
+ * | v *
* | STATE_RELEASE_SNAP_ID *
* | | *
* | v *
Context *send_create_object_map();
Context *handle_create_object_map(int *result);
+ Context *send_create_image_state();
+ Context *handle_create_image_state(int *result);
+
void send_release_snap_id();
Context *handle_release_snap_id(int *result);
#include "librbd/ObjectMap.h"
#include "librbd/Utils.h"
#include "librbd/image/DetachChildRequest.h"
+#include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
}
// object map disabled
- release_snap_id();
+ remove_image_state();
}
template <typename I>
return;
}
+ remove_image_state();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::remove_image_state() {
+ I &image_ctx = this->m_image_ctx;
+ auto type = cls::rbd::get_snap_namespace_type(m_snap_namespace);
+
+ if (type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY &&
+ type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY) {
+ release_snap_id();
+ return;
+ }
+
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << dendl;
+
+ auto ctx = create_context_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_remove_image_state>(this);
+ auto req = mirror::snapshot::RemoveImageStateRequest<I>::create(
+ &image_ctx, m_snap_id, ctx);
+ req->send();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_remove_image_state(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to remove image state: " << cpp_strerror(r)
+ << dendl;
+ this->complete(r);
+ return;
+ }
+
release_snap_id();
}
* v (skip if disabled/in-use)
* REMOVE_OBJECT_MAP
* |
+ * v (skip if not mirror snpashot)
+ * REMOVE_IMAGE_STATE
+ * |
* v (skip if in-use)
* RELEASE_SNAP_ID
* |
void remove_object_map();
void handle_remove_object_map(int r);
+ void remove_image_state();
+ void handle_remove_image_state(int r);
+
void release_snap_id();
void handle_release_snap_id(int r);
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "test/librados_test_stub/MockTestMemRadosClient.h"
#include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h"
+#include "librbd/mirror/snapshot/WriteImageStateRequest.h"
namespace librbd {
};
} // anonymous namespace
+
+namespace mirror {
+namespace snapshot {
+template <>
+struct WriteImageStateRequest<MockTestImageCtx> {
+ uint64_t snap_id = CEPH_NOSNAP;
+ ImageState image_state;
+ Context* on_finish = nullptr;
+ static WriteImageStateRequest* s_instance;
+ static WriteImageStateRequest *create(MockTestImageCtx *image_ctx,
+ uint64_t snap_id,
+ const ImageState &image_state,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->snap_id = snap_id;
+ s_instance->image_state = image_state;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ WriteImageStateRequest() {
+ s_instance = this;
+ }
+};
+
+WriteImageStateRequest<MockTestImageCtx>* WriteImageStateRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace mirror
} // namespace librbd
// template definitions
using ::testing::_;
using ::testing::DoAll;
using ::testing::InSequence;
+using ::testing::Invoke;
using ::testing::Return;
using ::testing::StrEq;
using ::testing::WithArg;
class TestMockMirrorSnapshotCreateNonPrimaryRequest : public TestMockFixture {
public:
typedef CreateNonPrimaryRequest<MockTestImageCtx> MockCreateNonPrimaryRequest;
+ typedef WriteImageStateRequest<MockTestImageCtx> MockWriteImageStateRequest;
uint64_t m_snap_seq = 0;
.WillOnce(WithArg<2>(CompleteContext(
r, mock_image_ctx.image_ctx->op_work_queue)));
}
+
+ void expect_write_image_state(
+ MockTestImageCtx &mock_image_ctx,
+ MockWriteImageStateRequest &mock_write_image_state_request, int r) {
+ EXPECT_CALL(mock_image_ctx, get_snap_id(_, _))
+ .WillOnce(Return(123));
+ EXPECT_CALL(mock_write_image_state_request, send())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_write_image_state_request, r]() {
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ mock_write_image_state_request.on_finish, r);
+ }));
+ }
};
TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, Success) {
mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
expect_create_snapshot(mock_image_ctx, 0);
+ MockWriteImageStateRequest mock_write_image_state_request;
+ expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0);
C_SaferCond ctx;
auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
- 123, nullptr, &ctx);
+ 123, {}, nullptr, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
}
C_SaferCond ctx;
auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
- 123, nullptr, &ctx);
+ 123, {}, nullptr, &ctx);
req->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
C_SaferCond ctx;
auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
- 123, nullptr, &ctx);
+ 123, {}, nullptr, &ctx);
req->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
C_SaferCond ctx;
auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
- 123, nullptr, &ctx);
+ 123, {}, nullptr, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, WriteImageStateError) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ InSequence seq;
+
+ expect_refresh_image(mock_image_ctx, true, 0);
+ expect_get_mirror_image(
+ mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
+ expect_create_snapshot(mock_image_ctx, 0);
+ MockWriteImageStateRequest mock_write_image_state_request;
+ expect_write_image_state(mock_image_ctx, mock_write_image_state_request,
+ -EINVAL);
+
+ C_SaferCond ctx;
+ auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
+ 123, {}, nullptr, &ctx);
req->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
C_SaferCond ctx;
auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
- 123, nullptr, &ctx);
+ 123, {}, nullptr, &ctx);
req->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
expect_create_snapshot(mock_image_ctx, 0);
+ MockWriteImageStateRequest mock_write_image_state_request;
+ expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0);
C_SaferCond ctx;
auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
- 123, nullptr, &ctx);
+ 123, {}, nullptr, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
}
C_SaferCond ctx;
auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
- 123, nullptr, &ctx);
+ 123, {}, nullptr, &ctx);
req->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
expect_create_snapshot(mock_image_ctx, 0);
+ MockWriteImageStateRequest mock_write_image_state_request;
+ expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0);
C_SaferCond ctx;
auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
- 123, nullptr, &ctx);
+ 123, {}, nullptr, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
}
#include "common/bit_vector.hpp"
#include "librbd/internal.h"
#include "librbd/ObjectMap.h"
+#include "librbd/mirror/snapshot/SetImageStateRequest.h"
#include "librbd/operation/SnapshotCreateRequest.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+template<>
+class SetImageStateRequest<MockImageCtx> {
+public:
+ static SetImageStateRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static SetImageStateRequest *create(MockImageCtx *image_ctx, uint64_t snap_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ SetImageStateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+SetImageStateRequest<MockImageCtx> *SetImageStateRequest<MockImageCtx>::s_instance;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
// template definitions
#include "librbd/operation/SnapshotCreateRequest.cc"
class TestMockOperationSnapshotCreateRequest : public TestMockFixture {
public:
typedef SnapshotCreateRequest<MockImageCtx> MockSnapshotCreateRequest;
+ typedef mirror::snapshot::SetImageStateRequest<MockImageCtx> MockSetImageStateRequest;
void expect_block_writes(MockImageCtx &mock_image_ctx) {
EXPECT_CALL(*mock_image_ctx.io_work_queue, block_writes(_))
}
}
+ void expect_set_image_state(
+ MockImageCtx &mock_image_ctx,
+ MockSetImageStateRequest &mock_set_image_state_request, int r) {
+ EXPECT_CALL(mock_set_image_state_request, send())
+ .WillOnce(FinishRequest(&mock_set_image_state_request, r,
+ &mock_image_ctx));
+ }
+
void expect_update_snap_context(MockImageCtx &mock_image_ctx) {
// state machine checks to ensure a refresh hasn't already added the snap
EXPECT_CALL(mock_image_ctx, get_snap_info(_))
EXPECT_CALL(*mock_image_ctx.io_work_queue, unblock_writes())
.Times(1);
}
-
};
TEST_F(TestMockOperationSnapshotCreateRequest, Success) {
ASSERT_EQ(0, cond_ctx.wait());
}
+TEST_F(TestMockOperationSnapshotCreateRequest, SetImageState) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_verify_lock_ownership(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_block_writes(mock_image_ctx);
+ expect_allocate_snap_id(mock_image_ctx, 0);
+ expect_snap_create(mock_image_ctx, 0);
+ expect_object_map_snap_create(mock_image_ctx);
+ MockSetImageStateRequest mock_set_image_state_request;
+ expect_set_image_state(mock_image_ctx, mock_set_image_state_request, 0);
+ expect_update_snap_context(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::MirrorPrimarySnapshotNamespace(),
+ "snap1", 0, false);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
} // namespace operation
} // namespace librbd
#include "librbd/internal.h"
#include "librbd/Operations.h"
#include "librbd/image/DetachChildRequest.h"
+#include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
#include "librbd/operation/SnapshotRemoveRequest.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-// template definitions
-#include "librbd/operation/SnapshotRemoveRequest.cc"
-
namespace librbd {
namespace image {
} // namespace image
+namespace mirror {
+namespace snapshot {
+
+template<>
+class RemoveImageStateRequest<MockImageCtx> {
+public:
+ static RemoveImageStateRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static RemoveImageStateRequest *create(MockImageCtx *image_ctx,
+ uint64_t snap_id,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ RemoveImageStateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+RemoveImageStateRequest<MockImageCtx> *RemoveImageStateRequest<MockImageCtx>::s_instance;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/operation/SnapshotRemoveRequest.cc"
+
+namespace librbd {
namespace operation {
using ::testing::_;
public:
typedef SnapshotRemoveRequest<MockImageCtx> MockSnapshotRemoveRequest;
typedef image::DetachChildRequest<MockImageCtx> MockDetachChildRequest;
+ typedef mirror::snapshot::RemoveImageStateRequest<MockImageCtx> MockRemoveImageStateRequest;
int create_snapshot(const char *snap_name) {
librbd::ImageCtx *ictx;
}
}
+ void expect_remove_image_state(
+ MockImageCtx &mock_image_ctx,
+ MockRemoveImageStateRequest &mock_remove_image_state_request, int r) {
+ EXPECT_CALL(mock_remove_image_state_request, send())
+ .WillOnce(FinishRequest(&mock_remove_image_state_request, r,
+ &mock_image_ctx));
+ }
+
void expect_get_parent_spec(MockImageCtx &mock_image_ctx, int r) {
if (mock_image_ctx.old_format) {
return;
ASSERT_EQ(-EBUSY, cond_ctx.wait());
}
+TEST_F(TestMockOperationSnapshotRemoveRequest, MirrorSnapshot) {
+ REQUIRE_FORMAT_V2();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ expect_op_work_queue(mock_image_ctx);
+
+ ::testing::InSequence seq;
+ expect_snapshot_trash_add(mock_image_ctx, 0);
+
+ uint64_t snap_id = ictx->snap_info.rbegin()->first;
+ expect_snapshot_get(mock_image_ctx,
+ {snap_id, {cls::rbd::MirrorPrimarySnapshotNamespace{}},
+ "mirror", 123, {}, 0}, 0);
+
+ expect_get_parent_spec(mock_image_ctx, 0);
+ expect_object_map_snap_remove(mock_image_ctx, 0);
+ MockRemoveImageStateRequest mock_remove_image_state_request;
+ expect_remove_image_state(mock_image_ctx, mock_remove_image_state_request, 0);
+ expect_release_snap_id(mock_image_ctx);
+ expect_snap_remove(mock_image_ctx, 0);
+ expect_rm_snap(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+ mock_image_ctx, &cond_ctx, cls::rbd::MirrorPrimarySnapshotNamespace(),
+ "mirror", snap_id);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+}
+
TEST_F(TestMockOperationSnapshotRemoveRequest, SnapshotTrashAddNotSupported) {
REQUIRE_FORMAT_V2();