]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
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 "include/rbd/librbd.hpp" | |
f67539c2 | 6 | #include "librbd/AsioEngine.h" |
11fdf7f2 TL |
7 | #include "librbd/ImageCtx.h" |
8 | #include "librbd/ImageState.h" | |
9f95a23c | 9 | #include "librbd/internal.h" |
11fdf7f2 | 10 | #include "librbd/Operations.h" |
1911f103 | 11 | #include "librbd/deep_copy/Handler.h" |
11fdf7f2 TL |
12 | #include "librbd/deep_copy/ImageCopyRequest.h" |
13 | #include "librbd/deep_copy/ObjectCopyRequest.h" | |
9f95a23c | 14 | #include "librbd/object_map/DiffRequest.h" |
11fdf7f2 TL |
15 | #include "test/librados_test_stub/MockTestMemIoCtxImpl.h" |
16 | #include "test/librbd/mock/MockImageCtx.h" | |
17 | #include "test/librbd/test_support.h" | |
18 | #include <boost/scope_exit.hpp> | |
19 | ||
20 | namespace librbd { | |
21 | ||
22 | namespace { | |
23 | ||
24 | struct MockTestImageCtx : public librbd::MockImageCtx { | |
25 | static MockTestImageCtx* s_instance; | |
26 | static MockTestImageCtx* create(const std::string &image_name, | |
27 | const std::string &image_id, | |
28 | librados::snap_t snap_id, librados::IoCtx& p, | |
29 | bool read_only) { | |
30 | ceph_assert(s_instance != nullptr); | |
31 | return s_instance; | |
32 | } | |
33 | ||
34 | explicit MockTestImageCtx(librbd::ImageCtx &image_ctx) | |
35 | : librbd::MockImageCtx(image_ctx) { | |
36 | s_instance = this; | |
37 | } | |
38 | ||
39 | MOCK_METHOD0(destroy, void()); | |
40 | }; | |
41 | ||
42 | MockTestImageCtx* MockTestImageCtx::s_instance = nullptr; | |
43 | ||
44 | } // anonymous namespace | |
45 | ||
46 | namespace deep_copy { | |
47 | ||
48 | template <> | |
49 | struct ObjectCopyRequest<librbd::MockTestImageCtx> { | |
50 | static ObjectCopyRequest* s_instance; | |
51 | static ObjectCopyRequest* create( | |
52 | librbd::MockTestImageCtx *src_image_ctx, | |
9f95a23c TL |
53 | librbd::MockTestImageCtx *dst_image_ctx, |
54 | librados::snap_t src_snap_id_start, | |
55 | librados::snap_t dst_snap_id_start, | |
56 | const SnapMap &snap_map, | |
cd265ab1 | 57 | uint64_t object_number, uint32_t flags, Handler* handler, |
1911f103 | 58 | Context *on_finish) { |
11fdf7f2 | 59 | ceph_assert(s_instance != nullptr); |
9f95a23c | 60 | std::lock_guard locker{s_instance->lock}; |
11fdf7f2 | 61 | s_instance->snap_map = &snap_map; |
f67539c2 | 62 | s_instance->flags = flags; |
11fdf7f2 | 63 | s_instance->object_contexts[object_number] = on_finish; |
9f95a23c | 64 | s_instance->cond.notify_all(); |
11fdf7f2 TL |
65 | return s_instance; |
66 | } | |
67 | ||
68 | MOCK_METHOD0(send, void()); | |
69 | ||
9f95a23c TL |
70 | ceph::mutex lock = ceph::make_mutex("lock"); |
71 | ceph::condition_variable cond; | |
11fdf7f2 TL |
72 | |
73 | const SnapMap *snap_map = nullptr; | |
74 | std::map<uint64_t, Context *> object_contexts; | |
f67539c2 | 75 | uint32_t flags = 0; |
11fdf7f2 | 76 | |
9f95a23c | 77 | ObjectCopyRequest() { |
11fdf7f2 TL |
78 | s_instance = this; |
79 | } | |
80 | }; | |
81 | ||
82 | ObjectCopyRequest<librbd::MockTestImageCtx>* ObjectCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
83 | ||
84 | } // namespace deep_copy | |
85 | ||
9f95a23c TL |
86 | namespace object_map { |
87 | ||
88 | template <> | |
89 | struct DiffRequest<MockTestImageCtx> { | |
90 | BitVector<2>* object_diff_state = nullptr; | |
91 | Context* on_finish = nullptr; | |
92 | static DiffRequest* s_instance; | |
93 | static DiffRequest* create(MockTestImageCtx *image_ctx, | |
94 | uint64_t snap_id_start, uint64_t snap_id_end, | |
95 | BitVector<2>* object_diff_state, | |
96 | Context* on_finish) { | |
97 | ceph_assert(s_instance != nullptr); | |
98 | s_instance->object_diff_state = object_diff_state; | |
99 | s_instance->on_finish = on_finish; | |
100 | return s_instance; | |
101 | } | |
102 | ||
103 | DiffRequest() { | |
104 | s_instance = this; | |
105 | } | |
106 | ||
107 | MOCK_METHOD0(send, void()); | |
108 | }; | |
109 | ||
110 | DiffRequest<MockTestImageCtx>* DiffRequest<MockTestImageCtx>::s_instance = nullptr; | |
111 | ||
112 | } // namespace object_map | |
11fdf7f2 TL |
113 | } // namespace librbd |
114 | ||
115 | // template definitions | |
116 | #include "librbd/deep_copy/ImageCopyRequest.cc" | |
117 | template class librbd::deep_copy::ImageCopyRequest<librbd::MockTestImageCtx>; | |
118 | ||
20effc67 TL |
119 | using namespace std::chrono_literals; |
120 | ||
11fdf7f2 TL |
121 | namespace librbd { |
122 | namespace deep_copy { | |
123 | ||
124 | using ::testing::_; | |
125 | using ::testing::InSequence; | |
9f95a23c | 126 | using ::testing::Invoke; |
11fdf7f2 TL |
127 | using ::testing::Return; |
128 | ||
129 | class TestMockDeepCopyImageCopyRequest : public TestMockFixture { | |
130 | public: | |
131 | typedef ImageCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest; | |
132 | typedef ObjectCopyRequest<librbd::MockTestImageCtx> MockObjectCopyRequest; | |
9f95a23c | 133 | typedef object_map::DiffRequest<librbd::MockTestImageCtx> MockDiffRequest; |
11fdf7f2 TL |
134 | |
135 | librbd::ImageCtx *m_src_image_ctx; | |
136 | librbd::ImageCtx *m_dst_image_ctx; | |
f67539c2 TL |
137 | |
138 | std::shared_ptr<librbd::AsioEngine> m_asio_engine; | |
139 | asio::ContextWQ *m_work_queue; | |
140 | ||
11fdf7f2 TL |
141 | librbd::SnapSeqs m_snap_seqs; |
142 | SnapMap m_snap_map; | |
143 | ||
144 | void SetUp() override { | |
145 | TestMockFixture::SetUp(); | |
146 | ||
147 | ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx)); | |
148 | ||
149 | librbd::RBD rbd; | |
150 | std::string dst_image_name = get_temp_image_name(); | |
151 | ASSERT_EQ(0, create_image_pp(rbd, m_ioctx, dst_image_name, m_image_size)); | |
152 | ASSERT_EQ(0, open_image(dst_image_name, &m_dst_image_ctx)); | |
153 | ||
f67539c2 TL |
154 | m_asio_engine = std::make_shared<librbd::AsioEngine>( |
155 | m_src_image_ctx->md_ctx); | |
156 | m_work_queue = m_asio_engine->get_work_queue(); | |
11fdf7f2 TL |
157 | } |
158 | ||
159 | void expect_get_image_size(librbd::MockTestImageCtx &mock_image_ctx, | |
160 | uint64_t size) { | |
161 | EXPECT_CALL(mock_image_ctx, get_image_size(_)) | |
162 | .WillOnce(Return(size)).RetiresOnSaturation(); | |
163 | } | |
164 | ||
9f95a23c TL |
165 | void expect_diff_send(MockDiffRequest& mock_request, |
166 | const BitVector<2>& diff_state, int r) { | |
167 | EXPECT_CALL(mock_request, send()) | |
168 | .WillOnce(Invoke([this, &mock_request, diff_state, r]() { | |
169 | if (r >= 0) { | |
170 | *mock_request.object_diff_state = diff_state; | |
171 | } | |
172 | m_work_queue->queue(mock_request.on_finish, r); | |
173 | })); | |
174 | } | |
175 | ||
f67539c2 TL |
176 | void expect_object_copy_send(MockObjectCopyRequest &mock_object_copy_request, |
177 | uint32_t flags) { | |
178 | EXPECT_CALL(mock_object_copy_request, send()) | |
179 | .WillOnce(Invoke([&mock_object_copy_request, flags]() { | |
180 | ASSERT_EQ(flags, mock_object_copy_request.flags); | |
181 | })); | |
11fdf7f2 TL |
182 | } |
183 | ||
184 | bool complete_object_copy(MockObjectCopyRequest &mock_object_copy_request, | |
185 | uint64_t object_num, Context **object_ctx, int r) { | |
9f95a23c | 186 | std::unique_lock locker{mock_object_copy_request.lock}; |
11fdf7f2 | 187 | while (mock_object_copy_request.object_contexts.count(object_num) == 0) { |
9f95a23c TL |
188 | if (mock_object_copy_request.cond.wait_for(locker, 10s) == |
189 | std::cv_status::timeout) { | |
11fdf7f2 TL |
190 | return false; |
191 | } | |
192 | } | |
193 | ||
194 | if (object_ctx != nullptr) { | |
195 | *object_ctx = mock_object_copy_request.object_contexts[object_num]; | |
196 | } else { | |
197 | m_work_queue->queue(mock_object_copy_request.object_contexts[object_num], | |
198 | r); | |
199 | } | |
200 | return true; | |
201 | } | |
202 | ||
203 | SnapMap wait_for_snap_map(MockObjectCopyRequest &mock_object_copy_request) { | |
9f95a23c | 204 | std::unique_lock locker{mock_object_copy_request.lock}; |
11fdf7f2 | 205 | while (mock_object_copy_request.snap_map == nullptr) { |
9f95a23c TL |
206 | if (mock_object_copy_request.cond.wait_for(locker, 10s) == |
207 | std::cv_status::timeout) { | |
11fdf7f2 TL |
208 | return SnapMap(); |
209 | } | |
210 | } | |
211 | return *mock_object_copy_request.snap_map; | |
212 | } | |
213 | ||
214 | int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name, | |
215 | librados::snap_t *snap_id) { | |
f67539c2 | 216 | NoOpProgressContext prog_ctx; |
11fdf7f2 | 217 | int r = image_ctx->operations->snap_create( |
f67539c2 | 218 | cls::rbd::UserSnapshotNamespace(), snap_name, 0, prog_ctx); |
11fdf7f2 TL |
219 | if (r < 0) { |
220 | return r; | |
221 | } | |
222 | ||
223 | r = image_ctx->state->refresh(); | |
224 | if (r < 0) { | |
225 | return r; | |
226 | } | |
227 | ||
228 | if (image_ctx->snap_ids.count({cls::rbd::UserSnapshotNamespace(), | |
229 | snap_name}) == 0) { | |
230 | return -ENOENT; | |
231 | } | |
232 | ||
233 | if (snap_id != nullptr) { | |
234 | *snap_id = image_ctx->snap_ids[{cls::rbd::UserSnapshotNamespace(), | |
235 | snap_name}]; | |
236 | } | |
237 | return 0; | |
238 | } | |
239 | ||
240 | int create_snap(const char* snap_name, | |
241 | librados::snap_t *src_snap_id_ = nullptr) { | |
242 | librados::snap_t src_snap_id; | |
243 | int r = create_snap(m_src_image_ctx, snap_name, &src_snap_id); | |
244 | if (r < 0) { | |
245 | return r; | |
246 | } | |
247 | ||
248 | if (src_snap_id_ != nullptr) { | |
249 | *src_snap_id_ = src_snap_id; | |
250 | } | |
251 | ||
252 | librados::snap_t dst_snap_id; | |
253 | r = create_snap(m_dst_image_ctx, snap_name, &dst_snap_id); | |
254 | if (r < 0) { | |
255 | return r; | |
256 | } | |
257 | ||
258 | // collection of all existing snaps in dst image | |
259 | SnapIds dst_snap_ids({dst_snap_id}); | |
260 | if (!m_snap_map.empty()) { | |
261 | dst_snap_ids.insert(dst_snap_ids.end(), | |
262 | m_snap_map.rbegin()->second.begin(), | |
263 | m_snap_map.rbegin()->second.end()); | |
264 | } | |
265 | m_snap_map[src_snap_id] = dst_snap_ids; | |
266 | m_snap_seqs[src_snap_id] = dst_snap_id; | |
267 | return 0; | |
268 | } | |
269 | }; | |
270 | ||
271 | TEST_F(TestMockDeepCopyImageCopyRequest, SimpleImage) { | |
272 | librados::snap_t snap_id_end; | |
273 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
274 | ||
275 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
276 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
277 | MockObjectCopyRequest mock_object_copy_request; | |
278 | ||
279 | InSequence seq; | |
9f95a23c TL |
280 | MockDiffRequest mock_diff_request; |
281 | expect_diff_send(mock_diff_request, {}, -EINVAL); | |
11fdf7f2 TL |
282 | expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order); |
283 | expect_get_image_size(mock_src_image_ctx, 0); | |
f67539c2 TL |
284 | expect_object_copy_send(mock_object_copy_request, 0); |
285 | ||
286 | librbd::deep_copy::NoOpHandler no_op; | |
287 | C_SaferCond ctx; | |
288 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
289 | &mock_dst_image_ctx, | |
290 | 0, snap_id_end, 0, false, boost::none, | |
291 | m_snap_seqs, &no_op, &ctx); | |
292 | request->send(); | |
293 | ||
294 | ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request)); | |
295 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0)); | |
296 | ASSERT_EQ(0, ctx.wait()); | |
297 | } | |
298 | ||
299 | TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffNonExistent) { | |
300 | librados::snap_t snap_id_end; | |
301 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
302 | ||
303 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
304 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
305 | ||
306 | InSequence seq; | |
307 | ||
308 | MockDiffRequest mock_diff_request; | |
309 | BitVector<2> diff_state; | |
310 | diff_state.resize(1); | |
311 | expect_diff_send(mock_diff_request, diff_state, 0); | |
312 | ||
313 | expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order); | |
314 | expect_get_image_size(mock_src_image_ctx, 0); | |
2a845540 | 315 | expect_op_work_queue(mock_src_image_ctx); |
f67539c2 TL |
316 | |
317 | librbd::deep_copy::NoOpHandler no_op; | |
318 | C_SaferCond ctx; | |
319 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
320 | &mock_dst_image_ctx, | |
321 | 0, snap_id_end, 0, false, boost::none, | |
322 | m_snap_seqs, &no_op, &ctx); | |
323 | request->send(); | |
324 | ||
325 | ASSERT_EQ(0, ctx.wait()); | |
326 | } | |
327 | ||
328 | TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffExistsDirty) { | |
329 | librados::snap_t snap_id_end; | |
330 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
331 | ||
332 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
333 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
334 | ||
335 | InSequence seq; | |
336 | ||
337 | MockDiffRequest mock_diff_request; | |
338 | BitVector<2> diff_state; | |
339 | diff_state.resize(1); | |
340 | diff_state[0] = object_map::DIFF_STATE_DATA_UPDATED; | |
341 | expect_diff_send(mock_diff_request, diff_state, 0); | |
342 | ||
343 | expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order); | |
344 | expect_get_image_size(mock_src_image_ctx, 0); | |
345 | MockObjectCopyRequest mock_object_copy_request; | |
346 | expect_object_copy_send(mock_object_copy_request, 0); | |
11fdf7f2 | 347 | |
1911f103 | 348 | librbd::deep_copy::NoOpHandler no_op; |
11fdf7f2 TL |
349 | C_SaferCond ctx; |
350 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
351 | &mock_dst_image_ctx, | |
9f95a23c | 352 | 0, snap_id_end, 0, false, boost::none, |
11fdf7f2 TL |
353 | m_snap_seqs, &no_op, &ctx); |
354 | request->send(); | |
355 | ||
356 | ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request)); | |
357 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0)); | |
358 | ASSERT_EQ(0, ctx.wait()); | |
359 | } | |
360 | ||
f67539c2 | 361 | TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffExistsClean) { |
9f95a23c TL |
362 | librados::snap_t snap_id_end; |
363 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
364 | ||
365 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
366 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
367 | ||
368 | InSequence seq; | |
369 | ||
370 | MockDiffRequest mock_diff_request; | |
371 | BitVector<2> diff_state; | |
372 | diff_state.resize(1); | |
f67539c2 | 373 | diff_state[0] = object_map::DIFF_STATE_DATA; |
9f95a23c TL |
374 | expect_diff_send(mock_diff_request, diff_state, 0); |
375 | ||
376 | expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order); | |
377 | expect_get_image_size(mock_src_image_ctx, 0); | |
f67539c2 TL |
378 | MockObjectCopyRequest mock_object_copy_request; |
379 | expect_object_copy_send(mock_object_copy_request, | |
380 | OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN); | |
9f95a23c | 381 | |
1911f103 | 382 | librbd::deep_copy::NoOpHandler no_op; |
9f95a23c TL |
383 | C_SaferCond ctx; |
384 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
385 | &mock_dst_image_ctx, | |
386 | 0, snap_id_end, 0, false, boost::none, | |
387 | m_snap_seqs, &no_op, &ctx); | |
388 | request->send(); | |
389 | ||
f67539c2 TL |
390 | ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request)); |
391 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0)); | |
9f95a23c TL |
392 | ASSERT_EQ(0, ctx.wait()); |
393 | } | |
394 | ||
2a845540 TL |
395 | TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffMix) { |
396 | librados::snap_t snap_id_end; | |
397 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
398 | ||
399 | uint64_t object_count = 12; | |
400 | ||
401 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
402 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
403 | MockObjectCopyRequest mock_object_copy_request; | |
404 | ||
405 | InSequence seq; | |
406 | ||
407 | MockDiffRequest mock_diff_request; | |
408 | BitVector<2> diff_state; | |
409 | diff_state.resize(object_count); | |
410 | diff_state[1] = object_map::DIFF_STATE_DATA_UPDATED; | |
411 | diff_state[2] = object_map::DIFF_STATE_DATA_UPDATED; | |
412 | diff_state[3] = object_map::DIFF_STATE_DATA; | |
413 | diff_state[5] = object_map::DIFF_STATE_DATA_UPDATED; | |
414 | diff_state[8] = object_map::DIFF_STATE_DATA; | |
415 | diff_state[9] = object_map::DIFF_STATE_DATA; | |
416 | diff_state[10] = object_map::DIFF_STATE_DATA_UPDATED; | |
417 | expect_diff_send(mock_diff_request, diff_state, 0); | |
418 | ||
419 | expect_get_image_size(mock_src_image_ctx, | |
420 | object_count * (1 << m_src_image_ctx->order)); | |
421 | expect_get_image_size(mock_src_image_ctx, 0); | |
422 | ||
423 | expect_op_work_queue(mock_src_image_ctx); | |
424 | expect_object_copy_send(mock_object_copy_request, 0); | |
425 | expect_object_copy_send(mock_object_copy_request, 0); | |
426 | expect_object_copy_send(mock_object_copy_request, | |
427 | OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN); | |
428 | expect_op_work_queue(mock_src_image_ctx); | |
429 | expect_object_copy_send(mock_object_copy_request, 0); | |
430 | expect_op_work_queue(mock_src_image_ctx); | |
431 | expect_object_copy_send(mock_object_copy_request, | |
432 | OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN); | |
433 | expect_object_copy_send(mock_object_copy_request, | |
434 | OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN); | |
435 | expect_object_copy_send(mock_object_copy_request, 0); | |
436 | expect_op_work_queue(mock_src_image_ctx); | |
437 | ||
438 | std::vector<bool> seen(object_count); | |
439 | struct Handler : public librbd::deep_copy::NoOpHandler { | |
440 | Handler(std::vector<bool>* seen) : m_seen(seen) {} | |
441 | ||
442 | int update_progress(uint64_t object_no, uint64_t end_object_no) override { | |
443 | EXPECT_THAT(object_no, ::testing::AllOf(::testing::Ge(1), | |
444 | ::testing::Le(m_seen->size()))); | |
445 | EXPECT_EQ(end_object_no, m_seen->size()); | |
446 | EXPECT_FALSE((*m_seen)[object_no - 1]); | |
447 | (*m_seen)[object_no - 1] = true; | |
448 | return 0; | |
449 | } | |
450 | ||
451 | std::vector<bool>* m_seen; | |
452 | } handler(&seen); | |
453 | ||
454 | C_SaferCond ctx; | |
455 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
456 | &mock_dst_image_ctx, | |
457 | 0, snap_id_end, 0, false, boost::none, | |
458 | m_snap_seqs, &handler, &ctx); | |
459 | request->send(); | |
460 | ||
461 | ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request)); | |
462 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0)); | |
463 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 2, nullptr, 0)); | |
464 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 3, nullptr, 0)); | |
465 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 5, nullptr, 0)); | |
466 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 8, nullptr, 0)); | |
467 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 9, nullptr, 0)); | |
468 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 10, nullptr, 0)); | |
469 | ASSERT_EQ(0, ctx.wait()); | |
470 | ||
471 | EXPECT_THAT(seen, ::testing::Each(::testing::IsTrue())); | |
472 | } | |
473 | ||
11fdf7f2 TL |
474 | TEST_F(TestMockDeepCopyImageCopyRequest, OutOfOrder) { |
475 | std::string max_ops_str; | |
476 | ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str)); | |
477 | ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "10")); | |
478 | BOOST_SCOPE_EXIT( (max_ops_str) ) { | |
479 | ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", | |
480 | max_ops_str.c_str())); | |
481 | } BOOST_SCOPE_EXIT_END; | |
482 | ||
483 | librados::snap_t snap_id_end; | |
484 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
485 | ||
486 | uint64_t object_count = 55; | |
487 | ||
488 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
489 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
490 | MockObjectCopyRequest mock_object_copy_request; | |
491 | ||
9f95a23c TL |
492 | MockDiffRequest mock_diff_request; |
493 | expect_diff_send(mock_diff_request, {}, -EINVAL); | |
11fdf7f2 TL |
494 | expect_get_image_size(mock_src_image_ctx, |
495 | object_count * (1 << m_src_image_ctx->order)); | |
496 | expect_get_image_size(mock_src_image_ctx, 0); | |
497 | ||
498 | EXPECT_CALL(mock_object_copy_request, send()).Times(object_count); | |
499 | ||
1911f103 | 500 | class Handler : public librbd::deep_copy::NoOpHandler { |
11fdf7f2 TL |
501 | public: |
502 | uint64_t object_count; | |
503 | librbd::deep_copy::ObjectNumber expected_object_number; | |
504 | ||
1911f103 | 505 | Handler(uint64_t object_count) |
11fdf7f2 TL |
506 | : object_count(object_count) { |
507 | } | |
508 | ||
509 | int update_progress(uint64_t object_no, uint64_t end_object_no) override { | |
510 | EXPECT_LE(object_no, object_count); | |
511 | EXPECT_EQ(end_object_no, object_count); | |
512 | if (!expected_object_number) { | |
513 | expected_object_number = 0; | |
514 | } else { | |
515 | expected_object_number = *expected_object_number + 1; | |
516 | } | |
517 | EXPECT_EQ(*expected_object_number, object_no - 1); | |
518 | ||
519 | return 0; | |
520 | } | |
1911f103 | 521 | } handler(object_count); |
11fdf7f2 TL |
522 | |
523 | C_SaferCond ctx; | |
524 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
525 | &mock_dst_image_ctx, | |
9f95a23c | 526 | 0, snap_id_end, 0, false, boost::none, |
1911f103 | 527 | m_snap_seqs, &handler, &ctx); |
11fdf7f2 TL |
528 | request->send(); |
529 | ||
530 | std::map<uint64_t, Context*> copy_contexts; | |
531 | ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request)); | |
532 | for (uint64_t i = 0; i < object_count; ++i) { | |
533 | if (i % 10 == 0) { | |
534 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, i, | |
535 | ©_contexts[i], 0)); | |
536 | } else { | |
537 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, i, nullptr, | |
538 | 0)); | |
539 | } | |
540 | } | |
541 | ||
542 | for (auto& pair : copy_contexts) { | |
543 | pair.second->complete(0); | |
544 | } | |
545 | ||
546 | ASSERT_EQ(0, ctx.wait()); | |
547 | } | |
548 | ||
549 | TEST_F(TestMockDeepCopyImageCopyRequest, SnapshotSubset) { | |
550 | librados::snap_t snap_id_start; | |
551 | librados::snap_t snap_id_end; | |
552 | ASSERT_EQ(0, create_snap("snap1")); | |
553 | ASSERT_EQ(0, create_snap("snap2", &snap_id_start)); | |
554 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
555 | ||
556 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
557 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
558 | MockObjectCopyRequest mock_object_copy_request; | |
559 | ||
560 | InSequence seq; | |
9f95a23c TL |
561 | MockDiffRequest mock_diff_request; |
562 | expect_diff_send(mock_diff_request, {}, -EINVAL); | |
11fdf7f2 TL |
563 | expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order); |
564 | expect_get_image_size(mock_src_image_ctx, 0); | |
565 | expect_get_image_size(mock_src_image_ctx, 0); | |
566 | expect_get_image_size(mock_src_image_ctx, 0); | |
f67539c2 | 567 | expect_object_copy_send(mock_object_copy_request, 0); |
11fdf7f2 | 568 | |
1911f103 | 569 | librbd::deep_copy::NoOpHandler no_op; |
11fdf7f2 TL |
570 | C_SaferCond ctx; |
571 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
572 | &mock_dst_image_ctx, | |
9f95a23c | 573 | snap_id_start, snap_id_end, 0, false, |
11fdf7f2 TL |
574 | boost::none, m_snap_seqs, &no_op, |
575 | &ctx); | |
576 | request->send(); | |
577 | ||
578 | SnapMap snap_map(m_snap_map); | |
579 | snap_map.erase(snap_map.begin()); | |
580 | ASSERT_EQ(snap_map, wait_for_snap_map(mock_object_copy_request)); | |
581 | ||
582 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0)); | |
583 | ASSERT_EQ(0, ctx.wait()); | |
584 | } | |
585 | ||
586 | TEST_F(TestMockDeepCopyImageCopyRequest, RestartPartialSync) { | |
587 | librados::snap_t snap_id_end; | |
588 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
589 | ||
590 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
591 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
592 | MockObjectCopyRequest mock_object_copy_request; | |
593 | ||
594 | InSequence seq; | |
9f95a23c TL |
595 | MockDiffRequest mock_diff_request; |
596 | expect_diff_send(mock_diff_request, {}, -EINVAL); | |
11fdf7f2 TL |
597 | expect_get_image_size(mock_src_image_ctx, 2 * (1 << m_src_image_ctx->order)); |
598 | expect_get_image_size(mock_src_image_ctx, 0); | |
f67539c2 | 599 | expect_object_copy_send(mock_object_copy_request, 0); |
11fdf7f2 | 600 | |
1911f103 | 601 | librbd::deep_copy::NoOpHandler no_op; |
11fdf7f2 TL |
602 | C_SaferCond ctx; |
603 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
604 | &mock_dst_image_ctx, | |
9f95a23c | 605 | 0, snap_id_end, 0, false, |
11fdf7f2 TL |
606 | librbd::deep_copy::ObjectNumber{0U}, |
607 | m_snap_seqs, &no_op, &ctx); | |
608 | request->send(); | |
609 | ||
610 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0)); | |
611 | ASSERT_EQ(0, ctx.wait()); | |
612 | } | |
613 | ||
614 | TEST_F(TestMockDeepCopyImageCopyRequest, Cancel) { | |
615 | std::string max_ops_str; | |
616 | ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str)); | |
617 | ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "1")); | |
618 | BOOST_SCOPE_EXIT( (max_ops_str) ) { | |
619 | ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", | |
620 | max_ops_str.c_str())); | |
621 | } BOOST_SCOPE_EXIT_END; | |
622 | ||
623 | librados::snap_t snap_id_end; | |
624 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
625 | ||
626 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
627 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
628 | MockObjectCopyRequest mock_object_copy_request; | |
629 | ||
630 | InSequence seq; | |
9f95a23c TL |
631 | MockDiffRequest mock_diff_request; |
632 | expect_diff_send(mock_diff_request, {}, -EINVAL); | |
11fdf7f2 TL |
633 | expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order); |
634 | expect_get_image_size(mock_src_image_ctx, 0); | |
f67539c2 | 635 | expect_object_copy_send(mock_object_copy_request, 0); |
11fdf7f2 | 636 | |
1911f103 | 637 | librbd::deep_copy::NoOpHandler no_op; |
11fdf7f2 TL |
638 | C_SaferCond ctx; |
639 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
640 | &mock_dst_image_ctx, | |
9f95a23c | 641 | 0, snap_id_end, 0, false, boost::none, |
11fdf7f2 TL |
642 | m_snap_seqs, &no_op, &ctx); |
643 | request->send(); | |
644 | ||
645 | ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request)); | |
646 | request->cancel(); | |
647 | ||
648 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0)); | |
649 | ASSERT_EQ(-ECANCELED, ctx.wait()); | |
650 | } | |
651 | ||
652 | TEST_F(TestMockDeepCopyImageCopyRequest, Cancel_Inflight_Sync) { | |
653 | std::string max_ops_str; | |
654 | ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str)); | |
655 | ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "3")); | |
656 | BOOST_SCOPE_EXIT( (max_ops_str) ) { | |
657 | ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", | |
658 | max_ops_str.c_str())); | |
659 | } BOOST_SCOPE_EXIT_END; | |
660 | ||
661 | librados::snap_t snap_id_end; | |
662 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
663 | ||
664 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
665 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
666 | MockObjectCopyRequest mock_object_copy_request; | |
667 | ||
668 | InSequence seq; | |
9f95a23c TL |
669 | MockDiffRequest mock_diff_request; |
670 | expect_diff_send(mock_diff_request, {}, -EINVAL); | |
11fdf7f2 TL |
671 | expect_get_image_size(mock_src_image_ctx, 6 * (1 << m_src_image_ctx->order)); |
672 | expect_get_image_size(mock_src_image_ctx, m_image_size); | |
673 | ||
674 | EXPECT_CALL(mock_object_copy_request, send()).Times(6); | |
675 | ||
1911f103 | 676 | struct Handler : public librbd::deep_copy::NoOpHandler { |
11fdf7f2 TL |
677 | librbd::deep_copy::ObjectNumber object_number; |
678 | ||
679 | int update_progress(uint64_t object_no, uint64_t end_object_no) override { | |
680 | object_number = object_number ? *object_number + 1 : 0; | |
681 | return 0; | |
682 | } | |
1911f103 | 683 | } handler; |
11fdf7f2 TL |
684 | |
685 | C_SaferCond ctx; | |
686 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
687 | &mock_dst_image_ctx, | |
9f95a23c | 688 | 0, snap_id_end, 0, false, boost::none, |
1911f103 | 689 | m_snap_seqs, &handler, &ctx); |
11fdf7f2 TL |
690 | request->send(); |
691 | ||
692 | ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request)); | |
693 | ||
694 | Context *cancel_ctx = nullptr; | |
695 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0)); | |
696 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0)); | |
697 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 2, nullptr, 0)); | |
698 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 3, &cancel_ctx, | |
699 | 0)); | |
700 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 4, nullptr, 0)); | |
701 | ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 5, nullptr, 0)); | |
702 | ||
703 | request->cancel(); | |
704 | cancel_ctx->complete(0); | |
705 | ||
706 | ASSERT_EQ(-ECANCELED, ctx.wait()); | |
1911f103 | 707 | ASSERT_EQ(5u, handler.object_number.get()); |
11fdf7f2 TL |
708 | } |
709 | ||
2a845540 TL |
710 | TEST_F(TestMockDeepCopyImageCopyRequest, CancelBeforeSend) { |
711 | librados::snap_t snap_id_end; | |
712 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
713 | ||
714 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
715 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
716 | ||
717 | InSequence seq; | |
718 | ||
719 | MockDiffRequest mock_diff_request; | |
720 | expect_diff_send(mock_diff_request, {}, -EINVAL); | |
721 | expect_get_image_size(mock_src_image_ctx, 2 * (1 << m_src_image_ctx->order)); | |
722 | expect_get_image_size(mock_src_image_ctx, 0); | |
723 | ||
724 | librbd::deep_copy::NoOpHandler no_op; | |
725 | C_SaferCond ctx; | |
726 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
727 | &mock_dst_image_ctx, | |
728 | 0, snap_id_end, 0, false, boost::none, | |
729 | m_snap_seqs, &no_op, &ctx); | |
730 | request->cancel(); | |
731 | request->send(); | |
732 | ||
733 | ASSERT_EQ(-ECANCELED, ctx.wait()); | |
734 | } | |
735 | ||
11fdf7f2 TL |
736 | TEST_F(TestMockDeepCopyImageCopyRequest, MissingSnap) { |
737 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
738 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
739 | ||
1911f103 | 740 | librbd::deep_copy::NoOpHandler no_op; |
11fdf7f2 TL |
741 | C_SaferCond ctx; |
742 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
743 | &mock_dst_image_ctx, | |
9f95a23c | 744 | 0, 123, 0, false, boost::none, |
11fdf7f2 TL |
745 | m_snap_seqs, &no_op, &ctx); |
746 | request->send(); | |
747 | ASSERT_EQ(-EINVAL, ctx.wait()); | |
748 | } | |
749 | ||
750 | TEST_F(TestMockDeepCopyImageCopyRequest, MissingFromSnap) { | |
751 | librados::snap_t snap_id_end; | |
752 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
753 | ||
754 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
755 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
756 | ||
1911f103 | 757 | librbd::deep_copy::NoOpHandler no_op; |
11fdf7f2 TL |
758 | C_SaferCond ctx; |
759 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
760 | &mock_dst_image_ctx, | |
9f95a23c TL |
761 | 123, snap_id_end, 0, false, |
762 | boost::none, m_snap_seqs, &no_op, | |
763 | &ctx); | |
11fdf7f2 TL |
764 | request->send(); |
765 | ASSERT_EQ(-EINVAL, ctx.wait()); | |
766 | } | |
767 | ||
768 | TEST_F(TestMockDeepCopyImageCopyRequest, EmptySnapMap) { | |
769 | librados::snap_t snap_id_start; | |
770 | librados::snap_t snap_id_end; | |
771 | ASSERT_EQ(0, create_snap("snap1", &snap_id_start)); | |
772 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
773 | ||
774 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
775 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
776 | ||
1911f103 | 777 | librbd::deep_copy::NoOpHandler no_op; |
11fdf7f2 TL |
778 | C_SaferCond ctx; |
779 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
780 | &mock_dst_image_ctx, | |
9f95a23c | 781 | snap_id_start, snap_id_end, 0, false, |
11fdf7f2 TL |
782 | boost::none, {{0, 0}}, &no_op, &ctx); |
783 | request->send(); | |
784 | ASSERT_EQ(-EINVAL, ctx.wait()); | |
785 | } | |
786 | ||
787 | TEST_F(TestMockDeepCopyImageCopyRequest, EmptySnapSeqs) { | |
788 | librados::snap_t snap_id_start; | |
789 | librados::snap_t snap_id_end; | |
790 | ASSERT_EQ(0, create_snap("snap1", &snap_id_start)); | |
791 | ASSERT_EQ(0, create_snap("copy", &snap_id_end)); | |
792 | ||
793 | librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); | |
794 | librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); | |
795 | ||
1911f103 | 796 | librbd::deep_copy::NoOpHandler no_op; |
11fdf7f2 TL |
797 | C_SaferCond ctx; |
798 | auto request = new MockImageCopyRequest(&mock_src_image_ctx, | |
799 | &mock_dst_image_ctx, | |
9f95a23c | 800 | snap_id_start, snap_id_end, 0, false, |
11fdf7f2 TL |
801 | boost::none, {}, &no_op, &ctx); |
802 | request->send(); | |
803 | ASSERT_EQ(-EINVAL, ctx.wait()); | |
804 | } | |
805 | ||
806 | } // namespace deep_copy | |
807 | } // namespace librbd |