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
,
154 Context
*on_finish
) {
158 snap_create(mock_image_ctx
, ns
, snap_name
);
160 WithArg
<2>(CompleteContext(
161 r
, mock_image_ctx
.image_ctx
->op_work_queue
))
165 void expect_unlink_peer(MockTestImageCtx
&mock_image_ctx
,
166 MockUnlinkPeerRequest
&mock_unlink_peer_request
,
167 uint64_t snap_id
, const std::string
&peer_uuid
,
168 bool is_linked
, int r
) {
169 EXPECT_CALL(mock_unlink_peer_request
, send())
170 .WillOnce(Invoke([&mock_image_ctx
, &mock_unlink_peer_request
,
171 snap_id
, peer_uuid
, is_linked
, r
]() {
172 ASSERT_EQ(mock_unlink_peer_request
.mirror_peer_uuid
,
174 ASSERT_EQ(mock_unlink_peer_request
.snap_id
, snap_id
);
176 auto it
= mock_image_ctx
.snap_info
.find(snap_id
);
177 ASSERT_NE(it
, mock_image_ctx
.snap_info
.end());
179 boost::get
<cls::rbd::MirrorSnapshotNamespace
>(
180 &it
->second
.snap_namespace
);
181 ASSERT_NE(nullptr, info
);
182 ASSERT_EQ(is_linked
, info
->mirror_peer_uuids
.erase(
184 if (info
->mirror_peer_uuids
.empty()) {
185 mock_image_ctx
.snap_info
.erase(it
);
188 mock_image_ctx
.image_ctx
->op_work_queue
->queue(
189 mock_unlink_peer_request
.on_finish
, r
);
194 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, Success
) {
197 librbd::ImageCtx
*ictx
;
198 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
200 MockTestImageCtx
mock_image_ctx(*ictx
);
204 expect_clone_md_ctx(mock_image_ctx
);
205 MockUtils mock_utils
;
206 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
207 expect_get_mirror_peers(mock_image_ctx
,
208 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
209 "mirror", "mirror uuid"}}, 0);
210 expect_create_snapshot(mock_image_ctx
, 0);
213 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
216 ASSERT_EQ(0, ctx
.wait());
219 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, CanNotError
) {
222 librbd::ImageCtx
*ictx
;
223 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
225 MockTestImageCtx
mock_image_ctx(*ictx
);
229 expect_clone_md_ctx(mock_image_ctx
);
230 MockUtils mock_utils
;
231 expect_can_create_primary_snapshot(mock_utils
, false, false, false);
234 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
237 ASSERT_EQ(-EINVAL
, ctx
.wait());
240 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, GetMirrorPeersError
) {
243 librbd::ImageCtx
*ictx
;
244 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
246 MockTestImageCtx
mock_image_ctx(*ictx
);
250 expect_clone_md_ctx(mock_image_ctx
);
251 MockUtils mock_utils
;
252 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
253 expect_get_mirror_peers(mock_image_ctx
,
254 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
255 "mirror", "mirror uuid"}}, -EINVAL
);
258 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
261 ASSERT_EQ(-EINVAL
, ctx
.wait());
264 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, CreateSnapshotError
) {
267 librbd::ImageCtx
*ictx
;
268 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
270 MockTestImageCtx
mock_image_ctx(*ictx
);
274 expect_clone_md_ctx(mock_image_ctx
);
275 MockUtils mock_utils
;
276 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
277 expect_get_mirror_peers(mock_image_ctx
,
278 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
279 "mirror", "mirror uuid"}}, 0);
280 expect_create_snapshot(mock_image_ctx
, -EINVAL
);
283 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
286 ASSERT_EQ(-EINVAL
, ctx
.wait());
289 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, SuccessUnlinkPeer
) {
292 librbd::ImageCtx
*ictx
;
293 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
294 ictx
->config
.set_val("rbd_mirroring_max_mirroring_snapshots", "3");
296 MockTestImageCtx
mock_image_ctx(*ictx
);
297 for (int i
= 0; i
< 3; i
++) {
298 cls::rbd::MirrorSnapshotNamespace ns
{
299 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"uuid"}, "", CEPH_NOSNAP
};
300 snap_create(mock_image_ctx
, ns
, "mirror_snap");
305 expect_clone_md_ctx(mock_image_ctx
);
306 MockUtils mock_utils
;
307 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
308 expect_get_mirror_peers(mock_image_ctx
,
309 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
310 "mirror", "mirror uuid"}}, 0);
311 expect_create_snapshot(mock_image_ctx
, 0);
312 MockUnlinkPeerRequest mock_unlink_peer_request
;
313 auto it
= mock_image_ctx
.snap_info
.rbegin();
314 auto snap_id
= it
->first
;
315 expect_unlink_peer(mock_image_ctx
, mock_unlink_peer_request
, snap_id
, "uuid",
318 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
321 ASSERT_EQ(0, ctx
.wait());
324 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, SuccessUnlinkNoPeer
) {
327 librbd::ImageCtx
*ictx
;
328 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
329 ictx
->config
.set_val("rbd_mirroring_max_mirroring_snapshots", "3");
331 MockTestImageCtx
mock_image_ctx(*ictx
);
332 cls::rbd::MirrorSnapshotNamespace ns
{
333 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {}, "", CEPH_NOSNAP
};
334 snap_create(mock_image_ctx
, ns
, "mirror_snap");
338 expect_clone_md_ctx(mock_image_ctx
);
339 MockUtils mock_utils
;
340 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
341 expect_get_mirror_peers(mock_image_ctx
,
342 {{"uuid", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
343 "mirror", "mirror uuid"}}, 0);
344 expect_create_snapshot(mock_image_ctx
, 0);
345 MockUnlinkPeerRequest mock_unlink_peer_request
;
346 auto it
= mock_image_ctx
.snap_info
.rbegin();
347 auto snap_id
= it
->first
;
348 std::list
<string
> peer_uuids
= {"uuid"};
349 expect_unlink_peer(mock_image_ctx
, mock_unlink_peer_request
, snap_id
, "uuid",
353 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
356 ASSERT_EQ(0, ctx
.wait());
359 TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest
, SuccessUnlinkMultiplePeers
) {
362 librbd::ImageCtx
*ictx
;
363 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
364 ictx
->config
.set_val("rbd_mirroring_max_mirroring_snapshots", "3");
366 MockTestImageCtx
mock_image_ctx(*ictx
);
367 for (int i
= 0; i
< 3; i
++) {
368 cls::rbd::MirrorSnapshotNamespace ns
{
369 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"uuid1", "uuid2"}, "",
371 snap_create(mock_image_ctx
, ns
, "mirror_snap");
376 expect_clone_md_ctx(mock_image_ctx
);
377 MockUtils mock_utils
;
378 expect_can_create_primary_snapshot(mock_utils
, false, false, true);
379 expect_get_mirror_peers(mock_image_ctx
,
380 {{"uuid1", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
381 "mirror", "mirror uuid"},
382 {"uuid2", cls::rbd::MIRROR_PEER_DIRECTION_TX
, "ceph",
383 "mirror", "mirror uuid"}}, 0);
384 expect_create_snapshot(mock_image_ctx
, 0);
385 MockUnlinkPeerRequest mock_unlink_peer_request
;
386 auto it
= mock_image_ctx
.snap_info
.rbegin();
387 auto snap_id
= it
->first
;
388 expect_unlink_peer(mock_image_ctx
, mock_unlink_peer_request
, snap_id
, "uuid1",
390 expect_unlink_peer(mock_image_ctx
, mock_unlink_peer_request
, snap_id
, "uuid2",
393 auto req
= new MockCreatePrimaryRequest(&mock_image_ctx
, "gid", CEPH_NOSNAP
,
396 ASSERT_EQ(0, ctx
.wait());
399 } // namespace snapshot
400 } // namespace mirror
401 } // namespace librbd