]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/rbd_mirror/image_deleter/test_mock_TrashWatcher.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / test / rbd_mirror / image_deleter / test_mock_TrashWatcher.cc
CommitLineData
11fdf7f2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
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"
13
14namespace librbd {
15namespace {
16
17struct MockTestImageCtx : public librbd::MockImageCtx {
18 MockTestImageCtx(librbd::ImageCtx &image_ctx)
19 : librbd::MockImageCtx(image_ctx) {
20 }
21};
22
23} // anonymous namespace
24
25struct MockTrashWatcher {
26 static MockTrashWatcher *s_instance;
27 static MockTrashWatcher &get_instance() {
28 ceph_assert(s_instance != nullptr);
29 return *s_instance;
30 }
31
32 MockTrashWatcher() {
33 s_instance = this;
34 }
35
36 MOCK_CONST_METHOD0(is_unregistered, bool());
37 MOCK_METHOD1(register_watch, void(Context*));
38 MOCK_METHOD1(unregister_watch, void(Context*));
39};
40
41template <>
42struct TrashWatcher<MockTestImageCtx> {
43 static TrashWatcher *s_instance;
44
45 TrashWatcher(librados::IoCtx &io_ctx, ::MockContextWQ *work_queue) {
46 s_instance = this;
47 }
48 virtual ~TrashWatcher() {
49 }
50
51 static TrashWatcher<MockTestImageCtx> &get_instance() {
52 ceph_assert(s_instance != nullptr);
53 return *s_instance;
54 }
55
56 virtual void handle_rewatch_complete(int r) = 0;
57
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;
61
62 bool is_unregistered() const {
63 return MockTrashWatcher::get_instance().is_unregistered();
64 }
65 void register_watch(Context *ctx) {
66 MockTrashWatcher::get_instance().register_watch(ctx);
67 }
68 void unregister_watch(Context *ctx) {
69 MockTrashWatcher::get_instance().unregister_watch(ctx);
70 }
71};
72
73MockTrashWatcher *MockTrashWatcher::s_instance = nullptr;
74TrashWatcher<MockTestImageCtx> *TrashWatcher<MockTestImageCtx>::s_instance = nullptr;
75
76} // namespace librbd
77
78namespace rbd {
79namespace mirror {
80
81template <>
82struct Threads<librbd::MockTestImageCtx> {
83 MockSafeTimer *timer;
9f95a23c 84 ceph::mutex &timer_lock;
11fdf7f2
TL
85
86 MockContextWQ *work_queue;
87
88 Threads(Threads<librbd::ImageCtx> *threads)
89 : timer(new MockSafeTimer()),
90 timer_lock(threads->timer_lock),
91 work_queue(new MockContextWQ()) {
92 }
93 ~Threads() {
94 delete timer;
95 delete work_queue;
96 }
97};
98
99} // namespace mirror
100} // namespace rbd
101
102#include "tools/rbd_mirror/image_deleter/TrashWatcher.cc"
103
104namespace rbd {
105namespace mirror {
106namespace image_deleter {
107
108using ::testing::_;
109using ::testing::DoAll;
110using ::testing::InSequence;
111using ::testing::Invoke;
112using ::testing::Return;
113using ::testing::ReturnArg;
114using ::testing::StrEq;
115using ::testing::WithArg;
116
117class TestMockImageDeleterTrashWatcher : public TestMockFixture {
118public:
119 typedef TrashWatcher<librbd::MockTestImageCtx> MockTrashWatcher;
120 typedef Threads<librbd::MockTestImageCtx> MockThreads;
121 typedef librbd::MockTrashWatcher MockLibrbdTrashWatcher;
122 typedef librbd::TrashWatcher<librbd::MockTestImageCtx> LibrbdTrashWatcher;
123
124 struct MockListener : TrashListener {
9f95a23c
TL
125 MOCK_METHOD2(handle_trash_image, void(const std::string&,
126 const ceph::real_clock::time_point&));
11fdf7f2
TL
127 };
128
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);
133 }));
134 }
135
136 void expect_trash_watcher_is_unregistered(MockLibrbdTrashWatcher &mock_trash_watcher,
137 bool unregistered) {
138 EXPECT_CALL(mock_trash_watcher, is_unregistered())
139 .WillOnce(Return(unregistered));
140 }
141
142 void expect_trash_watcher_register(MockLibrbdTrashWatcher &mock_trash_watcher,
143 int r) {
144 EXPECT_CALL(mock_trash_watcher, register_watch(_))
145 .WillOnce(CompleteContext(r));
146 }
147
148 void expect_trash_watcher_unregister(MockLibrbdTrashWatcher &mock_trash_watcher,
149 int r) {
150 EXPECT_CALL(mock_trash_watcher, unregister_watch(_))
151 .WillOnce(CompleteContext(r));
152 }
153
154 void expect_create_trash(librados::IoCtx &io_ctx, int r) {
9f95a23c 155 EXPECT_CALL(get_mock_io_ctx(io_ctx), create(RBD_TRASH, false, _))
11fdf7f2
TL
156 .WillOnce(Return(r));
157 }
158
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,
162 int r) {
163 bufferlist bl;
164 encode(last_image_id, bl);
165 encode(static_cast<size_t>(1024), bl);
166
167 bufferlist out_bl;
168 encode(images, out_bl);
169
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) {
174 *bl = out_bl;
175 })),
176 Return(r)));
177 }
178
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) {
182 auto wrapped_ctx =
9f95a23c
TL
183 new LambdaContext([this, ctx](int r) {
184 std::lock_guard timer_locker{m_threads->timer_lock};
11fdf7f2
TL
185 ctx->complete(r);
186 });
187 m_threads->work_queue->queue(wrapped_ctx, 0);
188 })),
189 ReturnArg<1>()));
190 }
191
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, _));
195 }
196
197 int when_shut_down(MockTrashWatcher &mock_trash_watcher) {
198 C_SaferCond ctx;
199 mock_trash_watcher.shut_down(&ctx);
200 return ctx.wait();
201 }
202
203};
204
205TEST_F(TestMockImageDeleterTrashWatcher, EmptyPool) {
206 MockThreads mock_threads(m_threads);
207 expect_work_queue(mock_threads);
208
209 InSequence seq;
210 expect_create_trash(m_local_io_ctx, 0);
211
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);
216
217 MockListener mock_listener;
218 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
219 mock_listener);
220 C_SaferCond ctx;
221 mock_trash_watcher.init(&ctx);
222 ASSERT_EQ(0, ctx.wait());
223
224 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
225 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
226}
227
228TEST_F(TestMockImageDeleterTrashWatcher, NonEmptyPool) {
229 MockThreads mock_threads(m_threads);
230 expect_work_queue(mock_threads);
231
232 MockListener mock_listener;
233 expect_handle_trash_image(mock_listener, "image0");
234
235 InSequence seq;
236 expect_create_trash(m_local_io_ctx, 0);
237
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);
241
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)] = {};
246 }
247 expect_trash_list(m_local_io_ctx, "", std::move(images), 0);
248
249 images.clear();
250 for (auto idx = 1024; idx < 2000; ++idx) {
251 images["image" + stringify(idx)] = {};
252 }
253 expect_trash_list(m_local_io_ctx, "image999", std::move(images), 0);
254
255 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
256 mock_listener);
257 C_SaferCond ctx;
258 mock_trash_watcher.init(&ctx);
259 ASSERT_EQ(0, ctx.wait());
260 m_threads->work_queue->drain();
261
262 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
263 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
264}
265
266TEST_F(TestMockImageDeleterTrashWatcher, Notify) {
267 MockThreads mock_threads(m_threads);
268 expect_work_queue(mock_threads);
269
270 MockListener mock_listener;
271 expect_handle_trash_image(mock_listener, "image1");
272
273 InSequence seq;
274 expect_create_trash(m_local_io_ctx, 0);
275
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);
280
281 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
282 mock_listener);
283 C_SaferCond ctx;
284 mock_trash_watcher.init(&ctx);
285 ASSERT_EQ(0, ctx.wait());
286
287 LibrbdTrashWatcher::get_instance().handle_image_added(
288 "image1", {cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "name", {}, {}});
289 m_threads->work_queue->drain();
290
291 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
292 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
293}
294
295TEST_F(TestMockImageDeleterTrashWatcher, CreateBlacklist) {
296 MockThreads mock_threads(m_threads);
297 expect_work_queue(mock_threads);
298
299 InSequence seq;
300 expect_create_trash(m_local_io_ctx, -EBLACKLISTED);
301
302 MockListener mock_listener;
303 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
304 mock_listener);
305 C_SaferCond ctx;
306 mock_trash_watcher.init(&ctx);
307 ASSERT_EQ(-EBLACKLISTED, ctx.wait());
308
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));
312}
313
314TEST_F(TestMockImageDeleterTrashWatcher, CreateDNE) {
315 MockThreads mock_threads(m_threads);
316 expect_work_queue(mock_threads);
317
318 InSequence seq;
319 expect_create_trash(m_local_io_ctx, -ENOENT);
320
321 MockListener mock_listener;
322 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
323 mock_listener);
324 C_SaferCond ctx;
325 mock_trash_watcher.init(&ctx);
326 ASSERT_EQ(-ENOENT, ctx.wait());
327
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));
331}
332
333TEST_F(TestMockImageDeleterTrashWatcher, CreateError) {
334 MockThreads mock_threads(m_threads);
335 expect_work_queue(mock_threads);
336
337 InSequence seq;
338 expect_create_trash(m_local_io_ctx, -EINVAL);
339
340 expect_timer_add_event(mock_threads);
341 expect_create_trash(m_local_io_ctx, 0);
342
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);
346
347 MockListener mock_listener;
348 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
349 mock_listener);
350 C_SaferCond ctx;
351 mock_trash_watcher.init(&ctx);
352 ASSERT_EQ(0, ctx.wait());
353
354 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
355 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
356}
357
358TEST_F(TestMockImageDeleterTrashWatcher, RegisterWatcherBlacklist) {
359 MockThreads mock_threads(m_threads);
360 expect_work_queue(mock_threads);
361
362 InSequence seq;
363 expect_create_trash(m_local_io_ctx, 0);
364
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);
368
369 MockListener mock_listener;
370 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
371 mock_listener);
372 C_SaferCond ctx;
373 mock_trash_watcher.init(&ctx);
374 ASSERT_EQ(-EBLACKLISTED, ctx.wait());
375
376 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
377 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
378}
379
380TEST_F(TestMockImageDeleterTrashWatcher, RegisterWatcherError) {
381 MockThreads mock_threads(m_threads);
382 expect_work_queue(mock_threads);
383
384 InSequence seq;
385 expect_create_trash(m_local_io_ctx, 0);
386
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);
391
392 expect_create_trash(m_local_io_ctx, 0);
393
394 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, true);
395 expect_trash_watcher_register(mock_librbd_trash_watcher, 0);
396
397 MockListener mock_listener;
398 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
399 mock_listener);
400 C_SaferCond ctx;
401 mock_trash_watcher.init(&ctx);
402 ASSERT_EQ(0, ctx.wait());
403
404 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
405 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
406}
407
408TEST_F(TestMockImageDeleterTrashWatcher, TrashListBlacklist) {
409 MockThreads mock_threads(m_threads);
410 expect_work_queue(mock_threads);
411
412 InSequence seq;
413 expect_create_trash(m_local_io_ctx, 0);
414
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);
419
420 MockListener mock_listener;
421 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
422 mock_listener);
423 C_SaferCond ctx;
424 mock_trash_watcher.init(&ctx);
425 ASSERT_EQ(-EBLACKLISTED, ctx.wait());
426
427 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
428 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
429}
430
431TEST_F(TestMockImageDeleterTrashWatcher, TrashListError) {
432 MockThreads mock_threads(m_threads);
433 expect_work_queue(mock_threads);
434
435 InSequence seq;
436 expect_create_trash(m_local_io_ctx, 0);
437
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);
442
443 expect_timer_add_event(mock_threads);
444 expect_create_trash(m_local_io_ctx, 0);
445
446 expect_trash_watcher_is_unregistered(mock_librbd_trash_watcher, false);
447 expect_trash_list(m_local_io_ctx, "", {}, 0);
448
449 MockListener mock_listener;
450 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
451 mock_listener);
452 C_SaferCond ctx;
453 mock_trash_watcher.init(&ctx);
454 ASSERT_EQ(0, ctx.wait());
455
456 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
457 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
458}
459
460TEST_F(TestMockImageDeleterTrashWatcher, Rewatch) {
461 MockThreads mock_threads(m_threads);
462 expect_work_queue(mock_threads);
463
464 InSequence seq;
465 expect_create_trash(m_local_io_ctx, 0);
466
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);
471
472 MockListener mock_listener;
473 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
474 mock_listener);
475 C_SaferCond ctx;
476 mock_trash_watcher.init(&ctx);
477 ASSERT_EQ(0, ctx.wait());
478
479 expect_timer_add_event(mock_threads);
480 expect_create_trash(m_local_io_ctx, 0);
481
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();
486
487 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
488 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
489}
490
491TEST_F(TestMockImageDeleterTrashWatcher, RewatchBlacklist) {
492 MockThreads mock_threads(m_threads);
493 expect_work_queue(mock_threads);
494
495 InSequence seq;
496 expect_create_trash(m_local_io_ctx, 0);
497
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);
502
503 MockListener mock_listener;
504 MockTrashWatcher mock_trash_watcher(m_local_io_ctx, &mock_threads,
505 mock_listener);
506 C_SaferCond ctx;
507 mock_trash_watcher.init(&ctx);
508 ASSERT_EQ(0, ctx.wait());
509
510 LibrbdTrashWatcher::get_instance().handle_rewatch_complete(-EBLACKLISTED);
511 m_threads->work_queue->drain();
512
513 expect_trash_watcher_unregister(mock_librbd_trash_watcher, 0);
514 ASSERT_EQ(0, when_shut_down(mock_trash_watcher));
515}
516
517} // namespace image_deleter
518} // namespace mirror
519} // namespace rbd