1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/librbd/test_mock_fixture.h"
5 #include "test/librbd/test_support.h"
6 #include "test/librbd/mock/MockImageCtx.h"
7 #include "test/librbd/mock/MockOperations.h"
8 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
9 #include "test/librados_test_stub/MockTestMemRadosClient.h"
10 #include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
16 struct MockTestImageCtx
: public MockImageCtx
{
17 explicit MockTestImageCtx(librbd::ImageCtx
& image_ctx
) : MockImageCtx(image_ctx
) {
21 } // anonymous namespace
24 // template definitions
25 #include "librbd/mirror/snapshot/UnlinkPeerRequest.cc"
26 template class librbd::mirror::snapshot::UnlinkPeerRequest
<librbd::MockTestImageCtx
>;
33 using ::testing::DoAll
;
34 using ::testing::InSequence
;
35 using ::testing::Invoke
;
36 using ::testing::Return
;
37 using ::testing::StrEq
;
39 class TestMockMirrorSnapshotUnlinkPeerRequest
: public TestMockFixture
{
41 typedef UnlinkPeerRequest
<MockTestImageCtx
> MockUnlinkPeerRequest
;
43 uint64_t m_snap_seq
= 0;
45 uint64_t snap_create(MockTestImageCtx
&mock_image_ctx
,
46 const cls::rbd::SnapshotNamespace
&ns
,
47 const std::string
& snap_name
) {
48 EXPECT_TRUE(mock_image_ctx
.snap_info
.insert(
50 SnapInfo
{snap_name
, ns
, 0, {}, 0, 0, {}}}).second
);
54 void expect_get_snap_info(MockTestImageCtx
&mock_image_ctx
,
55 librados::snap_t snap_id
) {
56 EXPECT_CALL(mock_image_ctx
, get_snap_info(snap_id
))
57 .WillRepeatedly(Invoke([&mock_image_ctx
](
58 librados::snap_t snap_id
) -> librbd::SnapInfo
* {
59 auto it
= mock_image_ctx
.snap_info
.find(snap_id
);
60 if (it
== mock_image_ctx
.snap_info
.end()) {
67 void expect_is_refresh_required(MockTestImageCtx
&mock_image_ctx
,
68 bool refresh_required
) {
69 EXPECT_CALL(*mock_image_ctx
.state
, is_refresh_required())
70 .WillOnce(Return(refresh_required
));
73 void expect_refresh_image(MockTestImageCtx
&mock_image_ctx
, int r
) {
74 EXPECT_CALL(*mock_image_ctx
.state
, refresh(_
))
75 .WillOnce(CompleteContext(r
, mock_image_ctx
.image_ctx
->op_work_queue
));
78 void expect_unlink_peer(MockTestImageCtx
&mock_image_ctx
, uint64_t snap_id
,
79 const std::string
&peer_uuid
, int r
) {
82 encode(snapid_t
{snap_id
}, bl
);
83 encode(peer_uuid
, bl
);
85 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
86 exec(mock_image_ctx
.header_oid
, _
, StrEq("rbd"),
87 StrEq("mirror_image_snapshot_unlink_peer"),
88 ContentsEqual(bl
), _
, _
, _
))
89 .WillOnce(Invoke([&mock_image_ctx
, snap_id
, peer_uuid
, r
](auto&&... args
) -> int {
91 auto it
= mock_image_ctx
.snap_info
.find(snap_id
);
92 EXPECT_NE(it
, mock_image_ctx
.snap_info
.end());
94 std::get_if
<cls::rbd::MirrorSnapshotNamespace
>(
95 &it
->second
.snap_namespace
);
96 EXPECT_NE(nullptr, info
);
97 EXPECT_NE(0, info
->mirror_peer_uuids
.erase(
104 void expect_notify_update(MockTestImageCtx
&mock_image_ctx
, int r
) {
105 EXPECT_CALL(mock_image_ctx
, notify_update(_
))
106 .WillOnce(CompleteContext(r
, mock_image_ctx
.image_ctx
->op_work_queue
));
109 void expect_remove_snapshot(MockTestImageCtx
&mock_image_ctx
,
110 uint64_t snap_id
, int r
) {
111 EXPECT_CALL(*mock_image_ctx
.operations
, snap_remove(_
, _
, _
))
112 .WillOnce(Invoke([&mock_image_ctx
, snap_id
, r
](
113 const cls::rbd::SnapshotNamespace
&snap_namespace
,
114 const std::string
&snap_name
, Context
*on_finish
) {
116 auto it
= mock_image_ctx
.snap_info
.find(snap_id
);
117 EXPECT_NE(it
, mock_image_ctx
.snap_info
.end());
118 EXPECT_EQ(it
->second
.snap_namespace
, snap_namespace
);
119 EXPECT_EQ(it
->second
.name
, snap_name
);
120 mock_image_ctx
.snap_info
.erase(it
);
122 mock_image_ctx
.image_ctx
->op_work_queue
->queue(
128 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, Success
) {
131 librbd::ImageCtx
*ictx
;
132 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
134 MockTestImageCtx
mock_image_ctx(*ictx
);
135 cls::rbd::MirrorSnapshotNamespace ns
{
136 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"peer1_uuid", "peer2_uuid"},
138 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
140 expect_get_snap_info(mock_image_ctx
, snap_id
);
144 expect_is_refresh_required(mock_image_ctx
, true);
145 expect_refresh_image(mock_image_ctx
, 0);
146 expect_unlink_peer(mock_image_ctx
, snap_id
, "peer1_uuid", 0);
147 expect_notify_update(mock_image_ctx
, 0);
148 expect_refresh_image(mock_image_ctx
, 0);
151 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer1_uuid",
154 ASSERT_EQ(0, ctx
.wait());
157 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, RemoveSnapshot
) {
160 librbd::ImageCtx
*ictx
;
161 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
163 MockTestImageCtx
mock_image_ctx(*ictx
);
164 cls::rbd::MirrorSnapshotNamespace ns
{
165 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"peer_uuid"},
167 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
168 snap_create(mock_image_ctx
, ns
, "mirror_snap2");
170 expect_get_snap_info(mock_image_ctx
, snap_id
);
174 expect_is_refresh_required(mock_image_ctx
, true);
175 expect_refresh_image(mock_image_ctx
, 0);
176 expect_remove_snapshot(mock_image_ctx
, snap_id
, 0);
179 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer_uuid",
182 ASSERT_EQ(0, ctx
.wait());
185 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, RemoveSnapshotNotAllowed
) {
188 librbd::ImageCtx
*ictx
;
189 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
191 MockTestImageCtx
mock_image_ctx(*ictx
);
192 cls::rbd::MirrorSnapshotNamespace ns
{
193 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"peer_uuid"},
195 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
196 snap_create(mock_image_ctx
, ns
, "mirror_snap2");
198 expect_get_snap_info(mock_image_ctx
, snap_id
);
202 expect_is_refresh_required(mock_image_ctx
, true);
203 expect_refresh_image(mock_image_ctx
, 0);
204 expect_unlink_peer(mock_image_ctx
, snap_id
, "peer_uuid", 0);
205 expect_notify_update(mock_image_ctx
, 0);
206 expect_refresh_image(mock_image_ctx
, 0);
209 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer_uuid",
212 ASSERT_EQ(0, ctx
.wait());
215 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, SnapshotRemoveEmptyPeers
) {
218 librbd::ImageCtx
*ictx
;
219 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
221 MockTestImageCtx
mock_image_ctx(*ictx
);
222 cls::rbd::MirrorSnapshotNamespace ns
{
223 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {},
225 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
226 ns
.mirror_peer_uuids
= {"peer_uuid"};
227 snap_create(mock_image_ctx
, ns
, "mirror_snap2");
229 expect_get_snap_info(mock_image_ctx
, snap_id
);
233 expect_is_refresh_required(mock_image_ctx
, true);
234 expect_refresh_image(mock_image_ctx
, 0);
235 expect_remove_snapshot(mock_image_ctx
, snap_id
, 0);
238 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer_uuid",
241 ASSERT_EQ(0, ctx
.wait());
244 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, SnapshotRemoveEmptyPeersNotAllowed
) {
247 librbd::ImageCtx
*ictx
;
248 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
250 MockTestImageCtx
mock_image_ctx(*ictx
);
251 cls::rbd::MirrorSnapshotNamespace ns
{
252 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {},
254 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
255 ns
.mirror_peer_uuids
= {"peer_uuid"};
256 snap_create(mock_image_ctx
, ns
, "mirror_snap2");
258 expect_get_snap_info(mock_image_ctx
, snap_id
);
262 expect_is_refresh_required(mock_image_ctx
, true);
263 expect_refresh_image(mock_image_ctx
, 0);
266 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer_uuid",
269 ASSERT_EQ(0, ctx
.wait());
272 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, SnapshotDNE
) {
275 librbd::ImageCtx
*ictx
;
276 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
278 MockTestImageCtx
mock_image_ctx(*ictx
);
280 expect_get_snap_info(mock_image_ctx
, 123);
284 expect_is_refresh_required(mock_image_ctx
, true);
285 expect_refresh_image(mock_image_ctx
, 0);
288 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, 123, "peer_uuid",
291 ASSERT_EQ(-ENOENT
, ctx
.wait());
294 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, PeerDNE
) {
297 librbd::ImageCtx
*ictx
;
298 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
300 MockTestImageCtx
mock_image_ctx(*ictx
);
301 cls::rbd::MirrorSnapshotNamespace ns
{
302 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"peer_uuid"},
304 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
306 expect_get_snap_info(mock_image_ctx
, snap_id
);
310 expect_is_refresh_required(mock_image_ctx
, true);
311 expect_refresh_image(mock_image_ctx
, 0);
314 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "unknown_peer",
317 ASSERT_EQ(0, ctx
.wait());
320 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, MultiPeerPeerDNE
) {
323 librbd::ImageCtx
*ictx
;
324 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
326 MockTestImageCtx
mock_image_ctx(*ictx
);
327 cls::rbd::MirrorSnapshotNamespace ns
{
328 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"peer1_uuid", "peer2_uuid"},
330 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
332 expect_get_snap_info(mock_image_ctx
, snap_id
);
336 expect_is_refresh_required(mock_image_ctx
, true);
337 expect_refresh_image(mock_image_ctx
, 0);
340 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "unknown_peer",
343 ASSERT_EQ(0, ctx
.wait());
346 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, InvalidSnapshot
) {
349 librbd::ImageCtx
*ictx
;
350 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
352 MockTestImageCtx
mock_image_ctx(*ictx
);
353 cls::rbd::UserSnapshotNamespace ns
;
354 auto snap_id
= snap_create(mock_image_ctx
, ns
, "user_snap");
356 expect_get_snap_info(mock_image_ctx
, snap_id
);
360 expect_is_refresh_required(mock_image_ctx
, false);
363 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer_uuid",
366 ASSERT_EQ(-EINVAL
, ctx
.wait());
369 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, RefreshError
) {
372 librbd::ImageCtx
*ictx
;
373 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
375 MockTestImageCtx
mock_image_ctx(*ictx
);
379 expect_is_refresh_required(mock_image_ctx
, true);
380 expect_refresh_image(mock_image_ctx
, -EINVAL
);
383 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, 123, "peer_uuid",
386 ASSERT_EQ(-EINVAL
, ctx
.wait());
389 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, UnlinkError
) {
392 librbd::ImageCtx
*ictx
;
393 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
395 MockTestImageCtx
mock_image_ctx(*ictx
);
396 cls::rbd::MirrorSnapshotNamespace ns
{
397 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"peer1_uuid", "peer2_uuid"},
399 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
401 expect_get_snap_info(mock_image_ctx
, snap_id
);
405 expect_is_refresh_required(mock_image_ctx
, false);
406 expect_unlink_peer(mock_image_ctx
, snap_id
, "peer1_uuid", -EINVAL
);
409 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer1_uuid",
412 ASSERT_EQ(-EINVAL
, ctx
.wait());
415 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, UnlinkErrorRestart
) {
418 librbd::ImageCtx
*ictx
;
419 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
421 MockTestImageCtx
mock_image_ctx(*ictx
);
422 cls::rbd::MirrorSnapshotNamespace ns
{
423 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"peer_uuid"},
425 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
426 snap_create(mock_image_ctx
, ns
, "mirror_snap2");
428 expect_get_snap_info(mock_image_ctx
, snap_id
);
432 expect_is_refresh_required(mock_image_ctx
, true);
433 expect_refresh_image(mock_image_ctx
, 0);
434 expect_unlink_peer(mock_image_ctx
, snap_id
, "peer_uuid", -ERESTART
);
435 expect_refresh_image(mock_image_ctx
, 0);
436 expect_remove_snapshot(mock_image_ctx
, snap_id
, 0);
439 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer_uuid",
442 ASSERT_EQ(0, ctx
.wait());
445 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, NotifyError
) {
448 librbd::ImageCtx
*ictx
;
449 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
451 MockTestImageCtx
mock_image_ctx(*ictx
);
452 cls::rbd::MirrorSnapshotNamespace ns
{
453 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"peer1_uuid", "peer2_uuid"},
455 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
457 expect_get_snap_info(mock_image_ctx
, snap_id
);
461 expect_is_refresh_required(mock_image_ctx
, false);
462 expect_unlink_peer(mock_image_ctx
, snap_id
, "peer1_uuid", 0);
463 expect_notify_update(mock_image_ctx
, -EINVAL
);
466 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer1_uuid",
469 ASSERT_EQ(-EINVAL
, ctx
.wait());
472 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest
, RemoveSnapshotError
) {
475 librbd::ImageCtx
*ictx
;
476 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
478 MockTestImageCtx
mock_image_ctx(*ictx
);
479 cls::rbd::MirrorSnapshotNamespace ns
{
480 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY
, {"peer_uuid"},
482 auto snap_id
= snap_create(mock_image_ctx
, ns
, "mirror_snap");
483 snap_create(mock_image_ctx
, ns
, "mirror_snap2");
485 expect_get_snap_info(mock_image_ctx
, snap_id
);
489 expect_is_refresh_required(mock_image_ctx
, false);
490 expect_remove_snapshot(mock_image_ctx
, snap_id
, -EINVAL
);
493 auto req
= new MockUnlinkPeerRequest(&mock_image_ctx
, snap_id
, "peer_uuid",
496 ASSERT_EQ(-EINVAL
, ctx
.wait());
499 } // namespace snapshot
500 } // namespace mirror
501 } // namespace librbd