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/Journal.h"
11 #include "librbd/image/SetFlagsRequest.h"
12 #include "librbd/io/AioCompletion.h"
13 #include "librbd/mirror/DisableRequest.h"
14 #include "librbd/journal/RemoveRequest.h"
15 #include "librbd/journal/StandardPolicy.h"
16 #include "librbd/journal/Types.h"
17 #include "librbd/journal/TypeTraits.h"
18 #include "librbd/object_map/RemoveRequest.h"
19 #include "librbd/operation/DisableFeaturesRequest.h"
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
27 struct MockOperationImageCtx
: public MockImageCtx
{
28 MockOperationImageCtx(librbd::ImageCtx
& image_ctx
) : MockImageCtx(image_ctx
) {
32 } // anonymous namespace
35 struct Journal
<MockOperationImageCtx
> {
36 static void get_work_queue(CephContext
*, MockContextWQ
**) {
43 class SetFlagsRequest
<MockOperationImageCtx
> {
45 static SetFlagsRequest
*s_instance
;
46 Context
*on_finish
= nullptr;
48 static SetFlagsRequest
*create(MockOperationImageCtx
*image_ctx
, uint64_t flags
,
49 uint64_t mask
, Context
*on_finish
) {
50 ceph_assert(s_instance
!= nullptr);
51 s_instance
->on_finish
= on_finish
;
59 MOCK_METHOD0(send
, void());
62 SetFlagsRequest
<MockOperationImageCtx
> *SetFlagsRequest
<MockOperationImageCtx
>::s_instance
;
69 class RemoveRequest
<MockOperationImageCtx
> {
71 static RemoveRequest
*s_instance
;
72 Context
*on_finish
= nullptr;
74 static RemoveRequest
*create(IoCtx
&ioctx
, const std::string
&imageid
,
75 const std::string
&client_id
,
76 MockContextWQ
*op_work_queue
,
78 ceph_assert(s_instance
!= nullptr);
79 s_instance
->on_finish
= on_finish
;
87 MOCK_METHOD0(send
, void());
90 RemoveRequest
<MockOperationImageCtx
> *RemoveRequest
<MockOperationImageCtx
>::s_instance
;
93 class StandardPolicy
<MockOperationImageCtx
> : public MockJournalPolicy
{
95 StandardPolicy(MockOperationImageCtx
* image_ctx
) {
100 struct TypeTraits
<MockOperationImageCtx
> {
101 typedef librbd::MockContextWQ ContextWQ
;
104 } // namespace journal
109 class DisableRequest
<MockOperationImageCtx
> {
111 static DisableRequest
*s_instance
;
112 Context
*on_finish
= nullptr;
114 static DisableRequest
*create(MockOperationImageCtx
*image_ctx
, bool force
,
115 bool remove
, Context
*on_finish
) {
116 ceph_assert(s_instance
!= nullptr);
117 s_instance
->on_finish
= on_finish
;
125 MOCK_METHOD0(send
, void());
128 DisableRequest
<MockOperationImageCtx
> *DisableRequest
<MockOperationImageCtx
>::s_instance
;
130 } // namespace mirror
132 namespace object_map
{
135 class RemoveRequest
<MockOperationImageCtx
> {
137 static RemoveRequest
*s_instance
;
138 Context
*on_finish
= nullptr;
140 static RemoveRequest
*create(MockOperationImageCtx
*image_ctx
, Context
*on_finish
) {
141 ceph_assert(s_instance
!= nullptr);
142 s_instance
->on_finish
= on_finish
;
150 MOCK_METHOD0(send
, void());
153 RemoveRequest
<MockOperationImageCtx
> *RemoveRequest
<MockOperationImageCtx
>::s_instance
;
155 } // namespace object_map
158 struct AsyncRequest
<MockOperationImageCtx
> : public AsyncRequest
<MockImageCtx
> {
159 MockOperationImageCtx
&m_image_ctx
;
161 AsyncRequest(MockOperationImageCtx
&image_ctx
, Context
*on_finish
)
162 : AsyncRequest
<MockImageCtx
>(image_ctx
, on_finish
), m_image_ctx(image_ctx
) {
166 } // namespace librbd
168 // template definitions
169 #include "librbd/AsyncRequest.cc"
170 #include "librbd/AsyncObjectThrottle.cc"
171 #include "librbd/operation/Request.cc"
172 #include "librbd/operation/DisableFeaturesRequest.cc"
175 namespace operation
{
177 using ::testing::Invoke
;
178 using ::testing::Return
;
179 using ::testing::WithArg
;
182 class TestMockOperationDisableFeaturesRequest
: public TestMockFixture
{
184 typedef librbd::image::SetFlagsRequest
<MockOperationImageCtx
> MockSetFlagsRequest
;
185 typedef librbd::journal::RemoveRequest
<MockOperationImageCtx
> MockRemoveJournalRequest
;
186 typedef librbd::mirror::DisableRequest
<MockOperationImageCtx
> MockDisableMirrorRequest
;
187 typedef librbd::object_map::RemoveRequest
<MockOperationImageCtx
> MockRemoveObjectMapRequest
;
188 typedef DisableFeaturesRequest
<MockOperationImageCtx
> MockDisableFeaturesRequest
;
190 class MirrorModeEnabler
{
192 MirrorModeEnabler(librados::IoCtx
&ioctx
, cls::rbd::MirrorMode mirror_mode
)
193 : m_ioctx(ioctx
), m_mirror_mode(mirror_mode
) {
194 EXPECT_EQ(0, librbd::cls_client::mirror_uuid_set(&m_ioctx
, "test-uuid"));
195 EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(&m_ioctx
, m_mirror_mode
));
198 ~MirrorModeEnabler() {
199 EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
200 &m_ioctx
, cls::rbd::MIRROR_MODE_DISABLED
));
203 librados::IoCtx
&m_ioctx
;
204 cls::rbd::MirrorMode m_mirror_mode
;
207 void expect_prepare_lock(MockOperationImageCtx
&mock_image_ctx
) {
208 EXPECT_CALL(*mock_image_ctx
.state
, prepare_lock(_
))
209 .WillOnce(Invoke([](Context
*on_ready
) {
210 on_ready
->complete(0);
212 expect_op_work_queue(mock_image_ctx
);
215 void expect_handle_prepare_lock_complete(MockOperationImageCtx
&mock_image_ctx
) {
216 EXPECT_CALL(*mock_image_ctx
.state
, handle_prepare_lock_complete());
219 void expect_block_writes(MockOperationImageCtx
&mock_image_ctx
) {
220 EXPECT_CALL(*mock_image_ctx
.io_image_dispatcher
, block_writes(_
))
221 .WillOnce(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
));
224 void expect_unblock_writes(MockOperationImageCtx
&mock_image_ctx
) {
225 EXPECT_CALL(*mock_image_ctx
.io_image_dispatcher
, unblock_writes()).Times(1);
228 void expect_verify_lock_ownership(MockOperationImageCtx
&mock_image_ctx
) {
229 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
230 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, is_lock_owner())
231 .WillRepeatedly(Return(true));
235 void expect_block_requests(MockOperationImageCtx
&mock_image_ctx
) {
236 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
237 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, block_requests(0)).Times(1);
241 void expect_unblock_requests(MockOperationImageCtx
&mock_image_ctx
) {
242 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
243 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, unblock_requests()).Times(1);
247 void expect_set_flags_request_send(
248 MockOperationImageCtx
&mock_image_ctx
,
249 MockSetFlagsRequest
&mock_set_flags_request
, int r
) {
250 EXPECT_CALL(mock_set_flags_request
, send())
251 .WillOnce(FinishRequest(&mock_set_flags_request
, r
,
255 void expect_disable_mirror_request_send(
256 MockOperationImageCtx
&mock_image_ctx
,
257 MockDisableMirrorRequest
&mock_disable_mirror_request
, int r
) {
258 EXPECT_CALL(mock_disable_mirror_request
, send())
259 .WillOnce(FinishRequest(&mock_disable_mirror_request
, r
,
263 void expect_close_journal(MockOperationImageCtx
&mock_image_ctx
, int r
) {
264 EXPECT_CALL(*mock_image_ctx
.journal
, close(_
))
265 .WillOnce(Invoke([&mock_image_ctx
, r
](Context
*on_finish
) {
266 mock_image_ctx
.journal
= nullptr;
267 mock_image_ctx
.image_ctx
->op_work_queue
->queue(on_finish
, r
);
271 void expect_remove_journal_request_send(
272 MockOperationImageCtx
&mock_image_ctx
,
273 MockRemoveJournalRequest
&mock_remove_journal_request
, int r
) {
274 EXPECT_CALL(mock_remove_journal_request
, send())
275 .WillOnce(FinishRequest(&mock_remove_journal_request
, r
,
279 void expect_remove_object_map_request_send(
280 MockOperationImageCtx
&mock_image_ctx
,
281 MockRemoveObjectMapRequest
&mock_remove_object_map_request
, int r
) {
282 EXPECT_CALL(mock_remove_object_map_request
, send())
283 .WillOnce(FinishRequest(&mock_remove_object_map_request
, r
,
287 void expect_notify_update(MockOperationImageCtx
&mock_image_ctx
) {
288 EXPECT_CALL(mock_image_ctx
, notify_update(_
))
289 .WillOnce(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
));
294 TEST_F(TestMockOperationDisableFeaturesRequest
, All
) {
297 librbd::ImageCtx
*ictx
;
298 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
301 ASSERT_EQ(0, librbd::get_features(ictx
, &features
));
303 uint64_t features_to_disable
= RBD_FEATURES_MUTABLE
& features
;
305 REQUIRE(features_to_disable
);
307 MockOperationImageCtx
mock_image_ctx(*ictx
);
308 MockExclusiveLock mock_exclusive_lock
;
309 MockJournal mock_journal
;
310 MockObjectMap mock_object_map
;
311 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
314 expect_verify_lock_ownership(mock_image_ctx
);
316 MockSetFlagsRequest mock_set_flags_request
;
317 MockRemoveJournalRequest mock_remove_journal_request
;
318 MockDisableMirrorRequest mock_disable_mirror_request
;
319 MockRemoveObjectMapRequest mock_remove_object_map_request
;
321 ::testing::InSequence seq
;
322 expect_prepare_lock(mock_image_ctx
);
323 expect_block_writes(mock_image_ctx
);
324 if (mock_image_ctx
.journal
!= nullptr) {
325 expect_is_journal_replaying(*mock_image_ctx
.journal
);
327 expect_block_requests(mock_image_ctx
);
328 if (features_to_disable
& RBD_FEATURE_JOURNALING
) {
329 expect_disable_mirror_request_send(mock_image_ctx
,
330 mock_disable_mirror_request
, 0);
331 expect_close_journal(mock_image_ctx
, 0);
332 expect_remove_journal_request_send(mock_image_ctx
,
333 mock_remove_journal_request
, 0);
335 if (features_to_disable
& RBD_FEATURE_OBJECT_MAP
) {
336 expect_remove_object_map_request_send(mock_image_ctx
,
337 mock_remove_object_map_request
, 0);
339 if (features_to_disable
& (RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
)) {
340 expect_set_flags_request_send(mock_image_ctx
,
341 mock_set_flags_request
, 0);
343 expect_notify_update(mock_image_ctx
);
344 expect_unblock_requests(mock_image_ctx
);
345 expect_unblock_writes(mock_image_ctx
);
346 expect_handle_prepare_lock_complete(mock_image_ctx
);
348 C_SaferCond cond_ctx
;
349 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
350 mock_image_ctx
, &cond_ctx
, 0, features_to_disable
, false);
352 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
355 ASSERT_EQ(0, cond_ctx
.wait());
358 TEST_F(TestMockOperationDisableFeaturesRequest
, ObjectMap
) {
359 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
361 librbd::ImageCtx
*ictx
;
362 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
364 MockOperationImageCtx
mock_image_ctx(*ictx
);
365 MockExclusiveLock mock_exclusive_lock
;
366 MockJournal mock_journal
;
367 MockObjectMap mock_object_map
;
368 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
371 expect_verify_lock_ownership(mock_image_ctx
);
373 MockSetFlagsRequest mock_set_flags_request
;
374 MockRemoveObjectMapRequest mock_remove_object_map_request
;
376 ::testing::InSequence seq
;
377 expect_prepare_lock(mock_image_ctx
);
378 expect_block_writes(mock_image_ctx
);
379 if (mock_image_ctx
.journal
!= nullptr) {
380 expect_is_journal_replaying(*mock_image_ctx
.journal
);
382 expect_block_requests(mock_image_ctx
);
383 expect_append_op_event(mock_image_ctx
, true, 0);
384 expect_remove_object_map_request_send(mock_image_ctx
,
385 mock_remove_object_map_request
, 0);
386 expect_set_flags_request_send(mock_image_ctx
,
387 mock_set_flags_request
, 0);
388 expect_notify_update(mock_image_ctx
);
389 expect_unblock_requests(mock_image_ctx
);
390 expect_unblock_writes(mock_image_ctx
);
391 expect_handle_prepare_lock_complete(mock_image_ctx
);
392 expect_commit_op_event(mock_image_ctx
, 0);
394 C_SaferCond cond_ctx
;
395 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
396 mock_image_ctx
, &cond_ctx
, 0,
397 RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
, false);
399 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
402 ASSERT_EQ(0, cond_ctx
.wait());
405 TEST_F(TestMockOperationDisableFeaturesRequest
, ObjectMapError
) {
406 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
408 librbd::ImageCtx
*ictx
;
409 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
411 MockOperationImageCtx
mock_image_ctx(*ictx
);
412 MockExclusiveLock mock_exclusive_lock
;
413 MockJournal mock_journal
;
414 MockObjectMap mock_object_map
;
415 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
418 expect_verify_lock_ownership(mock_image_ctx
);
420 MockSetFlagsRequest mock_set_flags_request
;
421 MockRemoveObjectMapRequest mock_remove_object_map_request
;
423 ::testing::InSequence seq
;
424 expect_prepare_lock(mock_image_ctx
);
425 expect_block_writes(mock_image_ctx
);
426 if (mock_image_ctx
.journal
!= nullptr) {
427 expect_is_journal_replaying(*mock_image_ctx
.journal
);
429 expect_block_requests(mock_image_ctx
);
430 expect_append_op_event(mock_image_ctx
, true, 0);
431 expect_remove_object_map_request_send(mock_image_ctx
,
432 mock_remove_object_map_request
, -EINVAL
);
433 expect_unblock_requests(mock_image_ctx
);
434 expect_unblock_writes(mock_image_ctx
);
435 expect_handle_prepare_lock_complete(mock_image_ctx
);
436 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
438 C_SaferCond cond_ctx
;
439 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
440 mock_image_ctx
, &cond_ctx
, 0,
441 RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
, false);
443 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
446 ASSERT_EQ(-EINVAL
, cond_ctx
.wait());
449 TEST_F(TestMockOperationDisableFeaturesRequest
, Mirroring
) {
450 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
452 MirrorModeEnabler
mirror_mode_enabler(m_ioctx
, cls::rbd::MIRROR_MODE_POOL
);
454 librbd::ImageCtx
*ictx
;
455 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
457 MockOperationImageCtx
mock_image_ctx(*ictx
);
458 MockExclusiveLock mock_exclusive_lock
;
459 MockJournal mock_journal
;
460 MockObjectMap mock_object_map
;
461 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
464 expect_verify_lock_ownership(mock_image_ctx
);
466 MockRemoveJournalRequest mock_remove_journal_request
;
467 MockDisableMirrorRequest mock_disable_mirror_request
;
469 ::testing::InSequence seq
;
470 expect_prepare_lock(mock_image_ctx
);
471 expect_block_writes(mock_image_ctx
);
472 expect_is_journal_replaying(*mock_image_ctx
.journal
);
473 expect_block_requests(mock_image_ctx
);
474 expect_disable_mirror_request_send(mock_image_ctx
,
475 mock_disable_mirror_request
, 0);
476 expect_close_journal(mock_image_ctx
, 0);
477 expect_remove_journal_request_send(mock_image_ctx
,
478 mock_remove_journal_request
, 0);
479 expect_notify_update(mock_image_ctx
);
480 expect_unblock_requests(mock_image_ctx
);
481 expect_unblock_writes(mock_image_ctx
);
482 expect_handle_prepare_lock_complete(mock_image_ctx
);
484 C_SaferCond cond_ctx
;
485 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
486 mock_image_ctx
, &cond_ctx
, 0, RBD_FEATURE_JOURNALING
, false);
488 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
491 ASSERT_EQ(0, cond_ctx
.wait());
494 TEST_F(TestMockOperationDisableFeaturesRequest
, MirroringError
) {
495 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
497 librbd::ImageCtx
*ictx
;
498 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
500 MockOperationImageCtx
mock_image_ctx(*ictx
);
501 MockExclusiveLock mock_exclusive_lock
;
502 MockJournal mock_journal
;
503 MockObjectMap mock_object_map
;
504 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
507 expect_verify_lock_ownership(mock_image_ctx
);
509 MockRemoveJournalRequest mock_remove_journal_request
;
510 MockDisableMirrorRequest mock_disable_mirror_request
;
512 ::testing::InSequence seq
;
513 expect_prepare_lock(mock_image_ctx
);
514 expect_block_writes(mock_image_ctx
);
515 expect_is_journal_replaying(*mock_image_ctx
.journal
);
516 expect_block_requests(mock_image_ctx
);
517 expect_disable_mirror_request_send(mock_image_ctx
,
518 mock_disable_mirror_request
, -EINVAL
);
519 expect_close_journal(mock_image_ctx
, 0);
520 expect_remove_journal_request_send(mock_image_ctx
,
521 mock_remove_journal_request
, 0);
522 expect_notify_update(mock_image_ctx
);
523 expect_unblock_requests(mock_image_ctx
);
524 expect_unblock_writes(mock_image_ctx
);
525 expect_handle_prepare_lock_complete(mock_image_ctx
);
527 C_SaferCond cond_ctx
;
528 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
529 mock_image_ctx
, &cond_ctx
, 0, RBD_FEATURE_JOURNALING
, false);
531 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
534 ASSERT_EQ(0, cond_ctx
.wait());
537 } // namespace operation
538 } // namespace librbd