]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/operation/test_mock_DisableFeaturesRequest.cc
171ac41a71a8f88b38c01a7f2feb4b4ea3fceaef
[ceph.git] / ceph / src / test / librbd / operation / test_mock_DisableFeaturesRequest.cc
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/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"
22
23 namespace librbd {
24
25 namespace {
26
27 struct MockOperationImageCtx : public MockImageCtx {
28 MockOperationImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
29 }
30 };
31
32 } // anonymous namespace
33
34 template<>
35 struct Journal<MockOperationImageCtx> {
36 static void get_work_queue(CephContext*, MockContextWQ**) {
37 }
38 };
39
40 namespace image {
41
42 template<>
43 class SetFlagsRequest<MockOperationImageCtx> {
44 public:
45 static SetFlagsRequest *s_instance;
46 Context *on_finish = nullptr;
47
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;
52 return s_instance;
53 }
54
55 SetFlagsRequest() {
56 s_instance = this;
57 }
58
59 MOCK_METHOD0(send, void());
60 };
61
62 SetFlagsRequest<MockOperationImageCtx> *SetFlagsRequest<MockOperationImageCtx>::s_instance;
63
64 } // namespace image
65
66 namespace journal {
67
68 template<>
69 class RemoveRequest<MockOperationImageCtx> {
70 public:
71 static RemoveRequest *s_instance;
72 Context *on_finish = nullptr;
73
74 static RemoveRequest *create(IoCtx &ioctx, const std::string &imageid,
75 const std::string &client_id,
76 MockContextWQ *op_work_queue,
77 Context *on_finish) {
78 ceph_assert(s_instance != nullptr);
79 s_instance->on_finish = on_finish;
80 return s_instance;
81 }
82
83 RemoveRequest() {
84 s_instance = this;
85 }
86
87 MOCK_METHOD0(send, void());
88 };
89
90 RemoveRequest<MockOperationImageCtx> *RemoveRequest<MockOperationImageCtx>::s_instance;
91
92 template<>
93 class StandardPolicy<MockOperationImageCtx> : public MockJournalPolicy {
94 public:
95 StandardPolicy(MockOperationImageCtx* image_ctx) {
96 }
97 };
98
99 template <>
100 struct TypeTraits<MockOperationImageCtx> {
101 typedef librbd::MockContextWQ ContextWQ;
102 };
103
104 } // namespace journal
105
106 namespace mirror {
107
108 template<>
109 class DisableRequest<MockOperationImageCtx> {
110 public:
111 static DisableRequest *s_instance;
112 Context *on_finish = nullptr;
113
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;
118 return s_instance;
119 }
120
121 DisableRequest() {
122 s_instance = this;
123 }
124
125 MOCK_METHOD0(send, void());
126 };
127
128 DisableRequest<MockOperationImageCtx> *DisableRequest<MockOperationImageCtx>::s_instance;
129
130 } // namespace mirror
131
132 namespace object_map {
133
134 template<>
135 class RemoveRequest<MockOperationImageCtx> {
136 public:
137 static RemoveRequest *s_instance;
138 Context *on_finish = nullptr;
139
140 static RemoveRequest *create(MockOperationImageCtx *image_ctx, Context *on_finish) {
141 ceph_assert(s_instance != nullptr);
142 s_instance->on_finish = on_finish;
143 return s_instance;
144 }
145
146 RemoveRequest() {
147 s_instance = this;
148 }
149
150 MOCK_METHOD0(send, void());
151 };
152
153 RemoveRequest<MockOperationImageCtx> *RemoveRequest<MockOperationImageCtx>::s_instance;
154
155 } // namespace object_map
156
157 template <>
158 struct AsyncRequest<MockOperationImageCtx> : public AsyncRequest<MockImageCtx> {
159 MockOperationImageCtx &m_image_ctx;
160
161 AsyncRequest(MockOperationImageCtx &image_ctx, Context *on_finish)
162 : AsyncRequest<MockImageCtx>(image_ctx, on_finish), m_image_ctx(image_ctx) {
163 }
164 };
165
166 } // namespace librbd
167
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"
173
174 namespace librbd {
175 namespace operation {
176
177 using ::testing::Invoke;
178 using ::testing::Return;
179 using ::testing::WithArg;
180 using ::testing::_;
181
182 class TestMockOperationDisableFeaturesRequest : public TestMockFixture {
183 public:
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;
189
190 class MirrorModeEnabler {
191 public:
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));
196 }
197
198 ~MirrorModeEnabler() {
199 EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
200 &m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
201 }
202 private:
203 librados::IoCtx &m_ioctx;
204 cls::rbd::MirrorMode m_mirror_mode;
205 };
206
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);
211 }));
212 expect_op_work_queue(mock_image_ctx);
213 }
214
215 void expect_handle_prepare_lock_complete(MockOperationImageCtx &mock_image_ctx) {
216 EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
217 }
218
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));
222 }
223
224 void expect_unblock_writes(MockOperationImageCtx &mock_image_ctx) {
225 EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, unblock_writes()).Times(1);
226 }
227
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));
232 }
233 }
234
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);
238 }
239 }
240
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);
244 }
245 }
246
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,
252 &mock_image_ctx));
253 }
254
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,
260 &mock_image_ctx));
261 }
262
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);
268 }));
269 }
270
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,
276 &mock_image_ctx));
277 }
278
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,
284 &mock_image_ctx));
285 }
286
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));
290 }
291
292 };
293
294 TEST_F(TestMockOperationDisableFeaturesRequest, All) {
295 REQUIRE_FORMAT_V2();
296
297 librbd::ImageCtx *ictx;
298 ASSERT_EQ(0, open_image(m_image_name, &ictx));
299
300 uint64_t features;
301 ASSERT_EQ(0, librbd::get_features(ictx, &features));
302
303 uint64_t features_to_disable = RBD_FEATURES_MUTABLE & features;
304
305 REQUIRE(features_to_disable);
306
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,
312 mock_object_map);
313
314 expect_verify_lock_ownership(mock_image_ctx);
315
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;
320
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);
326 }
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);
334 }
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);
338 }
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);
342 }
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);
347
348 C_SaferCond cond_ctx;
349 MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
350 mock_image_ctx, &cond_ctx, 0, features_to_disable, false);
351 {
352 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
353 req->send();
354 }
355 ASSERT_EQ(0, cond_ctx.wait());
356 }
357
358 TEST_F(TestMockOperationDisableFeaturesRequest, ObjectMap) {
359 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
360
361 librbd::ImageCtx *ictx;
362 ASSERT_EQ(0, open_image(m_image_name, &ictx));
363
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,
369 mock_object_map);
370
371 expect_verify_lock_ownership(mock_image_ctx);
372
373 MockSetFlagsRequest mock_set_flags_request;
374 MockRemoveObjectMapRequest mock_remove_object_map_request;
375
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);
381 }
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);
393
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);
398 {
399 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
400 req->send();
401 }
402 ASSERT_EQ(0, cond_ctx.wait());
403 }
404
405 TEST_F(TestMockOperationDisableFeaturesRequest, ObjectMapError) {
406 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
407
408 librbd::ImageCtx *ictx;
409 ASSERT_EQ(0, open_image(m_image_name, &ictx));
410
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,
416 mock_object_map);
417
418 expect_verify_lock_ownership(mock_image_ctx);
419
420 MockSetFlagsRequest mock_set_flags_request;
421 MockRemoveObjectMapRequest mock_remove_object_map_request;
422
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);
428 }
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);
437
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);
442 {
443 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
444 req->send();
445 }
446 ASSERT_EQ(-EINVAL, cond_ctx.wait());
447 }
448
449 TEST_F(TestMockOperationDisableFeaturesRequest, Mirroring) {
450 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
451
452 MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
453
454 librbd::ImageCtx *ictx;
455 ASSERT_EQ(0, open_image(m_image_name, &ictx));
456
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,
462 mock_object_map);
463
464 expect_verify_lock_ownership(mock_image_ctx);
465
466 MockRemoveJournalRequest mock_remove_journal_request;
467 MockDisableMirrorRequest mock_disable_mirror_request;
468
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);
483
484 C_SaferCond cond_ctx;
485 MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
486 mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
487 {
488 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
489 req->send();
490 }
491 ASSERT_EQ(0, cond_ctx.wait());
492 }
493
494 TEST_F(TestMockOperationDisableFeaturesRequest, MirroringError) {
495 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
496
497 librbd::ImageCtx *ictx;
498 ASSERT_EQ(0, open_image(m_image_name, &ictx));
499
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,
505 mock_object_map);
506
507 expect_verify_lock_ownership(mock_image_ctx);
508
509 MockRemoveJournalRequest mock_remove_journal_request;
510 MockDisableMirrorRequest mock_disable_mirror_request;
511
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);
526
527 C_SaferCond cond_ctx;
528 MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
529 mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
530 {
531 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
532 req->send();
533 }
534 ASSERT_EQ(0, cond_ctx.wait());
535 }
536
537 } // namespace operation
538 } // namespace librbd