1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
20 struct MockTestImageCtx
: public librbd::MockImageCtx
{
21 MockTestImageCtx(librbd::ImageCtx
&image_ctx
)
22 : librbd::MockImageCtx(image_ctx
) {
26 } // anonymous namespace
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
);
37 MOCK_METHOD2(notify_image_removed
, void(const std::string
&, Context
*));
44 TrashWatcher
<MockTestImageCtx
>* TrashWatcher
<MockTestImageCtx
>::s_instance
= nullptr;
49 struct RemoveRequest
<librbd::MockTestImageCtx
> {
50 static RemoveRequest
*s_instance
;
51 Context
*on_finish
= nullptr;
53 static RemoveRequest
*create(librados::IoCtx
&io_ctx
,
54 const std::string
&image_id
,
55 ContextWQ
*work_queue
,
57 librbd::ProgressContext
&progress_ctx
,
59 ceph_assert(s_instance
!= nullptr);
61 s_instance
->construct(image_id
);
62 s_instance
->on_finish
= on_finish
;
66 MOCK_METHOD1(construct
, void(const std::string
&));
67 MOCK_METHOD0(send
, void());
74 RemoveRequest
<librbd::MockTestImageCtx
>* RemoveRequest
<librbd::MockTestImageCtx
>::s_instance
= nullptr;
81 namespace image_deleter
{
84 struct SnapshotPurgeRequest
<librbd::MockTestImageCtx
> {
85 static SnapshotPurgeRequest
*s_instance
;
86 Context
*on_finish
= nullptr;
88 static SnapshotPurgeRequest
*create(librados::IoCtx
&io_ctx
,
89 const std::string
&image_id
,
91 ceph_assert(s_instance
!= nullptr);
92 s_instance
->construct(image_id
);
93 s_instance
->on_finish
= on_finish
;
97 MOCK_METHOD1(construct
, void(const std::string
&));
98 MOCK_METHOD0(send
, void());
100 SnapshotPurgeRequest() {
105 SnapshotPurgeRequest
<librbd::MockTestImageCtx
>* SnapshotPurgeRequest
<librbd::MockTestImageCtx
>::s_instance
= nullptr;
107 } // namespace image_deleter
108 } // namespace mirror
111 #include "tools/rbd_mirror/image_deleter/TrashRemoveRequest.cc"
115 namespace image_deleter
{
118 using ::testing::DoAll
;
119 using ::testing::Invoke
;
120 using ::testing::InSequence
;
121 using ::testing::Return
;
122 using ::testing::StrEq
;
123 using ::testing::WithArg
;
124 using ::testing::WithArgs
;
126 class TestMockImageDeleterTrashRemoveRequest
: public TestMockFixture
{
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
;
133 void expect_trash_get(const cls::rbd::TrashImageSpec
& trash_spec
, int r
) {
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
);
144 void expect_trash_state_set(const std::string
& image_id
, int r
) {
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
);
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
));
157 void expect_get_snapcontext(const std::string
& image_id
,
158 const ::SnapContext
&snapc
, int r
) {
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
) {
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
);
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
);
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);
201 TEST_F(TestMockImageDeleterTrashRemoveRequest
, Success
) {
204 cls::rbd::TrashImageSpec trash_image_spec
{
205 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
206 expect_trash_get(trash_image_spec
, 0);
208 expect_trash_state_set("image id", 0);
210 expect_get_snapcontext("image id", {1, {1}}, 0);
212 MockSnapshotPurgeRequest mock_snapshot_purge_request
;
213 expect_snapshot_purge(mock_snapshot_purge_request
, "image id", 0);
215 MockLibrbdTrashRemoveRequest mock_image_remove_request
;
216 expect_image_remove(mock_image_remove_request
, "image id", 0);
218 MockTrashWatcher mock_trash_watcher
;
219 expect_notify_image_removed(mock_trash_watcher
, "image id");
222 ErrorResult error_result
;
223 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
225 m_threads
->work_queue
, &ctx
);
227 ASSERT_EQ(0, ctx
.wait());
230 TEST_F(TestMockImageDeleterTrashRemoveRequest
, TrashDNE
) {
233 cls::rbd::TrashImageSpec trash_image_spec
{
234 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
235 expect_trash_get(trash_image_spec
, -ENOENT
);
238 ErrorResult error_result
;
239 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
241 m_threads
->work_queue
, &ctx
);
243 ASSERT_EQ(0, ctx
.wait());
246 TEST_F(TestMockImageDeleterTrashRemoveRequest
, TrashError
) {
249 cls::rbd::TrashImageSpec trash_image_spec
{
250 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
251 expect_trash_get(trash_image_spec
, -EPERM
);
254 ErrorResult error_result
;
255 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
257 m_threads
->work_queue
, &ctx
);
259 ASSERT_EQ(-EPERM
, ctx
.wait());
262 TEST_F(TestMockImageDeleterTrashRemoveRequest
, TrashSourceIncorrect
) {
265 cls::rbd::TrashImageSpec trash_image_spec
{
266 cls::rbd::TRASH_IMAGE_SOURCE_USER
, "image name", {}, {}};
267 expect_trash_get(trash_image_spec
, 0);
270 ErrorResult error_result
;
271 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
273 m_threads
->work_queue
, &ctx
);
275 ASSERT_EQ(0, ctx
.wait());
278 TEST_F(TestMockImageDeleterTrashRemoveRequest
, TrashStateIncorrect
) {
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);
287 ErrorResult error_result
;
288 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
290 m_threads
->work_queue
, &ctx
);
292 ASSERT_EQ(-EBUSY
, ctx
.wait());
293 ASSERT_EQ(ERROR_RESULT_RETRY_IMMEDIATELY
, error_result
);
296 TEST_F(TestMockImageDeleterTrashRemoveRequest
, TrashSetStateDNE
) {
299 cls::rbd::TrashImageSpec trash_image_spec
{
300 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
301 expect_trash_get(trash_image_spec
, 0);
303 expect_trash_state_set("image id", -ENOENT
);
306 ErrorResult error_result
;
307 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
309 m_threads
->work_queue
, &ctx
);
311 ASSERT_EQ(0, ctx
.wait());
314 TEST_F(TestMockImageDeleterTrashRemoveRequest
, TrashSetStateError
) {
317 cls::rbd::TrashImageSpec trash_image_spec
{
318 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
319 expect_trash_get(trash_image_spec
, 0);
321 expect_trash_state_set("image id", -EPERM
);
324 ErrorResult error_result
;
325 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
327 m_threads
->work_queue
, &ctx
);
329 ASSERT_EQ(-EPERM
, ctx
.wait());
332 TEST_F(TestMockImageDeleterTrashRemoveRequest
, GetSnapContextDNE
) {
335 cls::rbd::TrashImageSpec trash_image_spec
{
336 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
337 expect_trash_get(trash_image_spec
, 0);
339 expect_trash_state_set("image id", 0);
341 expect_get_snapcontext("image id", {1, {1}}, -ENOENT
);
343 MockLibrbdTrashRemoveRequest mock_image_remove_request
;
344 expect_image_remove(mock_image_remove_request
, "image id", 0);
346 MockTrashWatcher mock_trash_watcher
;
347 expect_notify_image_removed(mock_trash_watcher
, "image id");
350 ErrorResult error_result
;
351 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
353 m_threads
->work_queue
, &ctx
);
355 ASSERT_EQ(0, ctx
.wait());
358 TEST_F(TestMockImageDeleterTrashRemoveRequest
, GetSnapContextError
) {
361 cls::rbd::TrashImageSpec trash_image_spec
{
362 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
363 expect_trash_get(trash_image_spec
, 0);
365 expect_trash_state_set("image id", 0);
367 expect_get_snapcontext("image id", {1, {1}}, -EINVAL
);
370 ErrorResult error_result
;
371 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
373 m_threads
->work_queue
, &ctx
);
375 ASSERT_EQ(-EINVAL
, ctx
.wait());
378 TEST_F(TestMockImageDeleterTrashRemoveRequest
, PurgeSnapshotBusy
) {
381 cls::rbd::TrashImageSpec trash_image_spec
{
382 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
383 expect_trash_get(trash_image_spec
, 0);
385 expect_trash_state_set("image id", 0);
387 expect_get_snapcontext("image id", {1, {1}}, 0);
389 MockSnapshotPurgeRequest mock_snapshot_purge_request
;
390 expect_snapshot_purge(mock_snapshot_purge_request
, "image id", -EBUSY
);
393 ErrorResult error_result
;
394 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
396 m_threads
->work_queue
, &ctx
);
398 ASSERT_EQ(-EBUSY
, ctx
.wait());
399 ASSERT_EQ(ERROR_RESULT_RETRY_IMMEDIATELY
, error_result
);
402 TEST_F(TestMockImageDeleterTrashRemoveRequest
, PurgeSnapshotError
) {
405 cls::rbd::TrashImageSpec trash_image_spec
{
406 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
407 expect_trash_get(trash_image_spec
, 0);
409 expect_trash_state_set("image id", 0);
411 expect_get_snapcontext("image id", {1, {1}}, 0);
413 MockSnapshotPurgeRequest mock_snapshot_purge_request
;
414 expect_snapshot_purge(mock_snapshot_purge_request
, "image id", -EINVAL
);
417 ErrorResult error_result
;
418 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
420 m_threads
->work_queue
, &ctx
);
422 ASSERT_EQ(-EINVAL
, ctx
.wait());
425 TEST_F(TestMockImageDeleterTrashRemoveRequest
, RemoveError
) {
428 cls::rbd::TrashImageSpec trash_image_spec
{
429 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
, "image name", {}, {}};
430 expect_trash_get(trash_image_spec
, 0);
432 expect_trash_state_set("image id", 0);
434 expect_get_snapcontext("image id", {1, {1}}, 0);
436 MockSnapshotPurgeRequest mock_snapshot_purge_request
;
437 expect_snapshot_purge(mock_snapshot_purge_request
, "image id", 0);
439 MockLibrbdTrashRemoveRequest mock_image_remove_request
;
440 expect_image_remove(mock_image_remove_request
, "image id", -EINVAL
);
443 ErrorResult error_result
;
444 auto req
= MockTrashRemoveRequest::create(m_local_io_ctx
, "image id",
446 m_threads
->work_queue
, &ctx
);
448 ASSERT_EQ(-EINVAL
, ctx
.wait());
451 } // namespace image_deleter
452 } // namespace mirror