1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "include/stringify.h"
5 #include "test/librbd/test_mock_fixture.h"
6 #include "test/librbd/test_support.h"
7 #include "test/librbd/mock/MockImageCtx.h"
8 #include "test/librbd/mock/MockOperations.h"
9 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
10 #include "test/librados_test_stub/MockTestMemRadosClient.h"
11 #include "librbd/mirror/snapshot/CreatePrimaryRequest.h"
12 #include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
13 #include "librbd/mirror/snapshot/Utils.h"
19 struct MockTestImageCtx
: public MockImageCtx
{
20 explicit MockTestImageCtx(librbd::ImageCtx
& image_ctx
) : MockImageCtx(image_ctx
) {
24 } // anonymous namespace
33 static Mock
* s_instance
;
39 MOCK_METHOD4(can_create_primary_snapshot
,
40 bool(librbd::MockTestImageCtx
*, bool, bool, uint64_t *));
43 Mock
*Mock::s_instance
= nullptr;
45 } // anonymous namespace
47 template<> bool can_create_primary_snapshot(librbd::MockTestImageCtx
*image_ctx
,
48 bool demoted
, bool force
,
49 bool* requires_orphan
,
50 uint64_t *rollback_snap_id
) {
51 return Mock::s_instance
->can_create_primary_snapshot(image_ctx
, demoted
,
52 force
, rollback_snap_id
);
58 struct UnlinkPeerRequest
<MockTestImageCtx
> {
59 uint64_t snap_id
= CEPH_NOSNAP
;
60 std::string mirror_peer_uuid
;
61 Context
* on_finish
= nullptr;
62 static UnlinkPeerRequest
* s_instance
;
63 static UnlinkPeerRequest
*create(MockTestImageCtx
*image_ctx
,
65 const std::string
&mirror_peer_uuid
,
67 ceph_assert(s_instance
!= nullptr);
68 s_instance
->snap_id
= snap_id
;
69 s_instance
->mirror_peer_uuid
= mirror_peer_uuid
;
70 s_instance
->on_finish
= on_finish
;
74 MOCK_METHOD0(send
, void());
81 UnlinkPeerRequest
<MockTestImageCtx
>* UnlinkPeerRequest
<MockTestImageCtx
>::s_instance
= nullptr;
83 } // namespace snapshot
87 // template definitions
88 #include "librbd/mirror/snapshot/CreatePrimaryRequest.cc"
89 template class librbd::mirror::snapshot::CreatePrimaryRequest
<librbd::MockTestImageCtx
>;
96 using ::testing::DoAll
;
97 using ::testing::InSequence
;
98 using ::testing::Invoke
;
99 using ::testing::Return
;
100 using ::testing::StrEq
;
101 using ::testing::WithArg
;
103 class TestMockMirrorSnapshotCreatePrimaryRequest
: public TestMockFixture
{
105 typedef CreatePrimaryRequest
<MockTestImageCtx
> MockCreatePrimaryRequest
;
106 typedef UnlinkPeerRequest
<MockTestImageCtx
> MockUnlinkPeerRequest
;
107 typedef util::Mock MockUtils
;
109 uint64_t m_snap_seq
= 0;
111 void snap_create(MockTestImageCtx
&mock_image_ctx
,
112 const cls::rbd::SnapshotNamespace
&ns
,
113 const std::string
& snap_name
) {
114 ASSERT_TRUE(mock_image_ctx
.snap_info
.insert(
116 SnapInfo
{snap_name
, ns
, 0, {}, 0, 0, {}}}).second
);
119 void expect_clone_md_ctx(MockTestImageCtx
&mock_image_ctx
) {
120 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
), clone())
121 .WillOnce(Invoke([&mock_image_ctx
]() {
122 get_mock_io_ctx(mock_image_ctx
.md_ctx
).get();
123 return &get_mock_io_ctx(mock_image_ctx
.md_ctx
);
127 void expect_can_create_primary_snapshot(MockUtils
&mock_utils
, bool demoted
,
128 bool force
, bool result
) {
129 EXPECT_CALL(mock_utils
,
130 can_create_primary_snapshot(_
, demoted
, force
, nullptr))
131 .WillOnce(Return(result
));
134 void expect_get_mirror_peers(MockTestImageCtx
&mock_image_ctx
,
135 const std::vector
<cls::rbd::MirrorPeer
> &peers
,
141 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
142 exec(RBD_MIRRORING
, _
, StrEq("rbd"), StrEq("mirror_peer_list"),
144 .WillOnce(DoAll(WithArg
<5>(CopyInBufferlist(bl
)),
148 void expect_create_snapshot(MockTestImageCtx
&mock_image_ctx
, int r
) {
149 EXPECT_CALL(*mock_image_ctx
.operations
, snap_create(_
, _
, _
, _
, _
))
151 Invoke([this, &mock_image_ctx
, r
](
152 const cls::rbd::SnapshotNamespace
&ns
,
153 const std::string
& snap_name
,
155 ProgressContext
&prog_ctx
,
156 Context
*on_finish
) {
160 snap_create(mock_image_ctx
, ns
, snap_name
);
162 WithArg
<4>(CompleteContext(
163 r
, mock_image_ctx
.image_ctx
->op_work_queue
))
167 void expect_unlink_peer(MockTestImageCtx
&mock_image_ctx
,
168 MockUnlinkPeerRequest
&mock_unlink_peer_request
,
169 uint64_t snap_id
, const std::string
&peer_uuid
,
170 bool is_linked
, int r
) {
171 EXPECT_CALL(mock_unlink_peer_request
, send())
172 .WillOnce(Invoke([&mock_image_ctx
, &mock_unlink_peer_request
,
173 snap_id
, peer_uuid
, is_linked
, r
]() {
174 ASSERT_EQ(mock_unlink_peer_request
.mirror_peer_uuid
,
176 ASSERT_EQ(mock_unlink_peer_request
.snap_id
, snap_id
);
178 auto it
= mock_image_ctx
.snap_info
.find(snap_id
);
179 ASSERT_NE(it
, mock_image_ctx
.snap_info
.end());
181 boost::get
<cls::rbd::MirrorSnapshotNamespace
>(
182 &it
->second
.snap_namespace
);
183 ASSERT_NE(nullptr, info
);
184 ASSERT_EQ(is_linked
, info
->mirror_peer_uuids
.erase(
186 if (info
->mirror_peer_uuids
.empty()) {
187 mock_image_ctx
.snap_info
.erase(it
);
190 mock_image_ctx
.image_ctx
->op_work_queue
->queue(
191 mock_unlink_peer_request
.on_finish
, r
);
196 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, Success
) {
199 librbd::ImageCtx
*ictx
;
200 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
202 MockTestImageCtx
mock_image_ctx(*ictx
);
206 expect_clone_md_ctx(mock_image_ctx
);
207 MockUtils mock_utils
;
208 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
209 expect_get_mirror_peers(mock_image_ctx
,
210 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
211 "mirror", "mirror uuid"}}, 0);
212 expect_create_snapshot(mock_image_ctx
, 0);
215 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
216 0U, 0U, nullptr, &ctx
);
218 ASSERT_EQ(0, ctx
.wait());
221 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, CanNotError
) {
224 librbd::ImageCtx
*ictx
;
225 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
227 MockTestImageCtx
mock_image_ctx(*ictx
);
231 expect_clone_md_ctx(mock_image_ctx
);
232 MockUtils mock_utils
;
233 expect_can_create_primary_snapshot(mock_utils
, false, false, false);
236 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
237 0U, 0U, nullptr, &ctx
);
239 ASSERT_EQ(-EINVAL
, ctx
.wait());
242 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, GetMirrorPeersError
) {
245 librbd::ImageCtx
*ictx
;
246 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
248 MockTestImageCtx
mock_image_ctx(*ictx
);
252 expect_clone_md_ctx(mock_image_ctx
);
253 MockUtils mock_utils
;
254 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
255 expect_get_mirror_peers(mock_image_ctx
,
256 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
257 "mirror", "mirror uuid"}}, -EINVAL
);
260 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
261 0U, 0U, nullptr, &ctx
);
263 ASSERT_EQ(-EINVAL
, ctx
.wait());
266 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, CreateSnapshotError
) {
269 librbd::ImageCtx
*ictx
;
270 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
272 MockTestImageCtx
mock_image_ctx(*ictx
);
276 expect_clone_md_ctx(mock_image_ctx
);
277 MockUtils mock_utils
;
278 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
279 expect_get_mirror_peers(mock_image_ctx
,
280 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
281 "mirror", "mirror uuid"}}, 0);
282 expect_create_snapshot(mock_image_ctx
, -EINVAL
);
285 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
286 0U, 0U, nullptr, &ctx
);
288 ASSERT_EQ(-EINVAL
, ctx
.wait());
291 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, SuccessUnlinkPeer
) {
294 librbd::ImageCtx
*ictx
;
295 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
296 ictx
->config
.set_val("conf_rbd_mirroring_max_mirroring_snapshots", "3");
298 MockTestImageCtx
mock_image_ctx(*ictx
);
299 for (int i
= 0; i
< 3; i
++) {
300 cls::rbd::MirrorSnapshotNamespace ns
{
301 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"uuid"}, "", CEPH_NOSNAP
};
302 snap_create(mock_image_ctx
, ns
, "mirror_snap");
307 expect_clone_md_ctx(mock_image_ctx
);
308 MockUtils mock_utils
;
309 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
310 expect_get_mirror_peers(mock_image_ctx
,
311 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
312 "mirror", "mirror uuid"}}, 0);
313 expect_create_snapshot(mock_image_ctx
, 0);
314 MockUnlinkPeerRequest mock_unlink_peer_request
;
315 auto it
= mock_image_ctx
.snap_info
.rbegin();
316 auto snap_id
= it
->first
;
317 expect_unlink_peer(mock_image_ctx
, mock_unlink_peer_request
, snap_id
, "uuid",
320 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
321 0U, 0U, nullptr, &ctx
);
323 ASSERT_EQ(0, ctx
.wait());
326 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, SuccessUnlinkNoPeer
) {
329 librbd::ImageCtx
*ictx
;
330 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
331 ictx
->config
.set_val("conf_rbd_mirroring_max_mirroring_snapshots", "3");
333 MockTestImageCtx
mock_image_ctx(*ictx
);
334 cls::rbd::MirrorSnapshotNamespace ns
{
335 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {}, "", CEPH_NOSNAP
};
336 snap_create(mock_image_ctx
, ns
, "mirror_snap");
340 expect_clone_md_ctx(mock_image_ctx
);
341 MockUtils mock_utils
;
342 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
343 expect_get_mirror_peers(mock_image_ctx
,
344 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
345 "mirror", "mirror uuid"}}, 0);
346 expect_create_snapshot(mock_image_ctx
, 0);
347 MockUnlinkPeerRequest mock_unlink_peer_request
;
348 auto it
= mock_image_ctx
.snap_info
.rbegin();
349 auto snap_id
= it
->first
;
350 std::list
<string
> peer_uuids
= {"uuid"};
351 expect_unlink_peer(mock_image_ctx
, mock_unlink_peer_request
, snap_id
, "uuid",
355 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
356 0U, 0U, nullptr, &ctx
);
358 ASSERT_EQ(0, ctx
.wait());
361 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, SuccessUnlinkMultiplePeers
) {
364 librbd::ImageCtx
*ictx
;
365 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
366 ictx
->config
.set_val("conf_rbd_mirroring_max_mirroring_snapshots", "3");
368 MockTestImageCtx
mock_image_ctx(*ictx
);
369 for (int i
= 0; i
< 3; i
++) {
370 cls::rbd::MirrorSnapshotNamespace ns
{
371 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"uuid1", "uuid2"}, "",
373 snap_create(mock_image_ctx
, ns
, "mirror_snap");
378 expect_clone_md_ctx(mock_image_ctx
);
379 MockUtils mock_utils
;
380 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
381 expect_get_mirror_peers(mock_image_ctx
,
382 {{"uuid1", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
383 "mirror", "mirror uuid"},
384 {"uuid2", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
385 "mirror", "mirror uuid"}}, 0);
386 expect_create_snapshot(mock_image_ctx
, 0);
387 MockUnlinkPeerRequest mock_unlink_peer_request
;
388 auto it
= mock_image_ctx
.snap_info
.rbegin();
389 auto snap_id
= it
->first
;
390 expect_unlink_peer(mock_image_ctx
, mock_unlink_peer_request
, snap_id
, "uuid1",
392 expect_unlink_peer(mock_image_ctx
, mock_unlink_peer_request
, snap_id
, "uuid2",
395 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
396 0U, 0U, nullptr, &ctx
);
398 ASSERT_EQ(0, ctx
.wait());
401 } // namespace snapshot
402 } // namespace mirror
403 } // namespace librbd