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/MockJournalPolicy.h"
8 #include "cls/rbd/cls_rbd_client.h"
9 #include "librbd/internal.h"
10 #include "librbd/image/SetFlagsRequest.h"
11 #include "librbd/io/AioCompletion.h"
12 #include "librbd/mirror/DisableRequest.h"
13 #include "librbd/journal/RemoveRequest.h"
14 #include "librbd/journal/StandardPolicy.h"
15 #include "librbd/journal/Types.h"
16 #include "librbd/object_map/RemoveRequest.h"
17 #include "librbd/operation/DisableFeaturesRequest.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
25 struct MockOperationImageCtx
: public MockImageCtx
{
26 MockOperationImageCtx(librbd::ImageCtx
& image_ctx
) : MockImageCtx(image_ctx
) {
30 } // anonymous namespace
35 class SetFlagsRequest
<MockOperationImageCtx
> {
37 static SetFlagsRequest
*s_instance
;
38 Context
*on_finish
= nullptr;
40 static SetFlagsRequest
*create(MockOperationImageCtx
*image_ctx
, uint64_t flags
,
41 uint64_t mask
, Context
*on_finish
) {
42 ceph_assert(s_instance
!= nullptr);
43 s_instance
->on_finish
= on_finish
;
51 MOCK_METHOD0(send
, void());
54 SetFlagsRequest
<MockOperationImageCtx
> *SetFlagsRequest
<MockOperationImageCtx
>::s_instance
;
61 class RemoveRequest
<MockOperationImageCtx
> {
63 static RemoveRequest
*s_instance
;
64 Context
*on_finish
= nullptr;
66 static RemoveRequest
*create(IoCtx
&ioctx
, const std::string
&imageid
,
67 const std::string
&client_id
,
68 MockContextWQ
*op_work_queue
,
70 ceph_assert(s_instance
!= nullptr);
71 s_instance
->on_finish
= on_finish
;
79 MOCK_METHOD0(send
, void());
82 RemoveRequest
<MockOperationImageCtx
> *RemoveRequest
<MockOperationImageCtx
>::s_instance
;
85 class StandardPolicy
<MockOperationImageCtx
> : public MockJournalPolicy
{
87 StandardPolicy(MockOperationImageCtx
* image_ctx
) {
91 } // namespace journal
96 class DisableRequest
<MockOperationImageCtx
> {
98 static DisableRequest
*s_instance
;
99 Context
*on_finish
= nullptr;
101 static DisableRequest
*create(MockOperationImageCtx
*image_ctx
, bool force
,
102 bool remove
, Context
*on_finish
) {
103 ceph_assert(s_instance
!= nullptr);
104 s_instance
->on_finish
= on_finish
;
112 MOCK_METHOD0(send
, void());
115 DisableRequest
<MockOperationImageCtx
> *DisableRequest
<MockOperationImageCtx
>::s_instance
;
117 } // namespace mirror
119 namespace object_map
{
122 class RemoveRequest
<MockOperationImageCtx
> {
124 static RemoveRequest
*s_instance
;
125 Context
*on_finish
= nullptr;
127 static RemoveRequest
*create(MockOperationImageCtx
*image_ctx
, Context
*on_finish
) {
128 ceph_assert(s_instance
!= nullptr);
129 s_instance
->on_finish
= on_finish
;
137 MOCK_METHOD0(send
, void());
140 RemoveRequest
<MockOperationImageCtx
> *RemoveRequest
<MockOperationImageCtx
>::s_instance
;
142 } // namespace object_map
145 struct AsyncRequest
<MockOperationImageCtx
> : public AsyncRequest
<MockImageCtx
> {
146 MockOperationImageCtx
&m_image_ctx
;
148 AsyncRequest(MockOperationImageCtx
&image_ctx
, Context
*on_finish
)
149 : AsyncRequest
<MockImageCtx
>(image_ctx
, on_finish
), m_image_ctx(image_ctx
) {
153 } // namespace librbd
155 // template definitions
156 #include "librbd/AsyncRequest.cc"
157 #include "librbd/AsyncObjectThrottle.cc"
158 #include "librbd/operation/Request.cc"
159 #include "librbd/operation/DisableFeaturesRequest.cc"
162 namespace operation
{
164 using ::testing::Invoke
;
165 using ::testing::Return
;
166 using ::testing::WithArg
;
169 class TestMockOperationDisableFeaturesRequest
: public TestMockFixture
{
171 typedef librbd::image::SetFlagsRequest
<MockOperationImageCtx
> MockSetFlagsRequest
;
172 typedef librbd::journal::RemoveRequest
<MockOperationImageCtx
> MockRemoveJournalRequest
;
173 typedef librbd::mirror::DisableRequest
<MockOperationImageCtx
> MockDisableMirrorRequest
;
174 typedef librbd::object_map::RemoveRequest
<MockOperationImageCtx
> MockRemoveObjectMapRequest
;
175 typedef DisableFeaturesRequest
<MockOperationImageCtx
> MockDisableFeaturesRequest
;
177 class MirrorModeEnabler
{
179 MirrorModeEnabler(librados::IoCtx
&ioctx
, cls::rbd::MirrorMode mirror_mode
)
180 : m_ioctx(ioctx
), m_mirror_mode(mirror_mode
) {
181 EXPECT_EQ(0, librbd::cls_client::mirror_uuid_set(&m_ioctx
, "test-uuid"));
182 EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(&m_ioctx
, m_mirror_mode
));
185 ~MirrorModeEnabler() {
186 EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
187 &m_ioctx
, cls::rbd::MIRROR_MODE_DISABLED
));
190 librados::IoCtx
&m_ioctx
;
191 cls::rbd::MirrorMode m_mirror_mode
;
194 void expect_prepare_lock(MockOperationImageCtx
&mock_image_ctx
) {
195 EXPECT_CALL(*mock_image_ctx
.state
, prepare_lock(_
))
196 .WillOnce(Invoke([](Context
*on_ready
) {
197 on_ready
->complete(0);
199 expect_op_work_queue(mock_image_ctx
);
202 void expect_handle_prepare_lock_complete(MockOperationImageCtx
&mock_image_ctx
) {
203 EXPECT_CALL(*mock_image_ctx
.state
, handle_prepare_lock_complete());
206 void expect_block_writes(MockOperationImageCtx
&mock_image_ctx
) {
207 EXPECT_CALL(*mock_image_ctx
.io_work_queue
, block_writes(_
))
208 .WillOnce(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
));
211 void expect_unblock_writes(MockOperationImageCtx
&mock_image_ctx
) {
212 EXPECT_CALL(*mock_image_ctx
.io_work_queue
, unblock_writes()).Times(1);
215 void expect_verify_lock_ownership(MockOperationImageCtx
&mock_image_ctx
) {
216 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
217 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, is_lock_owner())
218 .WillRepeatedly(Return(true));
222 void expect_block_requests(MockOperationImageCtx
&mock_image_ctx
) {
223 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
224 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, block_requests(0)).Times(1);
228 void expect_unblock_requests(MockOperationImageCtx
&mock_image_ctx
) {
229 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
230 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, unblock_requests()).Times(1);
234 void expect_set_flags_request_send(
235 MockOperationImageCtx
&mock_image_ctx
,
236 MockSetFlagsRequest
&mock_set_flags_request
, int r
) {
237 EXPECT_CALL(mock_set_flags_request
, send())
238 .WillOnce(FinishRequest(&mock_set_flags_request
, r
,
242 void expect_disable_mirror_request_send(
243 MockOperationImageCtx
&mock_image_ctx
,
244 MockDisableMirrorRequest
&mock_disable_mirror_request
, int r
) {
245 EXPECT_CALL(mock_disable_mirror_request
, send())
246 .WillOnce(FinishRequest(&mock_disable_mirror_request
, r
,
250 void expect_close_journal(MockOperationImageCtx
&mock_image_ctx
, int r
) {
251 EXPECT_CALL(*mock_image_ctx
.journal
, close(_
))
252 .WillOnce(Invoke([&mock_image_ctx
, r
](Context
*on_finish
) {
253 mock_image_ctx
.journal
= nullptr;
254 mock_image_ctx
.image_ctx
->op_work_queue
->queue(on_finish
, r
);
258 void expect_remove_journal_request_send(
259 MockOperationImageCtx
&mock_image_ctx
,
260 MockRemoveJournalRequest
&mock_remove_journal_request
, int r
) {
261 EXPECT_CALL(mock_remove_journal_request
, send())
262 .WillOnce(FinishRequest(&mock_remove_journal_request
, r
,
266 void expect_remove_object_map_request_send(
267 MockOperationImageCtx
&mock_image_ctx
,
268 MockRemoveObjectMapRequest
&mock_remove_object_map_request
, int r
) {
269 EXPECT_CALL(mock_remove_object_map_request
, send())
270 .WillOnce(FinishRequest(&mock_remove_object_map_request
, r
,
274 void expect_notify_update(MockOperationImageCtx
&mock_image_ctx
) {
275 EXPECT_CALL(mock_image_ctx
, notify_update(_
))
276 .WillOnce(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
));
281 TEST_F(TestMockOperationDisableFeaturesRequest
, All
) {
284 librbd::ImageCtx
*ictx
;
285 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
288 ASSERT_EQ(0, librbd::get_features(ictx
, &features
));
290 uint64_t features_to_disable
= RBD_FEATURES_MUTABLE
& features
;
292 REQUIRE(features_to_disable
);
294 MockOperationImageCtx
mock_image_ctx(*ictx
);
295 MockExclusiveLock mock_exclusive_lock
;
296 MockJournal mock_journal
;
297 MockObjectMap mock_object_map
;
298 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
301 expect_verify_lock_ownership(mock_image_ctx
);
303 MockSetFlagsRequest mock_set_flags_request
;
304 MockRemoveJournalRequest mock_remove_journal_request
;
305 MockDisableMirrorRequest mock_disable_mirror_request
;
306 MockRemoveObjectMapRequest mock_remove_object_map_request
;
308 ::testing::InSequence seq
;
309 expect_prepare_lock(mock_image_ctx
);
310 expect_block_writes(mock_image_ctx
);
311 if (mock_image_ctx
.journal
!= nullptr) {
312 expect_is_journal_replaying(*mock_image_ctx
.journal
);
314 expect_block_requests(mock_image_ctx
);
315 if (features_to_disable
& RBD_FEATURE_JOURNALING
) {
316 expect_disable_mirror_request_send(mock_image_ctx
,
317 mock_disable_mirror_request
, 0);
318 expect_close_journal(mock_image_ctx
, 0);
319 expect_remove_journal_request_send(mock_image_ctx
,
320 mock_remove_journal_request
, 0);
322 if (features_to_disable
& RBD_FEATURE_OBJECT_MAP
) {
323 expect_remove_object_map_request_send(mock_image_ctx
,
324 mock_remove_object_map_request
, 0);
326 if (features_to_disable
& (RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
)) {
327 expect_set_flags_request_send(mock_image_ctx
,
328 mock_set_flags_request
, 0);
330 expect_notify_update(mock_image_ctx
);
331 expect_unblock_requests(mock_image_ctx
);
332 expect_unblock_writes(mock_image_ctx
);
333 expect_handle_prepare_lock_complete(mock_image_ctx
);
335 C_SaferCond cond_ctx
;
336 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
337 mock_image_ctx
, &cond_ctx
, 0, features_to_disable
, false);
339 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
342 ASSERT_EQ(0, cond_ctx
.wait());
345 TEST_F(TestMockOperationDisableFeaturesRequest
, ObjectMap
) {
346 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
348 librbd::ImageCtx
*ictx
;
349 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
351 MockOperationImageCtx
mock_image_ctx(*ictx
);
352 MockExclusiveLock mock_exclusive_lock
;
353 MockJournal mock_journal
;
354 MockObjectMap mock_object_map
;
355 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
358 expect_verify_lock_ownership(mock_image_ctx
);
360 MockSetFlagsRequest mock_set_flags_request
;
361 MockRemoveObjectMapRequest mock_remove_object_map_request
;
363 ::testing::InSequence seq
;
364 expect_prepare_lock(mock_image_ctx
);
365 expect_block_writes(mock_image_ctx
);
366 if (mock_image_ctx
.journal
!= nullptr) {
367 expect_is_journal_replaying(*mock_image_ctx
.journal
);
369 expect_block_requests(mock_image_ctx
);
370 expect_append_op_event(mock_image_ctx
, true, 0);
371 expect_remove_object_map_request_send(mock_image_ctx
,
372 mock_remove_object_map_request
, 0);
373 expect_set_flags_request_send(mock_image_ctx
,
374 mock_set_flags_request
, 0);
375 expect_notify_update(mock_image_ctx
);
376 expect_unblock_requests(mock_image_ctx
);
377 expect_unblock_writes(mock_image_ctx
);
378 expect_handle_prepare_lock_complete(mock_image_ctx
);
379 expect_commit_op_event(mock_image_ctx
, 0);
381 C_SaferCond cond_ctx
;
382 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
383 mock_image_ctx
, &cond_ctx
, 0,
384 RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
, false);
386 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
389 ASSERT_EQ(0, cond_ctx
.wait());
392 TEST_F(TestMockOperationDisableFeaturesRequest
, ObjectMapError
) {
393 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
395 librbd::ImageCtx
*ictx
;
396 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
398 MockOperationImageCtx
mock_image_ctx(*ictx
);
399 MockExclusiveLock mock_exclusive_lock
;
400 MockJournal mock_journal
;
401 MockObjectMap mock_object_map
;
402 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
405 expect_verify_lock_ownership(mock_image_ctx
);
407 MockSetFlagsRequest mock_set_flags_request
;
408 MockRemoveObjectMapRequest mock_remove_object_map_request
;
410 ::testing::InSequence seq
;
411 expect_prepare_lock(mock_image_ctx
);
412 expect_block_writes(mock_image_ctx
);
413 if (mock_image_ctx
.journal
!= nullptr) {
414 expect_is_journal_replaying(*mock_image_ctx
.journal
);
416 expect_block_requests(mock_image_ctx
);
417 expect_append_op_event(mock_image_ctx
, true, 0);
418 expect_remove_object_map_request_send(mock_image_ctx
,
419 mock_remove_object_map_request
, -EINVAL
);
420 expect_unblock_requests(mock_image_ctx
);
421 expect_unblock_writes(mock_image_ctx
);
422 expect_handle_prepare_lock_complete(mock_image_ctx
);
423 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
425 C_SaferCond cond_ctx
;
426 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
427 mock_image_ctx
, &cond_ctx
, 0,
428 RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
, false);
430 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
433 ASSERT_EQ(-EINVAL
, cond_ctx
.wait());
436 TEST_F(TestMockOperationDisableFeaturesRequest
, Mirroring
) {
437 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
439 MirrorModeEnabler
mirror_mode_enabler(m_ioctx
, cls::rbd::MIRROR_MODE_POOL
);
441 librbd::ImageCtx
*ictx
;
442 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
444 MockOperationImageCtx
mock_image_ctx(*ictx
);
445 MockExclusiveLock mock_exclusive_lock
;
446 MockJournal mock_journal
;
447 MockObjectMap mock_object_map
;
448 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
451 expect_verify_lock_ownership(mock_image_ctx
);
453 MockRemoveJournalRequest mock_remove_journal_request
;
454 MockDisableMirrorRequest mock_disable_mirror_request
;
456 ::testing::InSequence seq
;
457 expect_prepare_lock(mock_image_ctx
);
458 expect_block_writes(mock_image_ctx
);
459 expect_is_journal_replaying(*mock_image_ctx
.journal
);
460 expect_block_requests(mock_image_ctx
);
461 expect_disable_mirror_request_send(mock_image_ctx
,
462 mock_disable_mirror_request
, 0);
463 expect_close_journal(mock_image_ctx
, 0);
464 expect_remove_journal_request_send(mock_image_ctx
,
465 mock_remove_journal_request
, 0);
466 expect_notify_update(mock_image_ctx
);
467 expect_unblock_requests(mock_image_ctx
);
468 expect_unblock_writes(mock_image_ctx
);
469 expect_handle_prepare_lock_complete(mock_image_ctx
);
471 C_SaferCond cond_ctx
;
472 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
473 mock_image_ctx
, &cond_ctx
, 0, RBD_FEATURE_JOURNALING
, false);
475 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
478 ASSERT_EQ(0, cond_ctx
.wait());
481 TEST_F(TestMockOperationDisableFeaturesRequest
, MirroringError
) {
482 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
484 librbd::ImageCtx
*ictx
;
485 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
487 MockOperationImageCtx
mock_image_ctx(*ictx
);
488 MockExclusiveLock mock_exclusive_lock
;
489 MockJournal mock_journal
;
490 MockObjectMap mock_object_map
;
491 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
494 expect_verify_lock_ownership(mock_image_ctx
);
496 MockRemoveJournalRequest mock_remove_journal_request
;
497 MockDisableMirrorRequest mock_disable_mirror_request
;
499 ::testing::InSequence seq
;
500 expect_prepare_lock(mock_image_ctx
);
501 expect_block_writes(mock_image_ctx
);
502 expect_is_journal_replaying(*mock_image_ctx
.journal
);
503 expect_block_requests(mock_image_ctx
);
504 expect_disable_mirror_request_send(mock_image_ctx
,
505 mock_disable_mirror_request
, -EINVAL
);
506 expect_close_journal(mock_image_ctx
, 0);
507 expect_remove_journal_request_send(mock_image_ctx
,
508 mock_remove_journal_request
, 0);
509 expect_notify_update(mock_image_ctx
);
510 expect_unblock_requests(mock_image_ctx
);
511 expect_unblock_writes(mock_image_ctx
);
512 expect_handle_prepare_lock_complete(mock_image_ctx
);
514 C_SaferCond cond_ctx
;
515 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
516 mock_image_ctx
, &cond_ctx
, 0, RBD_FEATURE_JOURNALING
, false);
518 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
521 ASSERT_EQ(0, cond_ctx
.wait());
524 } // namespace operation
525 } // namespace librbd