]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/librbd/test_mock_Journal.cc
update sources to 12.2.7
[ceph.git] / ceph / src / test / librbd / test_mock_Journal.cc
CommitLineData
7c673cae
FG
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/librbd/test_mock_fixture.h"
5#include "test/journal/mock/MockJournaler.h"
6#include "test/librbd/test_support.h"
7#include "test/librbd/mock/MockImageCtx.h"
8#include "test/librbd/mock/MockJournalPolicy.h"
9#include "common/Cond.h"
10#include "common/Mutex.h"
11#include "cls/journal/cls_journal_types.h"
12#include "journal/Journaler.h"
13#include "librbd/Journal.h"
14#include "librbd/Utils.h"
15#include "librbd/io/AioCompletion.h"
16#include "librbd/io/ObjectRequest.h"
17#include "librbd/journal/Replay.h"
18#include "librbd/journal/RemoveRequest.h"
19#include "librbd/journal/CreateRequest.h"
20#include "librbd/journal/OpenRequest.h"
21#include "librbd/journal/Types.h"
22#include "librbd/journal/TypeTraits.h"
23#include "librbd/journal/PromoteRequest.h"
24#include "gmock/gmock.h"
25#include "gtest/gtest.h"
26#include <functional>
27#include <list>
28#include <boost/scope_exit.hpp>
29
30#define dout_context g_ceph_context
31#define dout_subsys ceph_subsys_rbd
32
33namespace librbd {
34
35namespace {
36
37struct MockJournalImageCtx : public MockImageCtx {
38 MockJournalImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
39 }
40};
41
42} // anonymous namespace
43
44namespace journal {
45
46template <>
47struct TypeTraits<MockJournalImageCtx> {
48 typedef ::journal::MockJournalerProxy Journaler;
49 typedef ::journal::MockFutureProxy Future;
50 typedef ::journal::MockReplayEntryProxy ReplayEntry;
51};
52
53struct MockReplay {
54 static MockReplay *s_instance;
55 static MockReplay &get_instance() {
56 assert(s_instance != nullptr);
57 return *s_instance;
58 }
59
60 MockReplay() {
61 s_instance = this;
62 }
63
64 MOCK_METHOD2(shut_down, void(bool cancel_ops, Context *));
65 MOCK_METHOD2(decode, int(bufferlist::iterator*, EventEntry *));
66 MOCK_METHOD3(process, void(const EventEntry&, Context *, Context *));
67 MOCK_METHOD2(replay_op_ready, void(uint64_t, Context *));
68};
69
70template <>
71class Replay<MockJournalImageCtx> {
72public:
73 static Replay *create(MockJournalImageCtx &image_ctx) {
74 return new Replay();
75 }
76
77 void shut_down(bool cancel_ops, Context *on_finish) {
78 MockReplay::get_instance().shut_down(cancel_ops, on_finish);
79 }
80
81 int decode(bufferlist::iterator *it, EventEntry *event_entry) {
82 return MockReplay::get_instance().decode(it, event_entry);
83 }
84
85 void process(const EventEntry& event_entry, Context *on_ready,
86 Context *on_commit) {
87 MockReplay::get_instance().process(event_entry, on_ready, on_commit);
88 }
89
90 void replay_op_ready(uint64_t op_tid, Context *on_resume) {
91 MockReplay::get_instance().replay_op_ready(op_tid, on_resume);
92 }
93};
94
95MockReplay *MockReplay::s_instance = nullptr;
96
97struct MockRemove {
98 static MockRemove *s_instance;
99 static MockRemove &get_instance() {
100 assert(s_instance != nullptr);
101 return *s_instance;
102 }
103
104 MockRemove() {
105 s_instance = this;
106 }
107
108 MOCK_METHOD0(send, void());
109};
110
111template <>
112class RemoveRequest<MockJournalImageCtx> {
113public:
114 static RemoveRequest *create(IoCtx &ioctx, const std::string &imageid,
115 const std::string &client_id,
116 ContextWQ *op_work_queue, Context *on_finish) {
117 return new RemoveRequest();
118 }
119
120 void send() {
121 MockRemove::get_instance().send();
122 }
123};
124
125MockRemove *MockRemove::s_instance = nullptr;
126
127struct MockCreate {
128 static MockCreate *s_instance;
129 static MockCreate &get_instance() {
130 assert(s_instance != nullptr);
131 return *s_instance;
132 }
133
134 MockCreate() {
135 s_instance = this;
136 }
137
138 MOCK_METHOD0(send, void());
139};
140
141template<>
142class CreateRequest<MockJournalImageCtx> {
143public:
144 static CreateRequest *create(IoCtx &ioctx, const std::string &imageid,
145 uint8_t order, uint8_t splay_width,
146 const std::string &object_pool,
147 uint64_t tag_class, TagData &tag_data,
148 const std::string &client_id,
149 ContextWQ *op_work_queue, Context *on_finish) {
150 return new CreateRequest();
151 }
152
153 void send() {
154 MockCreate::get_instance().send();
155 }
156};
157
158MockCreate *MockCreate::s_instance = nullptr;
159
160template<>
161class OpenRequest<MockJournalImageCtx> {
162public:
163 TagData *tag_data;
164 Context *on_finish;
165 static OpenRequest *s_instance;
166 static OpenRequest *create(MockJournalImageCtx *image_ctx,
167 ::journal::MockJournalerProxy *journaler,
168 Mutex *lock, journal::ImageClientMeta *client_meta,
169 uint64_t *tag_tid, journal::TagData *tag_data,
170 Context *on_finish) {
171 assert(s_instance != nullptr);
172 s_instance->tag_data = tag_data;
173 s_instance->on_finish = on_finish;
174 return s_instance;
175 }
176
177 OpenRequest() {
178 s_instance = this;
179 }
180
181 MOCK_METHOD0(send, void());
182};
183
184OpenRequest<MockJournalImageCtx> *OpenRequest<MockJournalImageCtx>::s_instance = nullptr;
185
186
187template <>
188class PromoteRequest<MockJournalImageCtx> {
189public:
190 static PromoteRequest s_instance;
191 static PromoteRequest *create(MockJournalImageCtx *image_ctx, bool force,
192 Context *on_finish) {
193 return &s_instance;
194 }
195
196 MOCK_METHOD0(send, void());
197};
198
199PromoteRequest<MockJournalImageCtx> PromoteRequest<MockJournalImageCtx>::s_instance;
200
201} // namespace journal
202} // namespace librbd
203
204// template definitions
205#include "librbd/Journal.cc"
206
207using ::testing::_;
208using ::testing::DoAll;
209using ::testing::InSequence;
210using ::testing::Invoke;
211using ::testing::InvokeWithoutArgs;
212using ::testing::MatcherCast;
213using ::testing::Return;
214using ::testing::SaveArg;
215using ::testing::SetArgPointee;
216using ::testing::WithArg;
217using namespace std::placeholders;
218
219ACTION_P2(StartReplay, wq, ctx) {
220 wq->queue(ctx, 0);
221}
222
223namespace librbd {
224
225class TestMockJournal : public TestMockFixture {
226public:
227 typedef journal::MockReplay MockJournalReplay;
228 typedef Journal<MockJournalImageCtx> MockJournal;
229 typedef journal::OpenRequest<MockJournalImageCtx> MockJournalOpenRequest;
230
231 typedef std::function<void(::journal::ReplayHandler*)> ReplayAction;
232 typedef std::list<Context *> Contexts;
233
234 TestMockJournal() : m_lock("lock") {
235 }
236
237 ~TestMockJournal() override {
238 assert(m_commit_contexts.empty());
239 }
240
241 Mutex m_lock;
242 Cond m_cond;
243 Contexts m_commit_contexts;
244
245 struct C_ReplayAction : public Context {
246 ::journal::ReplayHandler **replay_handler;
247 ReplayAction replay_action;
248
249 C_ReplayAction(::journal::ReplayHandler **replay_handler,
250 const ReplayAction &replay_action)
251 : replay_handler(replay_handler), replay_action(replay_action) {
252 }
253 void finish(int r) override {
254 if (replay_action) {
255 replay_action(*replay_handler);
256 }
257 }
258 };
259
260 void expect_construct_journaler(::journal::MockJournaler &mock_journaler) {
261 EXPECT_CALL(mock_journaler, construct());
262 }
263
264 void expect_open_journaler(MockImageCtx &mock_image_ctx,
265 ::journal::MockJournaler &mock_journaler,
266 MockJournalOpenRequest &mock_open_request,
267 bool primary, int r) {
268 EXPECT_CALL(mock_journaler, add_listener(_))
269 .WillOnce(SaveArg<0>(&m_listener));
270 EXPECT_CALL(mock_open_request, send())
271 .WillOnce(DoAll(Invoke([&mock_open_request, primary]() {
272 if (!primary) {
273 mock_open_request.tag_data->mirror_uuid = "remote mirror uuid";
274 }
275 }),
276 FinishRequest(&mock_open_request, r,
277 &mock_image_ctx)));
278 }
279
280 void expect_shut_down_journaler(::journal::MockJournaler &mock_journaler) {
281 EXPECT_CALL(mock_journaler, remove_listener(_));
282 EXPECT_CALL(mock_journaler, shut_down(_))
283 .WillOnce(CompleteContext(0, static_cast<ContextWQ*>(NULL)));
284 }
285
286 void expect_get_max_append_size(::journal::MockJournaler &mock_journaler,
287 uint32_t max_size) {
288 EXPECT_CALL(mock_journaler, get_max_append_size())
289 .WillOnce(Return(max_size));
290 }
291
292 void expect_get_journaler_cached_client(::journal::MockJournaler &mock_journaler,
293 const journal::ImageClientMeta &client_meta,
294 int r) {
295 journal::ClientData client_data;
296 client_data.client_meta = client_meta;
297
298 cls::journal::Client client;
299 ::encode(client_data, client.data);
300
301 EXPECT_CALL(mock_journaler, get_cached_client("", _))
302 .WillOnce(DoAll(SetArgPointee<1>(client),
303 Return(r)));
304 }
305
306 void expect_get_journaler_tags(MockImageCtx &mock_image_ctx,
307 ::journal::MockJournaler &mock_journaler,
308 uint64_t start_after_tag_tid,
309 ::journal::Journaler::Tags &&tags, int r) {
310 EXPECT_CALL(mock_journaler, get_tags(start_after_tag_tid, 0, _, _))
311 .WillOnce(DoAll(SetArgPointee<2>(tags),
312 WithArg<3>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))));
313 }
314
315 void expect_start_replay(MockJournalImageCtx &mock_image_ctx,
316 ::journal::MockJournaler &mock_journaler,
317 const ReplayAction &action) {
318 EXPECT_CALL(mock_journaler, start_replay(_))
319 .WillOnce(DoAll(SaveArg<0>(&m_replay_handler),
320 StartReplay(mock_image_ctx.image_ctx->op_work_queue,
321 new C_ReplayAction(&m_replay_handler,
322 action))));
323 }
324
325 void expect_stop_replay(::journal::MockJournaler &mock_journaler) {
326 EXPECT_CALL(mock_journaler, stop_replay(_))
327 .WillOnce(CompleteContext(0, static_cast<ContextWQ*>(NULL)));
328 }
329
330 void expect_shut_down_replay(MockJournalImageCtx &mock_image_ctx,
331 MockJournalReplay &mock_journal_replay, int r,
332 bool cancel_ops = false) {
333 EXPECT_CALL(mock_journal_replay, shut_down(cancel_ops, _))
334 .WillOnce(WithArg<1>(Invoke([this, &mock_image_ctx, r](Context *on_flush) {
335 this->commit_replay(mock_image_ctx, on_flush, r);})));
336 }
337
338 void expect_get_data(::journal::MockReplayEntry &mock_replay_entry) {
339 EXPECT_CALL(mock_replay_entry, get_data())
340 .WillOnce(Return(bufferlist()));
341 }
342
343 void expect_try_pop_front(MockJournalImageCtx &mock_image_ctx,
344 ::journal::MockJournaler &mock_journaler,
345 bool entries_available,
346 ::journal::MockReplayEntry &mock_replay_entry,
347 const ReplayAction &action = {}) {
348 EXPECT_CALL(mock_journaler, try_pop_front(_))
349 .WillOnce(DoAll(SetArgPointee<0>(::journal::MockReplayEntryProxy()),
350 StartReplay(mock_image_ctx.image_ctx->op_work_queue,
351 new C_ReplayAction(&m_replay_handler,
352 action)),
353 Return(entries_available)));
354 if (entries_available) {
355 expect_get_data(mock_replay_entry);
356 }
357 }
358
359 void expect_replay_process(MockJournalReplay &mock_journal_replay) {
360 EXPECT_CALL(mock_journal_replay, decode(_, _))
361 .WillOnce(Return(0));
362 EXPECT_CALL(mock_journal_replay, process(_, _, _))
363 .WillOnce(DoAll(WithArg<1>(CompleteContext(0, static_cast<ContextWQ*>(NULL))),
364 WithArg<2>(Invoke(this, &TestMockJournal::save_commit_context))));
365 }
366
367 void expect_start_append(::journal::MockJournaler &mock_journaler) {
368 EXPECT_CALL(mock_journaler, start_append(_, _, _));
369 }
370
371 void expect_stop_append(::journal::MockJournaler &mock_journaler, int r) {
372 EXPECT_CALL(mock_journaler, stop_append(_))
373 .WillOnce(CompleteContext(r, static_cast<ContextWQ*>(NULL)));
374 }
375
376 void expect_committed(::journal::MockJournaler &mock_journaler,
377 size_t events) {
378 EXPECT_CALL(mock_journaler, committed(MatcherCast<const ::journal::MockReplayEntryProxy&>(_)))
379 .Times(events);
380 }
381
382 void expect_append_journaler(::journal::MockJournaler &mock_journaler) {
383 EXPECT_CALL(mock_journaler, append(_, _))
384 .WillOnce(Return(::journal::MockFutureProxy()));
385 }
386
387 void expect_wait_future(::journal::MockFuture &mock_future,
388 Context **on_safe) {
389 EXPECT_CALL(mock_future, wait(_))
390 .WillOnce(SaveArg<0>(on_safe));
391 }
392
393 void expect_future_committed(::journal::MockJournaler &mock_journaler) {
394 EXPECT_CALL(mock_journaler, committed(MatcherCast<const ::journal::MockFutureProxy&>(_)));
395 }
396
397 void expect_future_is_valid(::journal::MockFuture &mock_future) {
398 EXPECT_CALL(mock_future, is_valid()).WillOnce(Return(false));
399 }
400
401 void expect_flush_commit_position(::journal::MockJournaler &mock_journaler) {
402 EXPECT_CALL(mock_journaler, flush_commit_position(_))
403 .WillOnce(CompleteContext(0, static_cast<ContextWQ*>(NULL)));
404 }
405
406 int when_open(MockJournal &mock_journal) {
407 C_SaferCond ctx;
408 mock_journal.open(&ctx);
409 return ctx.wait();
410 }
411
412 int when_close(MockJournal &mock_journal) {
413 C_SaferCond ctx;
414 mock_journal.close(&ctx);
415 return ctx.wait();
416 }
417
418 uint64_t when_append_write_event(MockJournalImageCtx &mock_image_ctx,
419 MockJournal &mock_journal, uint64_t length) {
420 bufferlist bl;
421 bl.append_zero(length);
422
423 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
424 return mock_journal.append_write_event(0, length, bl, {}, false);
425 }
426
427 uint64_t when_append_io_event(MockJournalImageCtx &mock_image_ctx,
428 MockJournal &mock_journal,
b32b8144
FG
429 io::ObjectRequest<> *object_request,
430 int filter_ret_val) {
7c673cae
FG
431 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
432 MockJournal::IOObjectRequests object_requests;
433 if (object_request != nullptr) {
434 object_requests.push_back(object_request);
435 }
436 return mock_journal.append_io_event(
437 journal::EventEntry{journal::AioFlushEvent{}}, object_requests, 0, 0,
b32b8144 438 false, filter_ret_val);
7c673cae
FG
439 }
440
441 void save_commit_context(Context *ctx) {
442 Mutex::Locker locker(m_lock);
443 m_commit_contexts.push_back(ctx);
444 m_cond.Signal();
445 }
446
447 void wake_up() {
448 Mutex::Locker locker(m_lock);
449 m_cond.Signal();
450 }
451
452 void commit_replay(MockJournalImageCtx &mock_image_ctx, Context *on_flush,
453 int r) {
454 Contexts commit_contexts;
455 std::swap(commit_contexts, m_commit_contexts);
456
457 derr << "SHUT DOWN REPLAY START" << dendl;
458 for (auto ctx : commit_contexts) {
459 mock_image_ctx.image_ctx->op_work_queue->queue(ctx, r);
460 }
461
462 on_flush = new FunctionContext([on_flush](int r) {
463 derr << "FLUSH START" << dendl;
464 on_flush->complete(r);
465 derr << "FLUSH FINISH" << dendl;
466 });
467 mock_image_ctx.image_ctx->op_work_queue->queue(on_flush, 0);
468 derr << "SHUT DOWN REPLAY FINISH" << dendl;
469 }
470
471 void open_journal(MockJournalImageCtx &mock_image_ctx,
472 MockJournal &mock_journal,
473 ::journal::MockJournaler &mock_journaler,
474 MockJournalOpenRequest &mock_open_request,
475 bool primary = true) {
476 expect_op_work_queue(mock_image_ctx);
477
478 InSequence seq;
479 expect_construct_journaler(mock_journaler);
480 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
481 primary, 0);
482 expect_get_max_append_size(mock_journaler, 1 << 16);
483 expect_start_replay(
484 mock_image_ctx, mock_journaler,
485 std::bind(&invoke_replay_complete, _1, 0));
486
487 MockJournalReplay mock_journal_replay;
488 expect_stop_replay(mock_journaler);
489 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
490 expect_committed(mock_journaler, 0);
94b18763 491 expect_flush_commit_position(mock_journaler);
7c673cae
FG
492 expect_start_append(mock_journaler);
493 ASSERT_EQ(0, when_open(mock_journal));
494 }
495
496 void close_journal(MockJournal &mock_journal,
497 ::journal::MockJournaler &mock_journaler) {
498 expect_stop_append(mock_journaler, 0);
499 ASSERT_EQ(0, when_close(mock_journal));
500 }
501
502 static void invoke_replay_ready(::journal::ReplayHandler *handler) {
503 handler->handle_entries_available();
504 }
505
506 static void invoke_replay_complete(::journal::ReplayHandler *handler, int r) {
507 handler->handle_complete(r);
508 }
509
510 ::journal::ReplayHandler *m_replay_handler = nullptr;
511 ::journal::JournalMetadataListener *m_listener = nullptr;
512};
513
514TEST_F(TestMockJournal, StateTransitions) {
515 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
516
517 librbd::ImageCtx *ictx;
518 ASSERT_EQ(0, open_image(m_image_name, &ictx));
519
520 MockJournalImageCtx mock_image_ctx(*ictx);
521 MockJournal mock_journal(mock_image_ctx);
522 expect_op_work_queue(mock_image_ctx);
523
524 InSequence seq;
525
526 ::journal::MockJournaler mock_journaler;
527 MockJournalOpenRequest mock_open_request;
528 expect_construct_journaler(mock_journaler);
529 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
530 true, 0);
531 expect_get_max_append_size(mock_journaler, 1 << 16);
532 expect_start_replay(
533 mock_image_ctx, mock_journaler,
534 std::bind(&invoke_replay_ready, _1));
535
536 ::journal::MockReplayEntry mock_replay_entry;
537 MockJournalReplay mock_journal_replay;
538 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
539 expect_replay_process(mock_journal_replay);
540 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
541 expect_replay_process(mock_journal_replay);
542 expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
543 std::bind(&invoke_replay_ready, _1));
544 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
545 expect_replay_process(mock_journal_replay);
546 expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
547 std::bind(&invoke_replay_complete, _1, 0));
548
549 expect_stop_replay(mock_journaler);
550 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
551 expect_committed(mock_journaler, 3);
94b18763 552 expect_flush_commit_position(mock_journaler);
7c673cae
FG
553
554 expect_start_append(mock_journaler);
555
556 ASSERT_EQ(0, when_open(mock_journal));
557
558 expect_stop_append(mock_journaler, 0);
559 expect_shut_down_journaler(mock_journaler);
560 ASSERT_EQ(0, when_close(mock_journal));
561}
562
563TEST_F(TestMockJournal, InitError) {
564 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
565
566 librbd::ImageCtx *ictx;
567 ASSERT_EQ(0, open_image(m_image_name, &ictx));
568
569 MockJournalImageCtx mock_image_ctx(*ictx);
570 MockJournal mock_journal(mock_image_ctx);
571 expect_op_work_queue(mock_image_ctx);
572
573 InSequence seq;
574
575 ::journal::MockJournaler mock_journaler;
576 MockJournalOpenRequest mock_open_request;
577 expect_construct_journaler(mock_journaler);
578 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
579 true, -EINVAL);
580 expect_shut_down_journaler(mock_journaler);
581 ASSERT_EQ(-EINVAL, when_open(mock_journal));
582}
583
584TEST_F(TestMockJournal, ReplayCompleteError) {
585 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
586
587 librbd::ImageCtx *ictx;
588 ASSERT_EQ(0, open_image(m_image_name, &ictx));
589
590 MockJournalImageCtx mock_image_ctx(*ictx);
591 MockJournal mock_journal(mock_image_ctx);
592 expect_op_work_queue(mock_image_ctx);
593
594 InSequence seq;
595
596 ::journal::MockJournaler mock_journaler;
597 MockJournalOpenRequest mock_open_request;
598 expect_construct_journaler(mock_journaler);
599 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
600 true, 0);
601 expect_get_max_append_size(mock_journaler, 1 << 16);
602 expect_start_replay(
603 mock_image_ctx, mock_journaler,
604 std::bind(&invoke_replay_complete, _1, -EINVAL));
605
606 MockJournalReplay mock_journal_replay;
607 expect_stop_replay(mock_journaler);
608 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0, true);
94b18763 609 expect_flush_commit_position(mock_journaler);
7c673cae
FG
610 expect_shut_down_journaler(mock_journaler);
611
612 // replay failure should result in replay-restart
613 expect_construct_journaler(mock_journaler);
614 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
615 true, 0);
616 expect_get_max_append_size(mock_journaler, 1 << 16);
617 expect_start_replay(
618 mock_image_ctx, mock_journaler,
619 std::bind(&invoke_replay_complete, _1, 0));
620
621 expect_stop_replay(mock_journaler);
622 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
94b18763 623 expect_flush_commit_position(mock_journaler);
7c673cae
FG
624 expect_start_append(mock_journaler);
625 ASSERT_EQ(0, when_open(mock_journal));
626
627 expect_stop_append(mock_journaler, 0);
628 expect_shut_down_journaler(mock_journaler);
629 ASSERT_EQ(0, when_close(mock_journal));
630}
631
632TEST_F(TestMockJournal, FlushReplayError) {
633 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
634
635 librbd::ImageCtx *ictx;
636 ASSERT_EQ(0, open_image(m_image_name, &ictx));
637
638 MockJournalImageCtx mock_image_ctx(*ictx);
639 MockJournal mock_journal(mock_image_ctx);
640 expect_op_work_queue(mock_image_ctx);
641
642 InSequence seq;
643
644 ::journal::MockJournaler mock_journaler;
645 MockJournalOpenRequest mock_open_request;
646 expect_construct_journaler(mock_journaler);
647 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
648 true, 0);
649 expect_get_max_append_size(mock_journaler, 1 << 16);
650 expect_start_replay(
651 mock_image_ctx, mock_journaler,
652 std::bind(&invoke_replay_ready, _1));
653
654 ::journal::MockReplayEntry mock_replay_entry;
655 MockJournalReplay mock_journal_replay;
656 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
657 expect_replay_process(mock_journal_replay);
658 expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
659 std::bind(&invoke_replay_complete, _1, 0));
660 expect_stop_replay(mock_journaler);
661 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, -EINVAL);
94b18763 662 expect_flush_commit_position(mock_journaler);
7c673cae
FG
663 expect_shut_down_journaler(mock_journaler);
664
665 // replay flush failure should result in replay-restart
666 expect_construct_journaler(mock_journaler);
667 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
668 true, 0);
669 expect_get_max_append_size(mock_journaler, 1 << 16);
670 expect_start_replay(
671 mock_image_ctx, mock_journaler,
672 std::bind(&invoke_replay_complete, _1, 0));
673
674 expect_stop_replay(mock_journaler);
675 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
94b18763 676 expect_flush_commit_position(mock_journaler);
7c673cae
FG
677 expect_start_append(mock_journaler);
678 ASSERT_EQ(0, when_open(mock_journal));
679
680 expect_stop_append(mock_journaler, 0);
681 expect_shut_down_journaler(mock_journaler);
682 ASSERT_EQ(0, when_close(mock_journal));
683}
684
685TEST_F(TestMockJournal, CorruptEntry) {
686 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
687
688 librbd::ImageCtx *ictx;
689 ASSERT_EQ(0, open_image(m_image_name, &ictx));
690
691 MockJournalImageCtx mock_image_ctx(*ictx);
692 MockJournal mock_journal(mock_image_ctx);
693 expect_op_work_queue(mock_image_ctx);
694
695 InSequence seq;
696
697 ::journal::MockJournaler mock_journaler;
698 MockJournalOpenRequest mock_open_request;
699 expect_construct_journaler(mock_journaler);
700 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
701 true, 0);
702 expect_get_max_append_size(mock_journaler, 1 << 16);
703 expect_start_replay(
704 mock_image_ctx, mock_journaler,
705 std::bind(&invoke_replay_ready, _1));
706
707 ::journal::MockReplayEntry mock_replay_entry;
708 MockJournalReplay mock_journal_replay;
709 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
710 EXPECT_CALL(mock_journal_replay, decode(_, _)).WillOnce(Return(-EBADMSG));
711 expect_stop_replay(mock_journaler);
712 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0, true);
94b18763 713 expect_flush_commit_position(mock_journaler);
7c673cae
FG
714 expect_shut_down_journaler(mock_journaler);
715
716 // replay failure should result in replay-restart
717 expect_construct_journaler(mock_journaler);
718 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
719 true, 0);
720 expect_get_max_append_size(mock_journaler, 1 << 16);
721 expect_start_replay(
722 mock_image_ctx, mock_journaler,
723 std::bind(&invoke_replay_complete, _1, 0));
724 expect_stop_replay(mock_journaler);
725 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
94b18763 726 expect_flush_commit_position(mock_journaler);
7c673cae
FG
727 expect_start_append(mock_journaler);
728 ASSERT_EQ(0, when_open(mock_journal));
729
730 expect_stop_append(mock_journaler, -EINVAL);
731 expect_shut_down_journaler(mock_journaler);
732 ASSERT_EQ(-EINVAL, when_close(mock_journal));
733}
734
735TEST_F(TestMockJournal, StopError) {
736 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
737
738 librbd::ImageCtx *ictx;
739 ASSERT_EQ(0, open_image(m_image_name, &ictx));
740
741 MockJournalImageCtx mock_image_ctx(*ictx);
742 MockJournal mock_journal(mock_image_ctx);
743 expect_op_work_queue(mock_image_ctx);
744
745 InSequence seq;
746
747 ::journal::MockJournaler mock_journaler;
748 MockJournalOpenRequest mock_open_request;
749 expect_construct_journaler(mock_journaler);
750 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
751 true, 0);
752 expect_get_max_append_size(mock_journaler, 1 << 16);
753 expect_start_replay(
754 mock_image_ctx, mock_journaler,
755 std::bind(&invoke_replay_complete, _1, 0));
756
757 MockJournalReplay mock_journal_replay;
758 expect_stop_replay(mock_journaler);
759 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
94b18763 760 expect_flush_commit_position(mock_journaler);
7c673cae
FG
761 expect_start_append(mock_journaler);
762 ASSERT_EQ(0, when_open(mock_journal));
763
764 expect_stop_append(mock_journaler, -EINVAL);
765 expect_shut_down_journaler(mock_journaler);
766 ASSERT_EQ(-EINVAL, when_close(mock_journal));
767}
768
769TEST_F(TestMockJournal, ReplayOnDiskPreFlushError) {
770 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
771
772 librbd::ImageCtx *ictx;
773 ASSERT_EQ(0, open_image(m_image_name, &ictx));
774
775 MockJournalImageCtx mock_image_ctx(*ictx);
776 MockJournal mock_journal(mock_image_ctx);
777 expect_op_work_queue(mock_image_ctx);
778
779 InSequence seq;
780 ::journal::MockJournaler mock_journaler;
781 MockJournalOpenRequest mock_open_request;
782 expect_construct_journaler(mock_journaler);
783 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
784 true, 0);
785 expect_get_max_append_size(mock_journaler, 1 << 16);
786
787 expect_start_replay(
788 mock_image_ctx, mock_journaler,
789 std::bind(&invoke_replay_ready, _1));
790
791 ::journal::MockReplayEntry mock_replay_entry;
792 MockJournalReplay mock_journal_replay;
793 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
794
795 EXPECT_CALL(mock_journal_replay, decode(_, _))
796 .WillOnce(Return(0));
797 Context *on_ready;
798 EXPECT_CALL(mock_journal_replay, process(_, _, _))
799 .WillOnce(DoAll(SaveArg<1>(&on_ready),
800 WithArg<2>(Invoke(this, &TestMockJournal::save_commit_context))));
801
802 expect_try_pop_front(mock_image_ctx, mock_journaler, false,
803 mock_replay_entry);
804 expect_stop_replay(mock_journaler);
805 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0, true);
94b18763 806 expect_flush_commit_position(mock_journaler);
7c673cae
FG
807 expect_shut_down_journaler(mock_journaler);
808
809 // replay write-to-disk failure should result in replay-restart
810 expect_construct_journaler(mock_journaler);
811 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
812 true, 0);
813 expect_get_max_append_size(mock_journaler, 1 << 16);
814 expect_start_replay(
815 mock_image_ctx, mock_journaler, {
816 std::bind(&invoke_replay_complete, _1, 0)
817 });
818
819 expect_stop_replay(mock_journaler);
820 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
94b18763 821 expect_flush_commit_position(mock_journaler);
7c673cae
FG
822 expect_start_append(mock_journaler);
823
824 C_SaferCond ctx;
825 mock_journal.open(&ctx);
826
827 // wait for the process callback
828 {
829 Mutex::Locker locker(m_lock);
830 while (m_commit_contexts.empty()) {
831 m_cond.Wait(m_lock);
832 }
833 }
834 on_ready->complete(0);
835
836 // inject RADOS error in the middle of replay
837 Context *on_safe = m_commit_contexts.front();
838 m_commit_contexts.clear();
839 on_safe->complete(-EINVAL);
840
841 // flag the replay as complete
842 m_replay_handler->handle_complete(0);
843
844 ASSERT_EQ(0, ctx.wait());
845
846 expect_stop_append(mock_journaler, 0);
847 expect_shut_down_journaler(mock_journaler);
848 ASSERT_EQ(0, when_close(mock_journal));
849}
850
851TEST_F(TestMockJournal, ReplayOnDiskPostFlushError) {
852 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
853
854 librbd::ImageCtx *ictx;
855 ASSERT_EQ(0, open_image(m_image_name, &ictx));
856
857 MockJournalImageCtx mock_image_ctx(*ictx);
858 MockJournal mock_journal(mock_image_ctx);
859 expect_op_work_queue(mock_image_ctx);
860
861 InSequence seq;
862
863 ::journal::MockJournaler mock_journaler;
864 MockJournalOpenRequest mock_open_request;
865 expect_construct_journaler(mock_journaler);
866 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
867 true, 0);
868 expect_get_max_append_size(mock_journaler, 1 << 16);
869 expect_start_replay(
870 mock_image_ctx, mock_journaler,
871 std::bind(&invoke_replay_ready, _1));
872
873 ::journal::MockReplayEntry mock_replay_entry;
874 MockJournalReplay mock_journal_replay;
875 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
876 expect_replay_process(mock_journal_replay);
877 expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
878 std::bind(&invoke_replay_complete, _1, 0));
879 expect_stop_replay(mock_journaler);
880
881 Context *on_flush = nullptr;
882 EXPECT_CALL(mock_journal_replay, shut_down(false, _))
883 .WillOnce(DoAll(SaveArg<1>(&on_flush),
884 InvokeWithoutArgs(this, &TestMockJournal::wake_up)));
94b18763 885 expect_flush_commit_position(mock_journaler);
7c673cae
FG
886
887 // replay write-to-disk failure should result in replay-restart
888 expect_shut_down_journaler(mock_journaler);
889 expect_construct_journaler(mock_journaler);
890 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
891 true, 0);
892 expect_get_max_append_size(mock_journaler, 1 << 16);
893 expect_start_replay(
894 mock_image_ctx, mock_journaler,
895 std::bind(&invoke_replay_complete, _1, 0));
896
897 expect_stop_replay(mock_journaler);
898 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
94b18763 899 expect_flush_commit_position(mock_journaler);
7c673cae
FG
900 expect_start_append(mock_journaler);
901
902 C_SaferCond ctx;
903 mock_journal.open(&ctx);
904
905 // proceed with the flush
906 {
907 // wait for on_flush callback
908 Mutex::Locker locker(m_lock);
909 while (on_flush == nullptr) {
910 m_cond.Wait(m_lock);
911 }
912 }
913
914 {
915 // wait for the on_safe process callback
916 Mutex::Locker locker(m_lock);
917 while (m_commit_contexts.empty()) {
918 m_cond.Wait(m_lock);
919 }
920 }
921 m_commit_contexts.front()->complete(-EINVAL);
922 m_commit_contexts.clear();
923 on_flush->complete(0);
924
925 ASSERT_EQ(0, ctx.wait());
926
927 expect_stop_append(mock_journaler, 0);
928 expect_shut_down_journaler(mock_journaler);
929 ASSERT_EQ(0, when_close(mock_journal));
930}
931
932TEST_F(TestMockJournal, EventAndIOCommitOrder) {
933 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
934
935 librbd::ImageCtx *ictx;
936 ASSERT_EQ(0, open_image(m_image_name, &ictx));
937
938 MockJournalImageCtx mock_image_ctx(*ictx);
939 MockJournal mock_journal(mock_image_ctx);
940 ::journal::MockJournaler mock_journaler;
941 MockJournalOpenRequest mock_open_request;
942 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
943 BOOST_SCOPE_EXIT_ALL(&) {
944 close_journal(mock_journal, mock_journaler);
945 };
946
947 ::journal::MockFuture mock_future;
948 Context *on_journal_safe1;
949 expect_append_journaler(mock_journaler);
950 expect_wait_future(mock_future, &on_journal_safe1);
b32b8144 951 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, nullptr, 0));
7c673cae
FG
952 mock_journal.get_work_queue()->drain();
953
954 Context *on_journal_safe2;
955 expect_append_journaler(mock_journaler);
956 expect_wait_future(mock_future, &on_journal_safe2);
b32b8144 957 ASSERT_EQ(2U, when_append_io_event(mock_image_ctx, mock_journal, nullptr, 0));
7c673cae
FG
958 mock_journal.get_work_queue()->drain();
959
960 // commit journal event followed by IO event (standard)
961 on_journal_safe1->complete(0);
962 ictx->op_work_queue->drain();
963 expect_future_committed(mock_journaler);
964 mock_journal.commit_io_event(1U, 0);
965
966 // commit IO event followed by journal event (cache overwrite)
967 mock_journal.commit_io_event(2U, 0);
968 expect_future_committed(mock_journaler);
969
970 C_SaferCond event_ctx;
971 mock_journal.wait_event(2U, &event_ctx);
972 on_journal_safe2->complete(0);
973 ictx->op_work_queue->drain();
974 ASSERT_EQ(0, event_ctx.wait());
975
976 expect_shut_down_journaler(mock_journaler);
977}
978
979TEST_F(TestMockJournal, AppendWriteEvent) {
980 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
981
982 librbd::ImageCtx *ictx;
983 ASSERT_EQ(0, open_image(m_image_name, &ictx));
984
985 MockJournalImageCtx mock_image_ctx(*ictx);
986 MockJournal mock_journal(mock_image_ctx);
987 ::journal::MockJournaler mock_journaler;
988 MockJournalOpenRequest mock_open_request;
989 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
990 BOOST_SCOPE_EXIT_ALL(&) {
991 close_journal(mock_journal, mock_journaler);
992 };
993
994 InSequence seq;
995
996 ::journal::MockFuture mock_future;
997 Context *on_journal_safe = nullptr;
998 expect_append_journaler(mock_journaler);
999 expect_append_journaler(mock_journaler);
1000 expect_append_journaler(mock_journaler);
1001 expect_wait_future(mock_future, &on_journal_safe);
1002 ASSERT_EQ(1U, when_append_write_event(mock_image_ctx, mock_journal, 1 << 17));
1003 mock_journal.get_work_queue()->drain();
1004
1005 on_journal_safe->complete(0);
1006 C_SaferCond event_ctx;
1007 mock_journal.wait_event(1U, &event_ctx);
1008 ASSERT_EQ(0, event_ctx.wait());
1009
1010 expect_future_committed(mock_journaler);
1011 expect_future_committed(mock_journaler);
1012 expect_future_committed(mock_journaler);
1013 mock_journal.commit_io_event(1U, 0);
1014 ictx->op_work_queue->drain();
1015
1016 expect_shut_down_journaler(mock_journaler);
1017}
1018
1019TEST_F(TestMockJournal, EventCommitError) {
1020 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1021
1022 librbd::ImageCtx *ictx;
1023 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1024
1025 MockJournalImageCtx mock_image_ctx(*ictx);
1026 MockJournal mock_journal(mock_image_ctx);
1027 ::journal::MockJournaler mock_journaler;
1028 MockJournalOpenRequest mock_open_request;
1029 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
1030 BOOST_SCOPE_EXIT_ALL(&) {
1031 close_journal(mock_journal, mock_journaler);
1032 };
1033
1034 C_SaferCond object_request_ctx;
b32b8144
FG
1035 auto object_request = new io::ObjectDiscardRequest<>(
1036 ictx, "oid", 0, 0, ictx->layout.object_size, {}, true, true, {},
1037 &object_request_ctx);
7c673cae
FG
1038
1039 ::journal::MockFuture mock_future;
1040 Context *on_journal_safe;
1041 expect_append_journaler(mock_journaler);
1042 expect_wait_future(mock_future, &on_journal_safe);
1043 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal,
b32b8144 1044 object_request, 0));
7c673cae
FG
1045 mock_journal.get_work_queue()->drain();
1046
1047 // commit the event in the journal w/o waiting writeback
1048 expect_future_committed(mock_journaler);
1049 on_journal_safe->complete(-EINVAL);
1050 ASSERT_EQ(-EINVAL, object_request_ctx.wait());
1051
1052 // cache should receive the error after attempting writeback
1053 expect_future_is_valid(mock_future);
1054 C_SaferCond flush_ctx;
1055 mock_journal.flush_event(1U, &flush_ctx);
1056 ASSERT_EQ(-EINVAL, flush_ctx.wait());
1057
1058 expect_shut_down_journaler(mock_journaler);
1059}
1060
1061TEST_F(TestMockJournal, EventCommitErrorWithPendingWriteback) {
1062 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1063
1064 librbd::ImageCtx *ictx;
1065 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1066
1067 MockJournalImageCtx mock_image_ctx(*ictx);
1068 MockJournal mock_journal(mock_image_ctx);
1069 ::journal::MockJournaler mock_journaler;
1070 MockJournalOpenRequest mock_open_request;
1071 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
1072 BOOST_SCOPE_EXIT_ALL(&) {
1073 close_journal(mock_journal, mock_journaler);
1074 };
1075
1076 C_SaferCond object_request_ctx;
b32b8144
FG
1077 auto object_request = new io::ObjectDiscardRequest<>(
1078 ictx, "oid", 0, 0, ictx->layout.object_size, {}, true, true, {},
1079 &object_request_ctx);
7c673cae
FG
1080
1081 ::journal::MockFuture mock_future;
1082 Context *on_journal_safe;
1083 expect_append_journaler(mock_journaler);
1084 expect_wait_future(mock_future, &on_journal_safe);
1085 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal,
b32b8144 1086 object_request, 0));
7c673cae
FG
1087 mock_journal.get_work_queue()->drain();
1088
1089 expect_future_is_valid(mock_future);
1090 C_SaferCond flush_ctx;
1091 mock_journal.flush_event(1U, &flush_ctx);
1092
1093 // commit the event in the journal w/ waiting cache writeback
1094 expect_future_committed(mock_journaler);
1095 on_journal_safe->complete(-EINVAL);
1096 ASSERT_EQ(-EINVAL, object_request_ctx.wait());
1097
1098 // cache should receive the error if waiting
1099 ASSERT_EQ(-EINVAL, flush_ctx.wait());
1100
1101 expect_shut_down_journaler(mock_journaler);
1102}
1103
1104TEST_F(TestMockJournal, IOCommitError) {
1105 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1106
1107 librbd::ImageCtx *ictx;
1108 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1109
1110 MockJournalImageCtx mock_image_ctx(*ictx);
1111 MockJournal mock_journal(mock_image_ctx);
1112 ::journal::MockJournaler mock_journaler;
1113 MockJournalOpenRequest mock_open_request;
1114 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
1115 BOOST_SCOPE_EXIT_ALL(&) {
1116 close_journal(mock_journal, mock_journaler);
1117 };
1118
1119 ::journal::MockFuture mock_future;
1120 Context *on_journal_safe;
1121 expect_append_journaler(mock_journaler);
1122 expect_wait_future(mock_future, &on_journal_safe);
b32b8144 1123 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, nullptr, 0));
7c673cae
FG
1124 mock_journal.get_work_queue()->drain();
1125
1126 // failed IO remains uncommitted in journal
1127 on_journal_safe->complete(0);
1128 ictx->op_work_queue->drain();
1129 mock_journal.commit_io_event(1U, -EINVAL);
1130
1131 expect_shut_down_journaler(mock_journaler);
1132}
1133
b32b8144
FG
1134TEST_F(TestMockJournal, IOCommitErrorFiltered) {
1135 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1136
1137 librbd::ImageCtx *ictx;
1138 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1139
1140 MockJournalImageCtx mock_image_ctx(*ictx);
1141 MockJournal mock_journal(mock_image_ctx);
1142 ::journal::MockJournaler mock_journaler;
1143 MockJournalOpenRequest mock_open_request;
1144 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
1145 BOOST_SCOPE_EXIT_ALL(&) {
1146 close_journal(mock_journal, mock_journaler);
1147 };
1148
1149 ::journal::MockFuture mock_future;
1150 Context *on_journal_safe;
1151 expect_append_journaler(mock_journaler);
1152 expect_wait_future(mock_future, &on_journal_safe);
1153 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, nullptr,
1154 -EILSEQ));
1155 mock_journal.get_work_queue()->drain();
1156
1157 // filter failed IO committed in journal
1158 on_journal_safe->complete(0);
1159 ictx->op_work_queue->drain();
1160 expect_future_committed(mock_journaler);
1161 mock_journal.commit_io_event(1U, -EILSEQ);
1162
1163 expect_shut_down_journaler(mock_journaler);
1164}
1165
7c673cae
FG
1166TEST_F(TestMockJournal, FlushCommitPosition) {
1167 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1168
1169 librbd::ImageCtx *ictx;
1170 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1171
1172 MockJournalImageCtx mock_image_ctx(*ictx);
1173 MockJournal mock_journal(mock_image_ctx);
1174 ::journal::MockJournaler mock_journaler;
1175 MockJournalOpenRequest mock_open_request;
1176 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
1177 BOOST_SCOPE_EXIT_ALL(&) {
1178 close_journal(mock_journal, mock_journaler);
1179 };
1180
1181 expect_flush_commit_position(mock_journaler);
1182 C_SaferCond ctx;
1183 mock_journal.flush_commit_position(&ctx);
1184 ASSERT_EQ(0, ctx.wait());
1185
1186 expect_shut_down_journaler(mock_journaler);
1187}
1188
1189TEST_F(TestMockJournal, ExternalReplay) {
1190 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1191
1192 librbd::ImageCtx *ictx;
1193 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1194
1195 MockJournalImageCtx mock_image_ctx(*ictx);
1196 MockJournal mock_journal(mock_image_ctx);
1197 ::journal::MockJournaler mock_journaler;
1198 MockJournalOpenRequest mock_open_request;
1199 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
1200 BOOST_SCOPE_EXIT_ALL(&) {
1201 close_journal(mock_journal, mock_journaler);
1202 };
1203
1204 InSequence seq;
1205 expect_stop_append(mock_journaler, 0);
1206 expect_start_append(mock_journaler);
1207 expect_shut_down_journaler(mock_journaler);
1208
1209 C_SaferCond start_ctx;
1210
1211 journal::Replay<MockJournalImageCtx> *journal_replay = nullptr;
1212 mock_journal.start_external_replay(&journal_replay, &start_ctx);
1213 ASSERT_EQ(0, start_ctx.wait());
1214
1215 mock_journal.stop_external_replay();
1216}
1217
1218TEST_F(TestMockJournal, ExternalReplayFailure) {
1219 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1220
1221 librbd::ImageCtx *ictx;
1222 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1223
1224 MockJournalImageCtx mock_image_ctx(*ictx);
1225 MockJournal mock_journal(mock_image_ctx);
1226 ::journal::MockJournaler mock_journaler;
1227 MockJournalOpenRequest mock_open_request;
1228 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
1229 BOOST_SCOPE_EXIT_ALL(&) {
1230 close_journal(mock_journal, mock_journaler);
1231 };
1232
1233 InSequence seq;
1234 expect_stop_append(mock_journaler, -EINVAL);
1235 expect_start_append(mock_journaler);
1236 expect_shut_down_journaler(mock_journaler);
1237
1238 C_SaferCond start_ctx;
1239
1240 journal::Replay<MockJournalImageCtx> *journal_replay = nullptr;
1241 mock_journal.start_external_replay(&journal_replay, &start_ctx);
1242 ASSERT_EQ(-EINVAL, start_ctx.wait());
1243}
1244
1245TEST_F(TestMockJournal, AppendDisabled) {
1246 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1247
1248 librbd::ImageCtx *ictx;
1249 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1250
1251 MockJournalImageCtx mock_image_ctx(*ictx);
1252 MockJournal mock_journal(mock_image_ctx);
1253 MockJournalPolicy mock_journal_policy;
1254
1255 ::journal::MockJournaler mock_journaler;
1256 MockJournalOpenRequest mock_open_request;
1257 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
1258 BOOST_SCOPE_EXIT_ALL(&) {
1259 close_journal(mock_journal, mock_journaler);
1260 };
1261
1262 InSequence seq;
1263 RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
1264 EXPECT_CALL(mock_image_ctx, get_journal_policy()).WillOnce(
1265 Return(ictx->get_journal_policy()));
1266 ASSERT_TRUE(mock_journal.is_journal_appending());
1267
1268 EXPECT_CALL(mock_image_ctx, get_journal_policy()).WillOnce(
1269 Return(&mock_journal_policy));
1270 EXPECT_CALL(mock_journal_policy, append_disabled()).WillOnce(Return(true));
1271 ASSERT_FALSE(mock_journal.is_journal_appending());
1272
1273 expect_shut_down_journaler(mock_journaler);
1274}
1275
1276TEST_F(TestMockJournal, CloseListenerEvent) {
1277 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1278
1279 librbd::ImageCtx *ictx;
1280 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1281
1282 MockJournalImageCtx mock_image_ctx(*ictx);
1283 MockJournal mock_journal(mock_image_ctx);
1284 ::journal::MockJournaler mock_journaler;
1285 MockJournalOpenRequest mock_open_request;
1286 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request);
1287
1288 struct Listener : public journal::Listener {
1289 C_SaferCond ctx;
1290 void handle_close() override {
1291 ctx.complete(0);
1292 }
1293 void handle_resync() override {
1294 ADD_FAILURE() << "unexpected resync request";
1295 }
1296 void handle_promoted() override {
1297 ADD_FAILURE() << "unexpected promotion event";
1298 }
1299 } listener;
1300 mock_journal.add_listener(&listener);
1301
1302 expect_shut_down_journaler(mock_journaler);
1303 close_journal(mock_journal, mock_journaler);
1304
1305 ASSERT_EQ(0, listener.ctx.wait());
1306 mock_journal.remove_listener(&listener);
1307}
1308
1309TEST_F(TestMockJournal, ResyncRequested) {
1310 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1311
1312 librbd::ImageCtx *ictx;
1313 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1314
1315 MockJournalImageCtx mock_image_ctx(*ictx);
1316 MockJournal mock_journal(mock_image_ctx);
1317 ::journal::MockJournaler mock_journaler;
1318 MockJournalOpenRequest mock_open_request;
1319 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request,
1320 false);
1321
1322 struct Listener : public journal::Listener {
1323 C_SaferCond ctx;
1324 void handle_close() override {
1325 ADD_FAILURE() << "unexpected close action";
1326 }
1327 void handle_resync() override {
1328 ctx.complete(0);
1329 }
1330 void handle_promoted() override {
1331 ADD_FAILURE() << "unexpected promotion event";
1332 }
1333 } listener;
1334 mock_journal.add_listener(&listener);
1335
1336 BOOST_SCOPE_EXIT_ALL(&) {
1337 mock_journal.remove_listener(&listener);
1338 close_journal(mock_journal, mock_journaler);
1339 };
1340
1341 InSequence seq;
1342
1343 journal::TagData tag_data;
1344 tag_data.mirror_uuid = Journal<>::LOCAL_MIRROR_UUID;
1345
1346 bufferlist tag_data_bl;
1347 ::encode(tag_data, tag_data_bl);
1348 expect_get_journaler_tags(mock_image_ctx, mock_journaler, 0,
1349 {{0, 0, tag_data_bl}}, 0);
1350
1351 journal::ImageClientMeta image_client_meta;
1352 image_client_meta.tag_class = 0;
1353 image_client_meta.resync_requested = true;
1354 expect_get_journaler_cached_client(mock_journaler, image_client_meta, 0);
1355 expect_shut_down_journaler(mock_journaler);
1356
1357 m_listener->handle_update(nullptr);
1358 ASSERT_EQ(0, listener.ctx.wait());
1359}
1360
1361TEST_F(TestMockJournal, ForcePromoted) {
1362 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1363
1364 librbd::ImageCtx *ictx;
1365 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1366
1367 MockJournalImageCtx mock_image_ctx(*ictx);
1368 MockJournal mock_journal(mock_image_ctx);
1369 ::journal::MockJournaler mock_journaler;
1370 MockJournalOpenRequest mock_open_request;
1371 open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request,
1372 false);
1373
1374 struct Listener : public journal::Listener {
1375 C_SaferCond ctx;
1376 void handle_close() override {
1377 ADD_FAILURE() << "unexpected close action";
1378 }
1379 void handle_resync() override {
1380 ADD_FAILURE() << "unexpected resync event";
1381 }
1382 void handle_promoted() override {
1383 ctx.complete(0);
1384 }
1385 } listener;
1386 mock_journal.add_listener(&listener);
1387
1388 BOOST_SCOPE_EXIT_ALL(&) {
1389 mock_journal.remove_listener(&listener);
1390 close_journal(mock_journal, mock_journaler);
1391 };
1392
1393 InSequence seq;
1394
1395 journal::TagData tag_data;
1396 tag_data.mirror_uuid = Journal<>::LOCAL_MIRROR_UUID;
1397
1398 bufferlist tag_data_bl;
1399 ::encode(tag_data, tag_data_bl);
1400 expect_get_journaler_tags(mock_image_ctx, mock_journaler, 0,
1401 {{100, 0, tag_data_bl}}, 0);
1402
1403 journal::ImageClientMeta image_client_meta;
1404 image_client_meta.tag_class = 0;
1405 expect_get_journaler_cached_client(mock_journaler, image_client_meta, 0);
1406 expect_shut_down_journaler(mock_journaler);
1407
1408 m_listener->handle_update(nullptr);
1409 ASSERT_EQ(0, listener.ctx.wait());
1410}
1411
1412} // namespace librbd