]>
Commit | Line | Data |
---|---|---|
eafe8130 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/rbd_mirror/test_mock_fixture.h" | |
5 | #include "cls/rbd/cls_rbd_types.h" | |
6 | #include "librbd/ImageCtx.h" | |
7 | #include "librbd/TrashWatcher.h" | |
8 | #include "librbd/Utils.h" | |
9 | #include "librbd/trash/RemoveRequest.h" | |
10 | #include "tools/rbd_mirror/Threads.h" | |
11 | #include "tools/rbd_mirror/image_deleter/SnapshotPurgeRequest.h" | |
12 | #include "tools/rbd_mirror/image_deleter/TrashRemoveRequest.h" | |
13 | #include "test/librados_test_stub/MockTestMemIoCtxImpl.h" | |
14 | #include "test/librbd/mock/MockImageCtx.h" | |
15 | ||
16 | namespace librbd { | |
17 | ||
18 | namespace { | |
19 | ||
20 | struct MockTestImageCtx : public librbd::MockImageCtx { | |
21 | MockTestImageCtx(librbd::ImageCtx &image_ctx) | |
22 | : librbd::MockImageCtx(image_ctx) { | |
23 | } | |
24 | }; | |
25 | ||
26 | } // anonymous namespace | |
27 | ||
28 | template<> | |
29 | struct TrashWatcher<MockTestImageCtx> { | |
30 | static TrashWatcher* s_instance; | |
31 | static void notify_image_removed(librados::IoCtx&, | |
32 | const std::string& image_id, Context *ctx) { | |
33 | ceph_assert(s_instance != nullptr); | |
34 | s_instance->notify_image_removed(image_id, ctx); | |
35 | } | |
36 | ||
37 | MOCK_METHOD2(notify_image_removed, void(const std::string&, Context*)); | |
38 | ||
39 | TrashWatcher() { | |
40 | s_instance = this; | |
41 | } | |
42 | }; | |
43 | ||
44 | TrashWatcher<MockTestImageCtx>* TrashWatcher<MockTestImageCtx>::s_instance = nullptr; | |
45 | ||
46 | namespace trash { | |
47 | ||
48 | template <> | |
49 | struct RemoveRequest<librbd::MockTestImageCtx> { | |
50 | static RemoveRequest *s_instance; | |
51 | Context *on_finish = nullptr; | |
52 | ||
53 | static RemoveRequest *create(librados::IoCtx &io_ctx, | |
54 | const std::string &image_id, | |
55 | ContextWQ *work_queue, | |
56 | bool force, | |
57 | librbd::ProgressContext &progress_ctx, | |
58 | Context *on_finish) { | |
59 | ceph_assert(s_instance != nullptr); | |
60 | EXPECT_TRUE(force); | |
61 | s_instance->construct(image_id); | |
62 | s_instance->on_finish = on_finish; | |
63 | return s_instance; | |
64 | } | |
65 | ||
66 | MOCK_METHOD1(construct, void(const std::string&)); | |
67 | MOCK_METHOD0(send, void()); | |
68 | ||
69 | RemoveRequest() { | |
70 | s_instance = this; | |
71 | } | |
72 | }; | |
73 | ||
74 | RemoveRequest<librbd::MockTestImageCtx>* RemoveRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
75 | ||
76 | } // namespace trash | |
77 | } // namespace librbd | |
78 | ||
79 | namespace rbd { | |
80 | namespace mirror { | |
81 | namespace image_deleter { | |
82 | ||
83 | template <> | |
84 | struct SnapshotPurgeRequest<librbd::MockTestImageCtx> { | |
85 | static SnapshotPurgeRequest *s_instance; | |
86 | Context *on_finish = nullptr; | |
87 | ||
88 | static SnapshotPurgeRequest *create(librados::IoCtx &io_ctx, | |
89 | const std::string &image_id, | |
90 | Context *on_finish) { | |
91 | ceph_assert(s_instance != nullptr); | |
92 | s_instance->construct(image_id); | |
93 | s_instance->on_finish = on_finish; | |
94 | return s_instance; | |
95 | } | |
96 | ||
97 | MOCK_METHOD1(construct, void(const std::string&)); | |
98 | MOCK_METHOD0(send, void()); | |
99 | ||
100 | SnapshotPurgeRequest() { | |
101 | s_instance = this; | |
102 | } | |
103 | }; | |
104 | ||
105 | SnapshotPurgeRequest<librbd::MockTestImageCtx>* SnapshotPurgeRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
106 | ||
107 | } // namespace image_deleter | |
108 | } // namespace mirror | |
109 | } // namespace rbd | |
110 | ||
111 | #include "tools/rbd_mirror/image_deleter/TrashRemoveRequest.cc" | |
112 | ||
113 | namespace rbd { | |
114 | namespace mirror { | |
115 | namespace image_deleter { | |
116 | ||
117 | using ::testing::_; | |
9f95a23c | 118 | using ::testing::DoAll; |
eafe8130 TL |
119 | using ::testing::Invoke; |
120 | using ::testing::InSequence; | |
121 | using ::testing::Return; | |
122 | using ::testing::StrEq; | |
123 | using ::testing::WithArg; | |
124 | using ::testing::WithArgs; | |
125 | ||
126 | class TestMockImageDeleterTrashRemoveRequest : public TestMockFixture { | |
127 | public: | |
128 | typedef TrashRemoveRequest<librbd::MockTestImageCtx> MockTrashRemoveRequest; | |
129 | typedef SnapshotPurgeRequest<librbd::MockTestImageCtx> MockSnapshotPurgeRequest; | |
130 | typedef librbd::TrashWatcher<librbd::MockTestImageCtx> MockTrashWatcher; | |
131 | typedef librbd::trash::RemoveRequest<librbd::MockTestImageCtx> MockLibrbdTrashRemoveRequest; | |
132 | ||
133 | void expect_trash_get(const cls::rbd::TrashImageSpec& trash_spec, int r) { | |
134 | using ceph::encode; | |
135 | EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx), | |
136 | exec(StrEq(RBD_TRASH), _, StrEq("rbd"), | |
137 | StrEq("trash_get"), _, _, _)) | |
138 | .WillOnce(WithArg<5>(Invoke([trash_spec, r](bufferlist* bl) { | |
139 | encode(trash_spec, *bl); | |
140 | return r; | |
141 | }))); | |
142 | } | |
143 | ||
144 | void expect_trash_state_set(const std::string& image_id, int r) { | |
145 | bufferlist in_bl; | |
146 | encode(image_id, in_bl); | |
147 | encode(cls::rbd::TRASH_IMAGE_STATE_REMOVING, in_bl); | |
148 | encode(cls::rbd::TRASH_IMAGE_STATE_NORMAL, in_bl); | |
149 | ||
150 | EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx), | |
151 | exec(StrEq(RBD_TRASH), _, StrEq("rbd"), | |
152 | StrEq("trash_state_set"), | |
153 | ContentsEqual(in_bl), _, _)) | |
154 | .WillOnce(Return(r)); | |
155 | } | |
156 | ||
157 | void expect_get_snapcontext(const std::string& image_id, | |
158 | const ::SnapContext &snapc, int r) { | |
159 | bufferlist bl; | |
160 | encode(snapc, bl); | |
161 | ||
162 | EXPECT_CALL(get_mock_io_ctx(m_local_io_ctx), | |
163 | exec(librbd::util::header_name(image_id), _, StrEq("rbd"), | |
164 | StrEq("get_snapcontext"), _, _, _)) | |
165 | .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) { | |
166 | *out_bl = bl; | |
167 | })), | |
168 | Return(r))); | |
169 | } | |
170 | ||
171 | void expect_snapshot_purge(MockSnapshotPurgeRequest &snapshot_purge_request, | |
172 | const std::string &image_id, int r) { | |
173 | EXPECT_CALL(snapshot_purge_request, construct(image_id)); | |
174 | EXPECT_CALL(snapshot_purge_request, send()) | |
175 | .WillOnce(Invoke([this, &snapshot_purge_request, r]() { | |
176 | m_threads->work_queue->queue( | |
177 | snapshot_purge_request.on_finish, r); | |
178 | })); | |
179 | } | |
180 | ||
181 | void expect_image_remove(MockLibrbdTrashRemoveRequest &image_remove_request, | |
182 | const std::string &image_id, int r) { | |
183 | EXPECT_CALL(image_remove_request, construct(image_id)); | |
184 | EXPECT_CALL(image_remove_request, send()) | |
185 | .WillOnce(Invoke([this, &image_remove_request, r]() { | |
186 | m_threads->work_queue->queue( | |
187 | image_remove_request.on_finish, r); | |
188 | })); | |
189 | } | |
190 | ||
191 | void expect_notify_image_removed(MockTrashWatcher& mock_trash_watcher, | |
192 | const std::string& image_id) { | |
193 | EXPECT_CALL(mock_trash_watcher, notify_image_removed(image_id, _)) | |
194 | .WillOnce(WithArg<1>(Invoke([this](Context *ctx) { | |
195 | m_threads->work_queue->queue(ctx, 0); | |
196 | }))); | |
197 | } | |
198 | ||
199 | }; | |
200 | ||
201 | TEST_F(TestMockImageDeleterTrashRemoveRequest, Success) { | |
202 | InSequence seq; | |
203 | ||
204 | cls::rbd::TrashImageSpec trash_image_spec{ | |
205 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
206 | expect_trash_get(trash_image_spec, 0); | |
207 | ||
208 | expect_trash_state_set("image id", 0); | |
209 | ||
210 | expect_get_snapcontext("image id", {1, {1}}, 0); | |
211 | ||
212 | MockSnapshotPurgeRequest mock_snapshot_purge_request; | |
213 | expect_snapshot_purge(mock_snapshot_purge_request, "image id", 0); | |
214 | ||
215 | MockLibrbdTrashRemoveRequest mock_image_remove_request; | |
216 | expect_image_remove(mock_image_remove_request, "image id", 0); | |
217 | ||
218 | MockTrashWatcher mock_trash_watcher; | |
219 | expect_notify_image_removed(mock_trash_watcher, "image id"); | |
220 | ||
221 | C_SaferCond ctx; | |
222 | ErrorResult error_result; | |
223 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
224 | &error_result, | |
225 | m_threads->work_queue, &ctx); | |
226 | req->send(); | |
227 | ASSERT_EQ(0, ctx.wait()); | |
228 | } | |
229 | ||
230 | TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashDNE) { | |
231 | InSequence seq; | |
232 | ||
233 | cls::rbd::TrashImageSpec trash_image_spec{ | |
234 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
235 | expect_trash_get(trash_image_spec, -ENOENT); | |
236 | ||
237 | C_SaferCond ctx; | |
238 | ErrorResult error_result; | |
239 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
240 | &error_result, | |
241 | m_threads->work_queue, &ctx); | |
242 | req->send(); | |
243 | ASSERT_EQ(0, ctx.wait()); | |
244 | } | |
245 | ||
246 | TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashError) { | |
247 | InSequence seq; | |
248 | ||
249 | cls::rbd::TrashImageSpec trash_image_spec{ | |
250 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
251 | expect_trash_get(trash_image_spec, -EPERM); | |
252 | ||
253 | C_SaferCond ctx; | |
254 | ErrorResult error_result; | |
255 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
256 | &error_result, | |
257 | m_threads->work_queue, &ctx); | |
258 | req->send(); | |
259 | ASSERT_EQ(-EPERM, ctx.wait()); | |
260 | } | |
261 | ||
262 | TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashSourceIncorrect) { | |
263 | InSequence seq; | |
264 | ||
265 | cls::rbd::TrashImageSpec trash_image_spec{ | |
266 | cls::rbd::TRASH_IMAGE_SOURCE_USER, "image name", {}, {}}; | |
267 | expect_trash_get(trash_image_spec, 0); | |
268 | ||
269 | C_SaferCond ctx; | |
270 | ErrorResult error_result; | |
271 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
272 | &error_result, | |
273 | m_threads->work_queue, &ctx); | |
274 | req->send(); | |
275 | ASSERT_EQ(0, ctx.wait()); | |
276 | } | |
277 | ||
278 | TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashStateIncorrect) { | |
279 | InSequence seq; | |
280 | ||
281 | cls::rbd::TrashImageSpec trash_image_spec{ | |
282 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
283 | trash_image_spec.state = cls::rbd::TRASH_IMAGE_STATE_RESTORING; | |
284 | expect_trash_get(trash_image_spec, 0); | |
285 | ||
286 | C_SaferCond ctx; | |
287 | ErrorResult error_result; | |
288 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
289 | &error_result, | |
290 | m_threads->work_queue, &ctx); | |
291 | req->send(); | |
292 | ASSERT_EQ(-EBUSY, ctx.wait()); | |
293 | ASSERT_EQ(ERROR_RESULT_RETRY_IMMEDIATELY, error_result); | |
294 | } | |
295 | ||
296 | TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashSetStateDNE) { | |
297 | InSequence seq; | |
298 | ||
299 | cls::rbd::TrashImageSpec trash_image_spec{ | |
300 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
301 | expect_trash_get(trash_image_spec, 0); | |
302 | ||
303 | expect_trash_state_set("image id", -ENOENT); | |
304 | ||
305 | C_SaferCond ctx; | |
306 | ErrorResult error_result; | |
307 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
308 | &error_result, | |
309 | m_threads->work_queue, &ctx); | |
310 | req->send(); | |
311 | ASSERT_EQ(0, ctx.wait()); | |
312 | } | |
313 | ||
314 | TEST_F(TestMockImageDeleterTrashRemoveRequest, TrashSetStateError) { | |
315 | InSequence seq; | |
316 | ||
317 | cls::rbd::TrashImageSpec trash_image_spec{ | |
318 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
319 | expect_trash_get(trash_image_spec, 0); | |
320 | ||
321 | expect_trash_state_set("image id", -EPERM); | |
322 | ||
323 | C_SaferCond ctx; | |
324 | ErrorResult error_result; | |
325 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
326 | &error_result, | |
327 | m_threads->work_queue, &ctx); | |
328 | req->send(); | |
329 | ASSERT_EQ(-EPERM, ctx.wait()); | |
330 | } | |
331 | ||
332 | TEST_F(TestMockImageDeleterTrashRemoveRequest, GetSnapContextDNE) { | |
333 | InSequence seq; | |
334 | ||
335 | cls::rbd::TrashImageSpec trash_image_spec{ | |
336 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
337 | expect_trash_get(trash_image_spec, 0); | |
338 | ||
339 | expect_trash_state_set("image id", 0); | |
340 | ||
341 | expect_get_snapcontext("image id", {1, {1}}, -ENOENT); | |
342 | ||
343 | MockLibrbdTrashRemoveRequest mock_image_remove_request; | |
344 | expect_image_remove(mock_image_remove_request, "image id", 0); | |
345 | ||
346 | MockTrashWatcher mock_trash_watcher; | |
347 | expect_notify_image_removed(mock_trash_watcher, "image id"); | |
348 | ||
349 | C_SaferCond ctx; | |
350 | ErrorResult error_result; | |
351 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
352 | &error_result, | |
353 | m_threads->work_queue, &ctx); | |
354 | req->send(); | |
355 | ASSERT_EQ(0, ctx.wait()); | |
356 | } | |
357 | ||
358 | TEST_F(TestMockImageDeleterTrashRemoveRequest, GetSnapContextError) { | |
359 | InSequence seq; | |
360 | ||
361 | cls::rbd::TrashImageSpec trash_image_spec{ | |
362 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
363 | expect_trash_get(trash_image_spec, 0); | |
364 | ||
365 | expect_trash_state_set("image id", 0); | |
366 | ||
367 | expect_get_snapcontext("image id", {1, {1}}, -EINVAL); | |
368 | ||
369 | C_SaferCond ctx; | |
370 | ErrorResult error_result; | |
371 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
372 | &error_result, | |
373 | m_threads->work_queue, &ctx); | |
374 | req->send(); | |
375 | ASSERT_EQ(-EINVAL, ctx.wait()); | |
376 | } | |
377 | ||
378 | TEST_F(TestMockImageDeleterTrashRemoveRequest, PurgeSnapshotBusy) { | |
379 | InSequence seq; | |
380 | ||
381 | cls::rbd::TrashImageSpec trash_image_spec{ | |
382 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
383 | expect_trash_get(trash_image_spec, 0); | |
384 | ||
385 | expect_trash_state_set("image id", 0); | |
386 | ||
387 | expect_get_snapcontext("image id", {1, {1}}, 0); | |
388 | ||
389 | MockSnapshotPurgeRequest mock_snapshot_purge_request; | |
390 | expect_snapshot_purge(mock_snapshot_purge_request, "image id", -EBUSY); | |
391 | ||
392 | C_SaferCond ctx; | |
393 | ErrorResult error_result; | |
394 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
395 | &error_result, | |
396 | m_threads->work_queue, &ctx); | |
397 | req->send(); | |
398 | ASSERT_EQ(-EBUSY, ctx.wait()); | |
399 | ASSERT_EQ(ERROR_RESULT_RETRY_IMMEDIATELY, error_result); | |
400 | } | |
401 | ||
402 | TEST_F(TestMockImageDeleterTrashRemoveRequest, PurgeSnapshotError) { | |
403 | InSequence seq; | |
404 | ||
405 | cls::rbd::TrashImageSpec trash_image_spec{ | |
406 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
407 | expect_trash_get(trash_image_spec, 0); | |
408 | ||
409 | expect_trash_state_set("image id", 0); | |
410 | ||
411 | expect_get_snapcontext("image id", {1, {1}}, 0); | |
412 | ||
413 | MockSnapshotPurgeRequest mock_snapshot_purge_request; | |
414 | expect_snapshot_purge(mock_snapshot_purge_request, "image id", -EINVAL); | |
415 | ||
416 | C_SaferCond ctx; | |
417 | ErrorResult error_result; | |
418 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
419 | &error_result, | |
420 | m_threads->work_queue, &ctx); | |
421 | req->send(); | |
422 | ASSERT_EQ(-EINVAL, ctx.wait()); | |
423 | } | |
424 | ||
425 | TEST_F(TestMockImageDeleterTrashRemoveRequest, RemoveError) { | |
426 | InSequence seq; | |
427 | ||
428 | cls::rbd::TrashImageSpec trash_image_spec{ | |
429 | cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING, "image name", {}, {}}; | |
430 | expect_trash_get(trash_image_spec, 0); | |
431 | ||
432 | expect_trash_state_set("image id", 0); | |
433 | ||
434 | expect_get_snapcontext("image id", {1, {1}}, 0); | |
435 | ||
436 | MockSnapshotPurgeRequest mock_snapshot_purge_request; | |
437 | expect_snapshot_purge(mock_snapshot_purge_request, "image id", 0); | |
438 | ||
439 | MockLibrbdTrashRemoveRequest mock_image_remove_request; | |
440 | expect_image_remove(mock_image_remove_request, "image id", -EINVAL); | |
441 | ||
442 | C_SaferCond ctx; | |
443 | ErrorResult error_result; | |
444 | auto req = MockTrashRemoveRequest::create(m_local_io_ctx, "image id", | |
445 | &error_result, | |
446 | m_threads->work_queue, &ctx); | |
447 | req->send(); | |
448 | ASSERT_EQ(-EINVAL, ctx.wait()); | |
449 | } | |
450 | ||
451 | } // namespace image_deleter | |
452 | } // namespace mirror | |
453 | } // namespace rbd |