]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/operation/test_mock_DisableFeaturesRequest.cc
7ae6d96a872d383023b6e855443b202b8f33f4df
[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 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 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 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 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 PoolMirrorModeEnabler {
178 public:
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));
183 }
184
185 ~PoolMirrorModeEnabler() {
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 };
192
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);
197 }));
198 expect_op_work_queue(mock_image_ctx);
199 }
200
201 void expect_handle_prepare_lock_complete(MockOperationImageCtx &mock_image_ctx) {
202 EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
203 }
204
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));
208 }
209
210 void expect_unblock_writes(MockOperationImageCtx &mock_image_ctx) {
211 EXPECT_CALL(*mock_image_ctx.io_work_queue, unblock_writes()).Times(1);
212 }
213
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));
218 }
219 }
220
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);
224 }
225 }
226
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);
230 }
231 }
232
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,
238 &mock_image_ctx));
239 }
240
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,
246 &mock_image_ctx));
247 }
248
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);
254 }));
255 }
256
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,
262 &mock_image_ctx));
263 }
264
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,
270 &mock_image_ctx));
271 }
272
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));
276 }
277
278 };
279
280 TEST_F(TestMockOperationDisableFeaturesRequest, All) {
281 REQUIRE_FORMAT_V2();
282
283 librbd::ImageCtx *ictx;
284 ASSERT_EQ(0, open_image(m_image_name, &ictx));
285
286 uint64_t features;
287 ASSERT_EQ(0, librbd::get_features(ictx, &features));
288
289 uint64_t features_to_disable = RBD_FEATURES_MUTABLE & features;
290
291 REQUIRE(features_to_disable);
292
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,
298 mock_object_map);
299
300 expect_verify_lock_ownership(mock_image_ctx);
301
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;
306
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);
312 }
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);
320 }
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);
324 }
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);
328 }
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);
333
334 C_SaferCond cond_ctx;
335 MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
336 mock_image_ctx, &cond_ctx, 0, features_to_disable, false);
337 {
338 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
339 req->send();
340 }
341 ASSERT_EQ(0, cond_ctx.wait());
342 }
343
344 TEST_F(TestMockOperationDisableFeaturesRequest, ObjectMap) {
345 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
346
347 librbd::ImageCtx *ictx;
348 ASSERT_EQ(0, open_image(m_image_name, &ictx));
349
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,
355 mock_object_map);
356
357 expect_verify_lock_ownership(mock_image_ctx);
358
359 MockSetFlagsRequest mock_set_flags_request;
360 MockRemoveObjectMapRequest mock_remove_object_map_request;
361
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);
367 }
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);
379
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);
384 {
385 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
386 req->send();
387 }
388 ASSERT_EQ(0, cond_ctx.wait());
389 }
390
391 TEST_F(TestMockOperationDisableFeaturesRequest, ObjectMapError) {
392 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
393
394 librbd::ImageCtx *ictx;
395 ASSERT_EQ(0, open_image(m_image_name, &ictx));
396
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,
402 mock_object_map);
403
404 expect_verify_lock_ownership(mock_image_ctx);
405
406 MockSetFlagsRequest mock_set_flags_request;
407 MockRemoveObjectMapRequest mock_remove_object_map_request;
408
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);
414 }
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);
423
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);
428 {
429 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
430 req->send();
431 }
432 ASSERT_EQ(-EINVAL, cond_ctx.wait());
433 }
434
435 TEST_F(TestMockOperationDisableFeaturesRequest, Mirroring) {
436 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
437
438 librbd::ImageCtx *ictx;
439 ASSERT_EQ(0, open_image(m_image_name, &ictx));
440
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,
446 mock_object_map);
447
448 expect_verify_lock_ownership(mock_image_ctx);
449
450 MockRemoveJournalRequest mock_remove_journal_request;
451 MockDisableMirrorRequest mock_disable_mirror_request;
452
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);
467
468 C_SaferCond cond_ctx;
469 MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
470 mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
471 {
472 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
473 req->send();
474 }
475 ASSERT_EQ(0, cond_ctx.wait());
476 }
477
478 TEST_F(TestMockOperationDisableFeaturesRequest, MirroringError) {
479 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
480
481 librbd::ImageCtx *ictx;
482 ASSERT_EQ(0, open_image(m_image_name, &ictx));
483
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,
489 mock_object_map);
490
491 expect_verify_lock_ownership(mock_image_ctx);
492
493 MockRemoveJournalRequest mock_remove_journal_request;
494 MockDisableMirrorRequest mock_disable_mirror_request;
495
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);
510
511 C_SaferCond cond_ctx;
512 MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
513 mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
514 {
515 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
516 req->send();
517 }
518 ASSERT_EQ(0, cond_ctx.wait());
519 }
520
521 } // namespace operation
522 } // namespace librbd