]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/image/test_mock_DetachChildRequest.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / test / librbd / image / test_mock_DetachChildRequest.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/MockContextWQ.h"
8 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
9 #include "test/librados_test_stub/MockTestMemRadosClient.h"
10 #include "librbd/image/TypeTraits.h"
11 #include "librbd/image/DetachChildRequest.h"
12 #include "librbd/trash/RemoveRequest.h"
13 #include "gmock/gmock.h"
14 #include "gtest/gtest.h"
15
16 namespace librbd {
17 namespace {
18
19 struct MockTestImageCtx : public MockImageCtx {
20 static MockTestImageCtx* s_instance;
21 static MockTestImageCtx* create(const std::string &image_name,
22 const std::string &image_id,
23 const char *snap, librados::IoCtx& p,
24 bool read_only) {
25 ceph_assert(s_instance != nullptr);
26 return s_instance;
27 }
28
29 MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
30 s_instance = this;
31 }
32 };
33
34 MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
35
36 } // anonymous namespace
37
38 namespace image {
39
40 template <>
41 struct TypeTraits<MockTestImageCtx> {
42 typedef librbd::MockContextWQ ContextWQ;
43 };
44
45 } // namespace image
46
47 namespace trash {
48
49 template <>
50 class RemoveRequest<MockTestImageCtx> {
51 private:
52 typedef ::librbd::image::TypeTraits<MockTestImageCtx> TypeTraits;
53 typedef typename TypeTraits::ContextWQ ContextWQ;
54 public:
55 static RemoveRequest *s_instance;
56 static RemoveRequest *create(librados::IoCtx &ioctx,
57 MockTestImageCtx *image_ctx,
58 ContextWQ *op_work_queue, bool force,
59 ProgressContext &prog_ctx, Context *on_finish) {
60 ceph_assert(s_instance != nullptr);
61 s_instance->on_finish = on_finish;
62 return s_instance;
63 }
64
65 Context *on_finish = nullptr;
66
67 RemoveRequest() {
68 s_instance = this;
69 }
70
71 MOCK_METHOD0(send, void());
72 };
73
74 RemoveRequest<MockTestImageCtx> *RemoveRequest<MockTestImageCtx>::s_instance;
75
76 } // namespace trash
77 } // namespace librbd
78
79 // template definitions
80 #include "librbd/image/DetachChildRequest.cc"
81
82 namespace librbd {
83 namespace image {
84
85 using ::testing::_;
86 using ::testing::DoAll;
87 using ::testing::DoDefault;
88 using ::testing::InSequence;
89 using ::testing::Invoke;
90 using ::testing::Return;
91 using ::testing::StrEq;
92 using ::testing::WithArg;
93
94 class TestMockImageDetachChildRequest : public TestMockFixture {
95 public:
96 typedef DetachChildRequest<MockTestImageCtx> MockDetachChildRequest;
97 typedef trash::RemoveRequest<MockTestImageCtx> MockTrashRemoveRequest;
98
99 void SetUp() override {
100 TestMockFixture::SetUp();
101
102 ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
103 }
104
105 void expect_test_op_features(MockTestImageCtx& mock_image_ctx, bool enabled) {
106 EXPECT_CALL(mock_image_ctx,
107 test_op_features(RBD_OPERATION_FEATURE_CLONE_CHILD))
108 .WillOnce(Return(enabled));
109 }
110
111 void expect_create_ioctx(MockImageCtx &mock_image_ctx,
112 librados::MockTestMemIoCtxImpl **io_ctx_impl) {
113 *io_ctx_impl = &get_mock_io_ctx(mock_image_ctx.md_ctx);
114 auto rados_client = (*io_ctx_impl)->get_mock_rados_client();
115
116 EXPECT_CALL(*rados_client, create_ioctx(_, _))
117 .WillOnce(DoAll(GetReference(*io_ctx_impl), Return(*io_ctx_impl)));
118 }
119
120 void expect_child_detach(MockImageCtx &mock_image_ctx,
121 librados::MockTestMemIoCtxImpl &mock_io_ctx_impl,
122 int r) {
123 auto& parent_spec = mock_image_ctx.parent_md.spec;
124
125 bufferlist bl;
126 encode(parent_spec.snap_id, bl);
127 encode(cls::rbd::ChildImageSpec{mock_image_ctx.md_ctx.get_id(), "",
128 mock_image_ctx.id}, bl);
129
130 EXPECT_CALL(mock_io_ctx_impl,
131 exec(util::header_name(parent_spec.image_id),
132 _, StrEq("rbd"), StrEq("child_detach"), ContentsEqual(bl),
133 _, _, _))
134 .WillOnce(Return(r));
135 }
136
137 void expect_remove_child(MockImageCtx &mock_image_ctx, int r) {
138 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
139 exec(RBD_CHILDREN, _, StrEq("rbd"), StrEq("remove_child"), _,
140 _, _, _))
141 .WillOnce(Return(r));
142 }
143
144 void expect_snapshot_get(MockImageCtx &mock_image_ctx,
145 librados::MockTestMemIoCtxImpl &mock_io_ctx_impl,
146 const std::string& parent_header_name,
147 const cls::rbd::SnapshotInfo& snap_info, int r) {
148
149 using ceph::encode;
150 EXPECT_CALL(mock_io_ctx_impl,
151 exec(parent_header_name, _, StrEq("rbd"),
152 StrEq("snapshot_get"), _, _, _, _))
153 .WillOnce(WithArg<5>(Invoke([snap_info, r](bufferlist* bl) {
154 encode(snap_info, *bl);
155 return r;
156 })));
157 }
158
159 void expect_open(MockImageCtx &mock_image_ctx, int r) {
160 EXPECT_CALL(*mock_image_ctx.state, open(true, _))
161 .WillOnce(WithArg<1>(Invoke([this, &mock_image_ctx, r](Context* ctx) {
162 EXPECT_EQ(0U, mock_image_ctx.read_only_mask &
163 IMAGE_READ_ONLY_FLAG_NON_PRIMARY);
164 image_ctx->op_work_queue->queue(ctx, r);
165 })));
166 if (r == 0) {
167 EXPECT_CALL(mock_image_ctx, test_features(_))
168 .WillOnce(Return(false));
169 }
170 }
171
172 void expect_close(MockImageCtx &mock_image_ctx, int r) {
173 EXPECT_CALL(*mock_image_ctx.state, close(_))
174 .WillOnce(Invoke([this, r](Context* ctx) {
175 image_ctx->op_work_queue->queue(ctx, r);
176 }));
177 }
178
179 void expect_snap_remove(MockImageCtx &mock_image_ctx,
180 const std::string &snap_name, int r) {
181 EXPECT_CALL(*mock_image_ctx.operations,
182 snap_remove({cls::rbd::TrashSnapshotNamespace{}},
183 StrEq(snap_name), _))
184 .WillOnce(WithArg<2>(Invoke([this, r](Context *ctx) {
185 image_ctx->op_work_queue->queue(ctx, r);
186 })));
187 }
188
189 void expect_trash_get(MockImageCtx &mock_image_ctx,
190 librados::MockTestMemIoCtxImpl &mock_io_ctx_impl,
191 const cls::rbd::TrashImageSpec& trash_spec,
192 int r) {
193 using ceph::encode;
194 EXPECT_CALL(mock_io_ctx_impl,
195 exec(RBD_TRASH, _, StrEq("rbd"),
196 StrEq("trash_get"), _, _, _, _))
197 .WillOnce(WithArg<5>(Invoke([trash_spec, r](bufferlist* bl) {
198 encode(trash_spec, *bl);
199 return r;
200 })));
201 }
202
203 void expect_trash_remove(MockTrashRemoveRequest& mock_trash_remove_request,
204 int r) {
205 EXPECT_CALL(mock_trash_remove_request, send())
206 .WillOnce(Invoke([&mock_trash_remove_request, r]() {
207 mock_trash_remove_request.on_finish->complete(r);
208 }));
209 }
210
211 librbd::ImageCtx *image_ctx;
212 };
213
214 TEST_F(TestMockImageDetachChildRequest, SuccessV1) {
215 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
216
217 MockTestImageCtx mock_image_ctx(*image_ctx);
218 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
219
220 InSequence seq;
221 expect_test_op_features(mock_image_ctx, false);
222 expect_remove_child(mock_image_ctx, 0);
223
224 C_SaferCond ctx;
225 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
226 req->send();
227 ASSERT_EQ(0, ctx.wait());
228 }
229
230 TEST_F(TestMockImageDetachChildRequest, SuccessV2) {
231 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
232
233 MockTestImageCtx mock_image_ctx(*image_ctx);
234 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
235
236 InSequence seq;
237 expect_test_op_features(mock_image_ctx, true);
238
239 librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
240 expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
241 expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
242 expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
243 "rbd_header.parent id",
244 {234, {cls::rbd::UserSnapshotNamespace{}},
245 "snap1", 123, {}, 0}, 0);
246
247 C_SaferCond ctx;
248 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
249 req->send();
250 ASSERT_EQ(0, ctx.wait());
251 }
252
253 TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotSuccess) {
254 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
255
256 MockTestImageCtx mock_image_ctx(*image_ctx);
257 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
258
259 InSequence seq;
260 expect_test_op_features(mock_image_ctx, true);
261
262 librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
263 expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
264 expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
265 expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
266 "rbd_header.parent id",
267 {234, {cls::rbd::TrashSnapshotNamespace{}},
268 "snap1", 123, {}, 0}, 0);
269 expect_open(mock_image_ctx, 0);
270 expect_snap_remove(mock_image_ctx, "snap1", 0);
271 const cls::rbd::TrashImageSpec trash_spec;
272 expect_trash_get(mock_image_ctx, *mock_io_ctx_impl, trash_spec, -ENOENT);
273 expect_close(mock_image_ctx, 0);
274
275 C_SaferCond ctx;
276 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
277 req->send();
278 ASSERT_EQ(0, ctx.wait());
279 }
280
281 TEST_F(TestMockImageDetachChildRequest, ParentAutoRemove) {
282 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
283
284 MockTestImageCtx mock_image_ctx(*image_ctx);
285 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
286
287 InSequence seq;
288 expect_test_op_features(mock_image_ctx, true);
289
290 librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
291 expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
292 expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
293 expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
294 "rbd_header.parent id",
295 {234, {cls::rbd::TrashSnapshotNamespace{}},
296 "snap1", 123, {}, 0}, 0);
297 expect_open(mock_image_ctx, 0);
298 expect_snap_remove(mock_image_ctx, "snap1", 0);
299 const cls::rbd::TrashImageSpec trash_spec =
300 {cls::rbd::TRASH_IMAGE_SOURCE_USER_PARENT, "parent", {}, {}};
301
302 expect_trash_get(mock_image_ctx, *mock_io_ctx_impl, trash_spec, 0);
303 MockTrashRemoveRequest mock_trash_remove_request;
304 expect_trash_remove(mock_trash_remove_request, 0);
305
306 C_SaferCond ctx;
307 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
308 req->send();
309 ASSERT_EQ(0, ctx.wait());
310 }
311
312 TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotInUse) {
313 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
314
315 MockTestImageCtx mock_image_ctx(*image_ctx);
316 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
317
318 InSequence seq;
319 expect_test_op_features(mock_image_ctx, true);
320
321 librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
322 expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
323 expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
324 expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
325 "rbd_header.parent id",
326 {234, {cls::rbd::TrashSnapshotNamespace{}},
327 "snap1", 123, {}, 1}, 0);
328
329 C_SaferCond ctx;
330 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
331 req->send();
332 ASSERT_EQ(0, ctx.wait());
333 }
334
335 TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotSnapshotGetError) {
336 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
337
338 MockTestImageCtx mock_image_ctx(*image_ctx);
339 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
340
341 InSequence seq;
342 expect_test_op_features(mock_image_ctx, true);
343
344 librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
345 expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
346 expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
347 expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
348 "rbd_header.parent id",
349 {234, {cls::rbd::TrashSnapshotNamespace{}},
350 "snap1", 123, {}, 0}, -EINVAL);
351
352 C_SaferCond ctx;
353 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
354 req->send();
355 ASSERT_EQ(0, ctx.wait());
356 }
357
358 TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotOpenParentError) {
359 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
360
361 MockTestImageCtx mock_image_ctx(*image_ctx);
362 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
363
364 InSequence seq;
365 expect_test_op_features(mock_image_ctx, true);
366
367 librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
368 expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
369 expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
370 expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
371 "rbd_header.parent id",
372 {234, {cls::rbd::TrashSnapshotNamespace{}},
373 "snap1", 123, {}, 0}, 0);
374 expect_open(mock_image_ctx, -EPERM);
375
376 C_SaferCond ctx;
377 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
378 req->send();
379 ASSERT_EQ(0, ctx.wait());
380 }
381
382 TEST_F(TestMockImageDetachChildRequest, TrashedSnapshotRemoveError) {
383 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
384
385 MockTestImageCtx mock_image_ctx(*image_ctx);
386 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
387
388 InSequence seq;
389 expect_test_op_features(mock_image_ctx, true);
390
391 librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
392 expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
393 expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, 0);
394 expect_snapshot_get(mock_image_ctx, *mock_io_ctx_impl,
395 "rbd_header.parent id",
396 {234, {cls::rbd::TrashSnapshotNamespace{}},
397 "snap1", 123, {}, 0}, 0);
398 expect_open(mock_image_ctx, 0);
399 expect_snap_remove(mock_image_ctx, "snap1", -EPERM);
400 expect_close(mock_image_ctx, -EPERM);
401
402 C_SaferCond ctx;
403 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
404 req->send();
405 ASSERT_EQ(0, ctx.wait());
406 }
407
408 TEST_F(TestMockImageDetachChildRequest, ParentDNE) {
409 MockTestImageCtx mock_image_ctx(*image_ctx);
410 expect_op_work_queue(mock_image_ctx);
411
412 C_SaferCond ctx;
413 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
414 req->send();
415 ASSERT_EQ(0, ctx.wait());
416 }
417
418 TEST_F(TestMockImageDetachChildRequest, ChildDetachError) {
419 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
420
421 MockTestImageCtx mock_image_ctx(*image_ctx);
422 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
423
424 InSequence seq;
425 expect_test_op_features(mock_image_ctx, true);
426
427 librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
428 expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
429 expect_child_detach(mock_image_ctx, *mock_io_ctx_impl, -EPERM);
430
431 C_SaferCond ctx;
432 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
433 req->send();
434 ASSERT_EQ(-EPERM, ctx.wait());
435 }
436
437 TEST_F(TestMockImageDetachChildRequest, RemoveChildError) {
438 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
439
440 MockTestImageCtx mock_image_ctx(*image_ctx);
441 mock_image_ctx.parent_md.spec = {m_ioctx.get_id(), "", "parent id", 234};
442
443 InSequence seq;
444 expect_test_op_features(mock_image_ctx, false);
445 expect_remove_child(mock_image_ctx, -EINVAL);
446
447 C_SaferCond ctx;
448 auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
449 req->send();
450 ASSERT_EQ(-EINVAL, ctx.wait());
451 }
452
453 } // namespace image
454 } // namespace librbd