]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/operation/test_mock_DisableFeaturesRequest.cc
ec374d518e96adb033f93d3f882904129062bfc4
[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/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"
20
21 namespace librbd {
22
23 namespace {
24
25 struct MockOperationImageCtx : public MockImageCtx {
26 MockOperationImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
27 }
28 };
29
30 } // anonymous namespace
31
32 namespace image {
33
34 template<>
35 class SetFlagsRequest<MockOperationImageCtx> {
36 public:
37 static SetFlagsRequest *s_instance;
38 Context *on_finish = nullptr;
39
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;
44 return s_instance;
45 }
46
47 SetFlagsRequest() {
48 s_instance = this;
49 }
50
51 MOCK_METHOD0(send, void());
52 };
53
54 SetFlagsRequest<MockOperationImageCtx> *SetFlagsRequest<MockOperationImageCtx>::s_instance;
55
56 } // namespace image
57
58 namespace journal {
59
60 template<>
61 class RemoveRequest<MockOperationImageCtx> {
62 public:
63 static RemoveRequest *s_instance;
64 Context *on_finish = nullptr;
65
66 static RemoveRequest *create(IoCtx &ioctx, const std::string &imageid,
67 const std::string &client_id,
68 MockContextWQ *op_work_queue,
69 Context *on_finish) {
70 ceph_assert(s_instance != nullptr);
71 s_instance->on_finish = on_finish;
72 return s_instance;
73 }
74
75 RemoveRequest() {
76 s_instance = this;
77 }
78
79 MOCK_METHOD0(send, void());
80 };
81
82 RemoveRequest<MockOperationImageCtx> *RemoveRequest<MockOperationImageCtx>::s_instance;
83
84 template<>
85 class StandardPolicy<MockOperationImageCtx> : public MockJournalPolicy {
86 public:
87 StandardPolicy(MockOperationImageCtx* image_ctx) {
88 }
89 };
90
91 } // namespace journal
92
93 namespace mirror {
94
95 template<>
96 class DisableRequest<MockOperationImageCtx> {
97 public:
98 static DisableRequest *s_instance;
99 Context *on_finish = nullptr;
100
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;
105 return s_instance;
106 }
107
108 DisableRequest() {
109 s_instance = this;
110 }
111
112 MOCK_METHOD0(send, void());
113 };
114
115 DisableRequest<MockOperationImageCtx> *DisableRequest<MockOperationImageCtx>::s_instance;
116
117 } // namespace mirror
118
119 namespace object_map {
120
121 template<>
122 class RemoveRequest<MockOperationImageCtx> {
123 public:
124 static RemoveRequest *s_instance;
125 Context *on_finish = nullptr;
126
127 static RemoveRequest *create(MockOperationImageCtx *image_ctx, Context *on_finish) {
128 ceph_assert(s_instance != nullptr);
129 s_instance->on_finish = on_finish;
130 return s_instance;
131 }
132
133 RemoveRequest() {
134 s_instance = this;
135 }
136
137 MOCK_METHOD0(send, void());
138 };
139
140 RemoveRequest<MockOperationImageCtx> *RemoveRequest<MockOperationImageCtx>::s_instance;
141
142 } // namespace object_map
143
144 template <>
145 struct AsyncRequest<MockOperationImageCtx> : public AsyncRequest<MockImageCtx> {
146 MockOperationImageCtx &m_image_ctx;
147
148 AsyncRequest(MockOperationImageCtx &image_ctx, Context *on_finish)
149 : AsyncRequest<MockImageCtx>(image_ctx, on_finish), m_image_ctx(image_ctx) {
150 }
151 };
152
153 } // namespace librbd
154
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"
160
161 namespace librbd {
162 namespace operation {
163
164 using ::testing::Invoke;
165 using ::testing::Return;
166 using ::testing::WithArg;
167 using ::testing::_;
168
169 class TestMockOperationDisableFeaturesRequest : public TestMockFixture {
170 public:
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;
176
177 class MirrorModeEnabler {
178 public:
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));
183 }
184
185 ~MirrorModeEnabler() {
186 EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
187 &m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
188 }
189 private:
190 librados::IoCtx &m_ioctx;
191 cls::rbd::MirrorMode m_mirror_mode;
192 };
193
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);
198 }));
199 expect_op_work_queue(mock_image_ctx);
200 }
201
202 void expect_handle_prepare_lock_complete(MockOperationImageCtx &mock_image_ctx) {
203 EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
204 }
205
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));
209 }
210
211 void expect_unblock_writes(MockOperationImageCtx &mock_image_ctx) {
212 EXPECT_CALL(*mock_image_ctx.io_work_queue, unblock_writes()).Times(1);
213 }
214
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));
219 }
220 }
221
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);
225 }
226 }
227
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);
231 }
232 }
233
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,
239 &mock_image_ctx));
240 }
241
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,
247 &mock_image_ctx));
248 }
249
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);
255 }));
256 }
257
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,
263 &mock_image_ctx));
264 }
265
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,
271 &mock_image_ctx));
272 }
273
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));
277 }
278
279 };
280
281 TEST_F(TestMockOperationDisableFeaturesRequest, All) {
282 REQUIRE_FORMAT_V2();
283
284 librbd::ImageCtx *ictx;
285 ASSERT_EQ(0, open_image(m_image_name, &ictx));
286
287 uint64_t features;
288 ASSERT_EQ(0, librbd::get_features(ictx, &features));
289
290 uint64_t features_to_disable = RBD_FEATURES_MUTABLE & features;
291
292 REQUIRE(features_to_disable);
293
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,
299 mock_object_map);
300
301 expect_verify_lock_ownership(mock_image_ctx);
302
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;
307
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);
313 }
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);
321 }
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);
325 }
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);
329 }
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);
334
335 C_SaferCond cond_ctx;
336 MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
337 mock_image_ctx, &cond_ctx, 0, features_to_disable, false);
338 {
339 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
340 req->send();
341 }
342 ASSERT_EQ(0, cond_ctx.wait());
343 }
344
345 TEST_F(TestMockOperationDisableFeaturesRequest, ObjectMap) {
346 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
347
348 librbd::ImageCtx *ictx;
349 ASSERT_EQ(0, open_image(m_image_name, &ictx));
350
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,
356 mock_object_map);
357
358 expect_verify_lock_ownership(mock_image_ctx);
359
360 MockSetFlagsRequest mock_set_flags_request;
361 MockRemoveObjectMapRequest mock_remove_object_map_request;
362
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);
368 }
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);
380
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);
385 {
386 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
387 req->send();
388 }
389 ASSERT_EQ(0, cond_ctx.wait());
390 }
391
392 TEST_F(TestMockOperationDisableFeaturesRequest, ObjectMapError) {
393 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
394
395 librbd::ImageCtx *ictx;
396 ASSERT_EQ(0, open_image(m_image_name, &ictx));
397
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,
403 mock_object_map);
404
405 expect_verify_lock_ownership(mock_image_ctx);
406
407 MockSetFlagsRequest mock_set_flags_request;
408 MockRemoveObjectMapRequest mock_remove_object_map_request;
409
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);
415 }
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);
424
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);
429 {
430 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
431 req->send();
432 }
433 ASSERT_EQ(-EINVAL, cond_ctx.wait());
434 }
435
436 TEST_F(TestMockOperationDisableFeaturesRequest, Mirroring) {
437 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
438
439 MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
440
441 librbd::ImageCtx *ictx;
442 ASSERT_EQ(0, open_image(m_image_name, &ictx));
443
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,
449 mock_object_map);
450
451 expect_verify_lock_ownership(mock_image_ctx);
452
453 MockRemoveJournalRequest mock_remove_journal_request;
454 MockDisableMirrorRequest mock_disable_mirror_request;
455
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);
470
471 C_SaferCond cond_ctx;
472 MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
473 mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
474 {
475 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
476 req->send();
477 }
478 ASSERT_EQ(0, cond_ctx.wait());
479 }
480
481 TEST_F(TestMockOperationDisableFeaturesRequest, MirroringError) {
482 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
483
484 librbd::ImageCtx *ictx;
485 ASSERT_EQ(0, open_image(m_image_name, &ictx));
486
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,
492 mock_object_map);
493
494 expect_verify_lock_ownership(mock_image_ctx);
495
496 MockRemoveJournalRequest mock_remove_journal_request;
497 MockDisableMirrorRequest mock_disable_mirror_request;
498
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);
513
514 C_SaferCond cond_ctx;
515 MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
516 mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
517 {
518 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
519 req->send();
520 }
521 ASSERT_EQ(0, cond_ctx.wait());
522 }
523
524 } // namespace operation
525 } // namespace librbd