1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/rbd_mirror/test_mock_fixture.h"
5 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
6 #include "test/librados_test_stub/MockTestMemRadosClient.h"
7 #include "test/librbd/mock/MockImageCtx.h"
8 #include "test/rbd_mirror/mock/MockContextWQ.h"
9 #include "test/rbd_mirror/mock/MockSafeTimer.h"
10 #include "librbd/TrashWatcher.h"
11 #include "tools/rbd_mirror/Threads.h"
12 #include "tools/rbd_mirror/image_deleter/TrashWatcher.h"
17 struct MockTestImageCtx
: public librbd::MockImageCtx
{
18 MockTestImageCtx(librbd::ImageCtx
&image_ctx
)
19 : librbd::MockImageCtx(image_ctx
) {
23 } // anonymous namespace
25 struct MockTrashWatcher
{
26 static MockTrashWatcher
*s_instance
;
27 static MockTrashWatcher
&get_instance() {
28 ceph_assert(s_instance
!= nullptr);
36 MOCK_CONST_METHOD0(is_unregistered
, bool());
37 MOCK_METHOD1(register_watch
, void(Context
*));
38 MOCK_METHOD1(unregister_watch
, void(Context
*));
42 struct TrashWatcher
<MockTestImageCtx
> {
43 static TrashWatcher
*s_instance
;
45 TrashWatcher(librados::IoCtx
&io_ctx
, ::MockContextWQ
*work_queue
) {
48 virtual ~TrashWatcher() {
51 static TrashWatcher
<MockTestImageCtx
> &get_instance() {
52 ceph_assert(s_instance
!= nullptr);
56 virtual void handle_rewatch_complete(int r
) = 0;
58 virtual void handle_image_added(const std::string
&image_id
,
59 const cls::rbd::TrashImageSpec
& spec
) = 0;
60 virtual void handle_image_removed(const std::string
&image_id
) = 0;
62 bool is_unregistered() const {
63 return MockTrashWatcher::get_instance().is_unregistered();
65 void register_watch(Context
*ctx
) {
66 MockTrashWatcher::get_instance().register_watch(ctx
);
68 void unregister_watch(Context
*ctx
) {
69 MockTrashWatcher::get_instance().unregister_watch(ctx
);
73 MockTrashWatcher
*MockTrashWatcher::s_instance
= nullptr;
74 TrashWatcher
<MockTestImageCtx
> *TrashWatcher
<MockTestImageCtx
>::s_instance
= nullptr;
82 struct Threads
<librbd::MockTestImageCtx
> {
84 ceph::mutex
&timer_lock
;
86 MockContextWQ
*work_queue
;
88 Threads(Threads
<librbd::ImageCtx
> *threads
)
89 : timer(new MockSafeTimer()),
90 timer_lock(threads
->timer_lock
),
91 work_queue(new MockContextWQ()) {
102 #include "tools/rbd_mirror/image_deleter/TrashWatcher.cc"
106 namespace image_deleter
{
109 using ::testing::DoAll
;
110 using ::testing::InSequence
;
111 using ::testing::Invoke
;
112 using ::testing::Return
;
113 using ::testing::ReturnArg
;
114 using ::testing::StrEq
;
115 using ::testing::WithArg
;
117 class TestMockImageDeleterTrashWatcher
: public TestMockFixture
{
119 typedef TrashWatcher
<librbd::MockTestImageCtx
> MockTrashWatcher
;
120 typedef Threads
<librbd::MockTestImageCtx
> MockThreads
;
121 typedef librbd::MockTrashWatcher MockLibrbdTrashWatcher
;
122 typedef librbd::TrashWatcher
<librbd::MockTestImageCtx
> LibrbdTrashWatcher
;
124 struct MockListener
: TrashListener
{
125 MOCK_METHOD2(handle_trash_image
, void(const std::string
&,
126 const ceph::real_clock::time_point
&));
129 void expect_work_queue(MockThreads
&mock_threads
) {
130 EXPECT_CALL(*mock_threads
.work_queue
, queue(_
, _
))
131 .WillRepeatedly(Invoke([this](Context
*ctx
, int r
) {
132 m_threads
->work_queue
->queue(ctx
, r
);
136 void expect_trash_watcher_is_unregistered(MockLibrbdTrashWatcher
&mock_trash_watcher
,
138 EXPECT_CALL(mock_trash_watcher
, is_unregistered())
139 .WillOnce(Return(unregistered
));
142 void expect_trash_watcher_register(MockLibrbdTrashWatcher
&mock_trash_watcher
,
144 EXPECT_CALL(mock_trash_watcher
, register_watch(_
))
145 .WillOnce(CompleteContext(r
));
148 void expect_trash_watcher_unregister(MockLibrbdTrashWatcher
&mock_trash_watcher
,
150 EXPECT_CALL(mock_trash_watcher
, unregister_watch(_
))
151 .WillOnce(CompleteContext(r
));
154 void expect_create_trash(librados::IoCtx
&io_ctx
, int r
) {
155 EXPECT_CALL(get_mock_io_ctx(io_ctx
), create(RBD_TRASH
, false, _
))
156 .WillOnce(Return(r
));
159 void expect_trash_list(librados::IoCtx
&io_ctx
,
160 const std::string
& last_image_id
,
161 std::map
<std::string
, cls::rbd::TrashImageSpec
>&& images
,
164 encode(last_image_id
, bl
);
165 encode(static_cast<size_t>(1024), bl
);
168 encode(images
, out_bl
);
170 EXPECT_CALL(get_mock_io_ctx(io_ctx
),
171 exec(RBD_TRASH
, _
, StrEq("rbd"), StrEq("trash_list"),
172 ContentsEqual(bl
), _
, _
))
173 .WillOnce(DoAll(WithArg
<5>(Invoke([out_bl
](bufferlist
*bl
) {
179 void expect_timer_add_event(MockThreads
&mock_threads
) {
180 EXPECT_CALL(*mock_threads
.timer
, add_event_after(_
, _
))
181 .WillOnce(DoAll(WithArg
<1>(Invoke([this](Context
*ctx
) {
183 new LambdaContext([this, ctx
](int r
) {
184 std::lock_guard timer_locker
{m_threads
->timer_lock
};
187 m_threads
->work_queue
->queue(wrapped_ctx
, 0);
192 void expect_handle_trash_image(MockListener
& mock_listener
,
193 const std::string
& global_image_id
) {
194 EXPECT_CALL(mock_listener
, handle_trash_image(global_image_id
, _
));
197 int when_shut_down(MockTrashWatcher
&mock_trash_watcher
) {
199 mock_trash_watcher
.shut_down(&ctx
);
205 TEST_F(TestMockImageDeleterTrashWatcher
, EmptyPool
) {
206 MockThreads
mock_threads(m_threads
);
207 expect_work_queue(mock_threads
);
210 expect_create_trash(m_local_io_ctx
, 0);
212 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
213 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
214 expect_trash_watcher_register(mock_librbd_trash_watcher
, 0);
215 expect_trash_list(m_local_io_ctx
, "", {}, 0);
217 MockListener mock_listener
;
218 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
221 mock_trash_watcher
.init(&ctx
);
222 ASSERT_EQ(0, ctx
.wait());
224 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
225 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
228 TEST_F(TestMockImageDeleterTrashWatcher
, NonEmptyPool
) {
229 MockThreads
mock_threads(m_threads
);
230 expect_work_queue(mock_threads
);
232 MockListener mock_listener
;
233 expect_handle_trash_image(mock_listener
, "image0");
236 expect_create_trash(m_local_io_ctx
, 0);
238 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
239 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
240 expect_trash_watcher_register(mock_librbd_trash_watcher
, 0);
242 std::map
<std::string
, cls::rbd::TrashImageSpec
> images
;
243 images
["image0"] = {cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "name", {}, {}};
244 for (auto idx
= 1; idx
< 1024; ++idx
) {
245 images
["image" + stringify(idx
)] = {};
247 expect_trash_list(m_local_io_ctx
, "", std::move(images
), 0);
250 for (auto idx
= 1024; idx
< 2000; ++idx
) {
251 images
["image" + stringify(idx
)] = {};
253 expect_trash_list(m_local_io_ctx
, "image999", std::move(images
), 0);
255 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
258 mock_trash_watcher
.init(&ctx
);
259 ASSERT_EQ(0, ctx
.wait());
260 m_threads
->work_queue
->drain();
262 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
263 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
266 TEST_F(TestMockImageDeleterTrashWatcher
, Notify
) {
267 MockThreads
mock_threads(m_threads
);
268 expect_work_queue(mock_threads
);
270 MockListener mock_listener
;
271 expect_handle_trash_image(mock_listener
, "image1");
274 expect_create_trash(m_local_io_ctx
, 0);
276 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
277 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
278 expect_trash_watcher_register(mock_librbd_trash_watcher
, 0);
279 expect_trash_list(m_local_io_ctx
, "", {}, 0);
281 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
284 mock_trash_watcher
.init(&ctx
);
285 ASSERT_EQ(0, ctx
.wait());
287 LibrbdTrashWatcher::get_instance().handle_image_added(
288 "image1", {cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "name", {}, {}});
289 m_threads
->work_queue
->drain();
291 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
292 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
295 TEST_F(TestMockImageDeleterTrashWatcher
, CreateBlacklist
) {
296 MockThreads
mock_threads(m_threads
);
297 expect_work_queue(mock_threads
);
300 expect_create_trash(m_local_io_ctx
, -EBLACKLISTED
);
302 MockListener mock_listener
;
303 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
306 mock_trash_watcher
.init(&ctx
);
307 ASSERT_EQ(-EBLACKLISTED
, ctx
.wait());
309 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
310 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
311 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
314 TEST_F(TestMockImageDeleterTrashWatcher
, CreateDNE
) {
315 MockThreads
mock_threads(m_threads
);
316 expect_work_queue(mock_threads
);
319 expect_create_trash(m_local_io_ctx
, -ENOENT
);
321 MockListener mock_listener
;
322 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
325 mock_trash_watcher
.init(&ctx
);
326 ASSERT_EQ(-ENOENT
, ctx
.wait());
328 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
329 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
330 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
333 TEST_F(TestMockImageDeleterTrashWatcher
, CreateError
) {
334 MockThreads
mock_threads(m_threads
);
335 expect_work_queue(mock_threads
);
338 expect_create_trash(m_local_io_ctx
, -EINVAL
);
340 expect_timer_add_event(mock_threads
);
341 expect_create_trash(m_local_io_ctx
, 0);
343 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
344 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
345 expect_trash_watcher_register(mock_librbd_trash_watcher
, 0);
347 MockListener mock_listener
;
348 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
351 mock_trash_watcher
.init(&ctx
);
352 ASSERT_EQ(0, ctx
.wait());
354 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
355 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
358 TEST_F(TestMockImageDeleterTrashWatcher
, RegisterWatcherBlacklist
) {
359 MockThreads
mock_threads(m_threads
);
360 expect_work_queue(mock_threads
);
363 expect_create_trash(m_local_io_ctx
, 0);
365 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
366 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
367 expect_trash_watcher_register(mock_librbd_trash_watcher
, -EBLACKLISTED
);
369 MockListener mock_listener
;
370 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
373 mock_trash_watcher
.init(&ctx
);
374 ASSERT_EQ(-EBLACKLISTED
, ctx
.wait());
376 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
377 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
380 TEST_F(TestMockImageDeleterTrashWatcher
, RegisterWatcherError
) {
381 MockThreads
mock_threads(m_threads
);
382 expect_work_queue(mock_threads
);
385 expect_create_trash(m_local_io_ctx
, 0);
387 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
388 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
389 expect_trash_watcher_register(mock_librbd_trash_watcher
, -EINVAL
);
390 expect_timer_add_event(mock_threads
);
392 expect_create_trash(m_local_io_ctx
, 0);
394 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
395 expect_trash_watcher_register(mock_librbd_trash_watcher
, 0);
397 MockListener mock_listener
;
398 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
401 mock_trash_watcher
.init(&ctx
);
402 ASSERT_EQ(0, ctx
.wait());
404 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
405 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
408 TEST_F(TestMockImageDeleterTrashWatcher
, TrashListBlacklist
) {
409 MockThreads
mock_threads(m_threads
);
410 expect_work_queue(mock_threads
);
413 expect_create_trash(m_local_io_ctx
, 0);
415 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
416 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
417 expect_trash_watcher_register(mock_librbd_trash_watcher
, 0);
418 expect_trash_list(m_local_io_ctx
, "", {}, -EBLACKLISTED
);
420 MockListener mock_listener
;
421 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
424 mock_trash_watcher
.init(&ctx
);
425 ASSERT_EQ(-EBLACKLISTED
, ctx
.wait());
427 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
428 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
431 TEST_F(TestMockImageDeleterTrashWatcher
, TrashListError
) {
432 MockThreads
mock_threads(m_threads
);
433 expect_work_queue(mock_threads
);
436 expect_create_trash(m_local_io_ctx
, 0);
438 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
439 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
440 expect_trash_watcher_register(mock_librbd_trash_watcher
, 0);
441 expect_trash_list(m_local_io_ctx
, "", {}, -EINVAL
);
443 expect_timer_add_event(mock_threads
);
444 expect_create_trash(m_local_io_ctx
, 0);
446 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, false);
447 expect_trash_list(m_local_io_ctx
, "", {}, 0);
449 MockListener mock_listener
;
450 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
453 mock_trash_watcher
.init(&ctx
);
454 ASSERT_EQ(0, ctx
.wait());
456 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
457 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
460 TEST_F(TestMockImageDeleterTrashWatcher
, Rewatch
) {
461 MockThreads
mock_threads(m_threads
);
462 expect_work_queue(mock_threads
);
465 expect_create_trash(m_local_io_ctx
, 0);
467 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
468 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
469 expect_trash_watcher_register(mock_librbd_trash_watcher
, 0);
470 expect_trash_list(m_local_io_ctx
, "", {}, 0);
472 MockListener mock_listener
;
473 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
476 mock_trash_watcher
.init(&ctx
);
477 ASSERT_EQ(0, ctx
.wait());
479 expect_timer_add_event(mock_threads
);
480 expect_create_trash(m_local_io_ctx
, 0);
482 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, false);
483 expect_trash_list(m_local_io_ctx
, "", {}, 0);
484 LibrbdTrashWatcher::get_instance().handle_rewatch_complete(0);
485 m_threads
->work_queue
->drain();
487 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
488 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
491 TEST_F(TestMockImageDeleterTrashWatcher
, RewatchBlacklist
) {
492 MockThreads
mock_threads(m_threads
);
493 expect_work_queue(mock_threads
);
496 expect_create_trash(m_local_io_ctx
, 0);
498 MockLibrbdTrashWatcher mock_librbd_trash_watcher
;
499 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher
, true);
500 expect_trash_watcher_register(mock_librbd_trash_watcher
, 0);
501 expect_trash_list(m_local_io_ctx
, "", {}, 0);
503 MockListener mock_listener
;
504 MockTrashWatcher
mock_trash_watcher(m_local_io_ctx
, &mock_threads
,
507 mock_trash_watcher
.init(&ctx
);
508 ASSERT_EQ(0, ctx
.wait());
510 LibrbdTrashWatcher::get_instance().handle_rewatch_complete(-EBLACKLISTED
);
511 m_threads
->work_queue
->drain();
513 expect_trash_watcher_unregister(mock_librbd_trash_watcher
, 0);
514 ASSERT_EQ(0, when_shut_down(mock_trash_watcher
));
517 } // namespace image_deleter
518 } // namespace mirror