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 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 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 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 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 PoolMirrorModeEnabler
{
179 PoolMirrorModeEnabler(librados::IoCtx
&ioctx
) : m_ioctx(ioctx
) {
180 EXPECT_EQ(0, librbd::cls_client::mirror_uuid_set(&m_ioctx
, "test-uuid"));
181 EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
182 &m_ioctx
, cls::rbd::MIRROR_MODE_POOL
));
185 ~PoolMirrorModeEnabler() {
186 EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
187 &m_ioctx
, cls::rbd::MIRROR_MODE_DISABLED
));
190 librados::IoCtx
&m_ioctx
;
193 void expect_prepare_lock(MockOperationImageCtx
&mock_image_ctx
) {
194 EXPECT_CALL(*mock_image_ctx
.state
, prepare_lock(_
))
195 .WillOnce(Invoke([](Context
*on_ready
) {
196 on_ready
->complete(0);
198 expect_op_work_queue(mock_image_ctx
);
201 void expect_handle_prepare_lock_complete(MockOperationImageCtx
&mock_image_ctx
) {
202 EXPECT_CALL(*mock_image_ctx
.state
, handle_prepare_lock_complete());
205 void expect_block_writes(MockOperationImageCtx
&mock_image_ctx
) {
206 EXPECT_CALL(*mock_image_ctx
.io_work_queue
, block_writes(_
))
207 .WillOnce(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
));
210 void expect_unblock_writes(MockOperationImageCtx
&mock_image_ctx
) {
211 EXPECT_CALL(*mock_image_ctx
.io_work_queue
, unblock_writes()).Times(1);
214 void expect_verify_lock_ownership(MockOperationImageCtx
&mock_image_ctx
) {
215 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
216 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, is_lock_owner())
217 .WillRepeatedly(Return(true));
221 void expect_block_requests(MockOperationImageCtx
&mock_image_ctx
) {
222 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
223 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, block_requests(0)).Times(1);
227 void expect_unblock_requests(MockOperationImageCtx
&mock_image_ctx
) {
228 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
229 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, unblock_requests()).Times(1);
233 void expect_set_flags_request_send(
234 MockOperationImageCtx
&mock_image_ctx
,
235 MockSetFlagsRequest
&mock_set_flags_request
, int r
) {
236 EXPECT_CALL(mock_set_flags_request
, send())
237 .WillOnce(FinishRequest(&mock_set_flags_request
, r
,
241 void expect_disable_mirror_request_send(
242 MockOperationImageCtx
&mock_image_ctx
,
243 MockDisableMirrorRequest
&mock_disable_mirror_request
, int r
) {
244 EXPECT_CALL(mock_disable_mirror_request
, send())
245 .WillOnce(FinishRequest(&mock_disable_mirror_request
, r
,
249 void expect_close_journal(MockOperationImageCtx
&mock_image_ctx
, int r
) {
250 EXPECT_CALL(*mock_image_ctx
.journal
, close(_
))
251 .WillOnce(Invoke([&mock_image_ctx
, r
](Context
*on_finish
) {
252 mock_image_ctx
.journal
= nullptr;
253 mock_image_ctx
.image_ctx
->op_work_queue
->queue(on_finish
, r
);
257 void expect_remove_journal_request_send(
258 MockOperationImageCtx
&mock_image_ctx
,
259 MockRemoveJournalRequest
&mock_remove_journal_request
, int r
) {
260 EXPECT_CALL(mock_remove_journal_request
, send())
261 .WillOnce(FinishRequest(&mock_remove_journal_request
, r
,
265 void expect_remove_object_map_request_send(
266 MockOperationImageCtx
&mock_image_ctx
,
267 MockRemoveObjectMapRequest
&mock_remove_object_map_request
, int r
) {
268 EXPECT_CALL(mock_remove_object_map_request
, send())
269 .WillOnce(FinishRequest(&mock_remove_object_map_request
, r
,
273 void expect_notify_update(MockOperationImageCtx
&mock_image_ctx
) {
274 EXPECT_CALL(mock_image_ctx
, notify_update(_
))
275 .WillOnce(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
));
280 TEST_F(TestMockOperationDisableFeaturesRequest
, All
) {
283 librbd::ImageCtx
*ictx
;
284 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
287 ASSERT_EQ(0, librbd::get_features(ictx
, &features
));
289 uint64_t features_to_disable
= RBD_FEATURES_MUTABLE
& features
;
291 REQUIRE(features_to_disable
);
293 MockOperationImageCtx
mock_image_ctx(*ictx
);
294 MockExclusiveLock mock_exclusive_lock
;
295 MockJournal mock_journal
;
296 MockObjectMap mock_object_map
;
297 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
300 expect_verify_lock_ownership(mock_image_ctx
);
302 MockSetFlagsRequest mock_set_flags_request
;
303 MockRemoveJournalRequest mock_remove_journal_request
;
304 MockDisableMirrorRequest mock_disable_mirror_request
;
305 MockRemoveObjectMapRequest mock_remove_object_map_request
;
307 ::testing::InSequence seq
;
308 expect_prepare_lock(mock_image_ctx
);
309 expect_block_writes(mock_image_ctx
);
310 if (mock_image_ctx
.journal
!= nullptr) {
311 expect_is_journal_replaying(*mock_image_ctx
.journal
);
313 expect_block_requests(mock_image_ctx
);
314 if (features_to_disable
& RBD_FEATURE_JOURNALING
) {
315 expect_disable_mirror_request_send(mock_image_ctx
,
316 mock_disable_mirror_request
, 0);
317 expect_close_journal(mock_image_ctx
, 0);
318 expect_remove_journal_request_send(mock_image_ctx
,
319 mock_remove_journal_request
, 0);
321 if (features_to_disable
& RBD_FEATURE_OBJECT_MAP
) {
322 expect_remove_object_map_request_send(mock_image_ctx
,
323 mock_remove_object_map_request
, 0);
325 if (features_to_disable
& (RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
)) {
326 expect_set_flags_request_send(mock_image_ctx
,
327 mock_set_flags_request
, 0);
329 expect_notify_update(mock_image_ctx
);
330 expect_unblock_requests(mock_image_ctx
);
331 expect_unblock_writes(mock_image_ctx
);
332 expect_handle_prepare_lock_complete(mock_image_ctx
);
334 C_SaferCond cond_ctx
;
335 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
336 mock_image_ctx
, &cond_ctx
, 0, features_to_disable
, false);
338 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
341 ASSERT_EQ(0, cond_ctx
.wait());
344 TEST_F(TestMockOperationDisableFeaturesRequest
, ObjectMap
) {
345 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
347 librbd::ImageCtx
*ictx
;
348 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
350 MockOperationImageCtx
mock_image_ctx(*ictx
);
351 MockExclusiveLock mock_exclusive_lock
;
352 MockJournal mock_journal
;
353 MockObjectMap mock_object_map
;
354 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
357 expect_verify_lock_ownership(mock_image_ctx
);
359 MockSetFlagsRequest mock_set_flags_request
;
360 MockRemoveObjectMapRequest mock_remove_object_map_request
;
362 ::testing::InSequence seq
;
363 expect_prepare_lock(mock_image_ctx
);
364 expect_block_writes(mock_image_ctx
);
365 if (mock_image_ctx
.journal
!= nullptr) {
366 expect_is_journal_replaying(*mock_image_ctx
.journal
);
368 expect_block_requests(mock_image_ctx
);
369 expect_append_op_event(mock_image_ctx
, true, 0);
370 expect_remove_object_map_request_send(mock_image_ctx
,
371 mock_remove_object_map_request
, 0);
372 expect_set_flags_request_send(mock_image_ctx
,
373 mock_set_flags_request
, 0);
374 expect_notify_update(mock_image_ctx
);
375 expect_unblock_requests(mock_image_ctx
);
376 expect_unblock_writes(mock_image_ctx
);
377 expect_handle_prepare_lock_complete(mock_image_ctx
);
378 expect_commit_op_event(mock_image_ctx
, 0);
380 C_SaferCond cond_ctx
;
381 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
382 mock_image_ctx
, &cond_ctx
, 0,
383 RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
, false);
385 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
388 ASSERT_EQ(0, cond_ctx
.wait());
391 TEST_F(TestMockOperationDisableFeaturesRequest
, ObjectMapError
) {
392 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
394 librbd::ImageCtx
*ictx
;
395 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
397 MockOperationImageCtx
mock_image_ctx(*ictx
);
398 MockExclusiveLock mock_exclusive_lock
;
399 MockJournal mock_journal
;
400 MockObjectMap mock_object_map
;
401 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
404 expect_verify_lock_ownership(mock_image_ctx
);
406 MockSetFlagsRequest mock_set_flags_request
;
407 MockRemoveObjectMapRequest mock_remove_object_map_request
;
409 ::testing::InSequence seq
;
410 expect_prepare_lock(mock_image_ctx
);
411 expect_block_writes(mock_image_ctx
);
412 if (mock_image_ctx
.journal
!= nullptr) {
413 expect_is_journal_replaying(*mock_image_ctx
.journal
);
415 expect_block_requests(mock_image_ctx
);
416 expect_append_op_event(mock_image_ctx
, true, 0);
417 expect_remove_object_map_request_send(mock_image_ctx
,
418 mock_remove_object_map_request
, -EINVAL
);
419 expect_unblock_requests(mock_image_ctx
);
420 expect_unblock_writes(mock_image_ctx
);
421 expect_handle_prepare_lock_complete(mock_image_ctx
);
422 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
424 C_SaferCond cond_ctx
;
425 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
426 mock_image_ctx
, &cond_ctx
, 0,
427 RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
, false);
429 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
432 ASSERT_EQ(-EINVAL
, cond_ctx
.wait());
435 TEST_F(TestMockOperationDisableFeaturesRequest
, Mirroring
) {
436 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
438 librbd::ImageCtx
*ictx
;
439 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
441 MockOperationImageCtx
mock_image_ctx(*ictx
);
442 MockExclusiveLock mock_exclusive_lock
;
443 MockJournal mock_journal
;
444 MockObjectMap mock_object_map
;
445 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
448 expect_verify_lock_ownership(mock_image_ctx
);
450 MockRemoveJournalRequest mock_remove_journal_request
;
451 MockDisableMirrorRequest mock_disable_mirror_request
;
453 ::testing::InSequence seq
;
454 expect_prepare_lock(mock_image_ctx
);
455 expect_block_writes(mock_image_ctx
);
456 expect_is_journal_replaying(*mock_image_ctx
.journal
);
457 expect_block_requests(mock_image_ctx
);
458 expect_disable_mirror_request_send(mock_image_ctx
,
459 mock_disable_mirror_request
, 0);
460 expect_close_journal(mock_image_ctx
, 0);
461 expect_remove_journal_request_send(mock_image_ctx
,
462 mock_remove_journal_request
, 0);
463 expect_notify_update(mock_image_ctx
);
464 expect_unblock_requests(mock_image_ctx
);
465 expect_unblock_writes(mock_image_ctx
);
466 expect_handle_prepare_lock_complete(mock_image_ctx
);
468 C_SaferCond cond_ctx
;
469 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
470 mock_image_ctx
, &cond_ctx
, 0, RBD_FEATURE_JOURNALING
, false);
472 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
475 ASSERT_EQ(0, cond_ctx
.wait());
478 TEST_F(TestMockOperationDisableFeaturesRequest
, MirroringError
) {
479 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
481 librbd::ImageCtx
*ictx
;
482 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
484 MockOperationImageCtx
mock_image_ctx(*ictx
);
485 MockExclusiveLock mock_exclusive_lock
;
486 MockJournal mock_journal
;
487 MockObjectMap mock_object_map
;
488 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
491 expect_verify_lock_ownership(mock_image_ctx
);
493 MockRemoveJournalRequest mock_remove_journal_request
;
494 MockDisableMirrorRequest mock_disable_mirror_request
;
496 ::testing::InSequence seq
;
497 expect_prepare_lock(mock_image_ctx
);
498 expect_block_writes(mock_image_ctx
);
499 expect_is_journal_replaying(*mock_image_ctx
.journal
);
500 expect_block_requests(mock_image_ctx
);
501 expect_disable_mirror_request_send(mock_image_ctx
,
502 mock_disable_mirror_request
, -EINVAL
);
503 expect_close_journal(mock_image_ctx
, 0);
504 expect_remove_journal_request_send(mock_image_ctx
,
505 mock_remove_journal_request
, 0);
506 expect_notify_update(mock_image_ctx
);
507 expect_unblock_requests(mock_image_ctx
);
508 expect_unblock_writes(mock_image_ctx
);
509 expect_handle_prepare_lock_complete(mock_image_ctx
);
511 C_SaferCond cond_ctx
;
512 MockDisableFeaturesRequest
*req
= new MockDisableFeaturesRequest(
513 mock_image_ctx
, &cond_ctx
, 0, RBD_FEATURE_JOURNALING
, false);
515 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
518 ASSERT_EQ(0, cond_ctx
.wait());
521 } // namespace operation
522 } // namespace librbd