]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc
1189f76fb4696704140e0c336e3bd0ed3182d907
[ceph.git] / ceph / src / test / librbd / operation / test_mock_SnapshotRemoveRequest.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/librados_test_stub/MockTestMemIoCtxImpl.h"
8 #include "common/bit_vector.hpp"
9 #include "librbd/ImageState.h"
10 #include "librbd/internal.h"
11 #include "librbd/Operations.h"
12 #include "librbd/image/DetachChildRequest.h"
13 #include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
14 #include "librbd/operation/SnapshotRemoveRequest.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17
18 namespace librbd {
19 namespace image {
20
21 template <>
22 class DetachChildRequest<MockImageCtx> {
23 public:
24 static DetachChildRequest *s_instance;
25 static DetachChildRequest *create(MockImageCtx &image_ctx,
26 Context *on_finish) {
27 ceph_assert(s_instance != nullptr);
28 s_instance->on_finish = on_finish;
29 return s_instance;
30 }
31
32 Context *on_finish = nullptr;
33
34 DetachChildRequest() {
35 s_instance = this;
36 }
37
38 MOCK_METHOD0(send, void());
39 };
40
41 DetachChildRequest<MockImageCtx> *DetachChildRequest<MockImageCtx>::s_instance;
42
43 } // namespace image
44
45 namespace mirror {
46 namespace snapshot {
47
48 template<>
49 class RemoveImageStateRequest<MockImageCtx> {
50 public:
51 static RemoveImageStateRequest *s_instance;
52 Context *on_finish = nullptr;
53
54 static RemoveImageStateRequest *create(MockImageCtx *image_ctx,
55 uint64_t snap_id,
56 Context *on_finish) {
57 ceph_assert(s_instance != nullptr);
58 s_instance->on_finish = on_finish;
59 return s_instance;
60 }
61
62 RemoveImageStateRequest() {
63 s_instance = this;
64 }
65
66 MOCK_METHOD0(send, void());
67 };
68
69 RemoveImageStateRequest<MockImageCtx> *RemoveImageStateRequest<MockImageCtx>::s_instance;
70
71 } // namespace snapshot
72 } // namespace mirror
73 } // namespace librbd
74
75 // template definitions
76 #include "librbd/operation/SnapshotRemoveRequest.cc"
77
78 namespace librbd {
79 namespace operation {
80
81 using ::testing::_;
82 using ::testing::DoAll;
83 using ::testing::DoDefault;
84 using ::testing::Invoke;
85 using ::testing::Return;
86 using ::testing::SetArgPointee;
87 using ::testing::StrEq;
88 using ::testing::WithArg;
89
90 class TestMockOperationSnapshotRemoveRequest : public TestMockFixture {
91 public:
92 typedef SnapshotRemoveRequest<MockImageCtx> MockSnapshotRemoveRequest;
93 typedef image::DetachChildRequest<MockImageCtx> MockDetachChildRequest;
94 typedef mirror::snapshot::RemoveImageStateRequest<MockImageCtx> MockRemoveImageStateRequest;
95
96 int create_snapshot(const char *snap_name) {
97 librbd::ImageCtx *ictx;
98 int r = open_image(m_image_name, &ictx);
99 if (r < 0) {
100 return r;
101 }
102
103 r = snap_create(*ictx, snap_name);
104 if (r < 0) {
105 return r;
106 }
107
108 r = snap_protect(*ictx, snap_name);
109 if (r < 0) {
110 return r;
111 }
112 close_image(ictx);
113 return 0;
114 }
115
116 void expect_snapshot_trash_add(MockImageCtx &mock_image_ctx, int r) {
117 if (mock_image_ctx.old_format) {
118 return;
119 }
120
121 auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
122 exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
123 StrEq("snapshot_trash_add"),
124 _, _, _, _));
125 if (r < 0) {
126 expect.WillOnce(Return(r));
127 } else {
128 expect.WillOnce(DoDefault());
129 }
130 }
131
132 void expect_snapshot_get(MockImageCtx &mock_image_ctx,
133 const cls::rbd::SnapshotInfo& snap_info, int r) {
134 if (mock_image_ctx.old_format) {
135 return;
136 }
137
138 using ceph::encode;
139 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
140 exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
141 StrEq("snapshot_get"), _, _, _, _))
142 .WillOnce(WithArg<5>(Invoke([snap_info, r](bufferlist* bl) {
143 encode(snap_info, *bl);
144 return r;
145 })));
146 }
147
148 void expect_children_list(MockImageCtx &mock_image_ctx,
149 const cls::rbd::ChildImageSpecs& child_images, int r) {
150 if (mock_image_ctx.old_format) {
151 return;
152 }
153
154 using ceph::encode;
155 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
156 exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
157 StrEq("children_list"), _, _, _, _))
158 .WillOnce(WithArg<5>(Invoke([child_images, r](bufferlist* bl) {
159 encode(child_images, *bl);
160 return r;
161 })));
162 }
163
164 void expect_detach_stale_child(MockImageCtx &mock_image_ctx, int r) {
165 auto& parent_spec = mock_image_ctx.parent_md.spec;
166
167 bufferlist bl;
168 encode(parent_spec.snap_id, bl);
169 encode(cls::rbd::ChildImageSpec{mock_image_ctx.md_ctx.get_id(), "",
170 mock_image_ctx.id}, bl);
171 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
172 exec(util::header_name(parent_spec.image_id),
173 _, StrEq("rbd"), StrEq("child_detach"), ContentsEqual(bl),
174 _, _, _))
175 .WillOnce(Return(r));
176 }
177
178 void expect_object_map_snap_remove(MockImageCtx &mock_image_ctx, int r) {
179 if (mock_image_ctx.object_map != nullptr) {
180 EXPECT_CALL(*mock_image_ctx.object_map, snapshot_remove(_, _))
181 .WillOnce(WithArg<1>(CompleteContext(
182 r, mock_image_ctx.image_ctx->op_work_queue)));
183 }
184 }
185
186 void expect_remove_image_state(
187 MockImageCtx &mock_image_ctx,
188 MockRemoveImageStateRequest &mock_remove_image_state_request, int r) {
189 EXPECT_CALL(mock_remove_image_state_request, send())
190 .WillOnce(FinishRequest(&mock_remove_image_state_request, r,
191 &mock_image_ctx));
192 }
193
194 void expect_get_parent_spec(MockImageCtx &mock_image_ctx, int r) {
195 if (mock_image_ctx.old_format) {
196 return;
197 }
198
199 auto &expect = EXPECT_CALL(mock_image_ctx, get_parent_spec(_, _));
200 if (r < 0) {
201 expect.WillOnce(Return(r));
202 } else {
203 auto &parent_spec = mock_image_ctx.snap_info.rbegin()->second.parent.spec;
204 expect.WillOnce(DoAll(SetArgPointee<1>(parent_spec),
205 Return(0)));
206 }
207 }
208
209 void expect_detach_child(MockImageCtx &mock_image_ctx,
210 MockDetachChildRequest& mock_request, int r) {
211 EXPECT_CALL(mock_request, send())
212 .WillOnce(FinishRequest(&mock_request, r, &mock_image_ctx));
213 }
214
215 void expect_snap_remove(MockImageCtx &mock_image_ctx, int r) {
216 auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
217 exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
218 StrEq(mock_image_ctx.old_format ? "snap_remove" :
219 "snapshot_remove"),
220 _, _, _, _));
221 if (r < 0) {
222 expect.WillOnce(Return(r));
223 } else {
224 expect.WillOnce(DoDefault());
225 }
226 }
227
228 void expect_rm_snap(MockImageCtx &mock_image_ctx) {
229 EXPECT_CALL(mock_image_ctx, rm_snap(_, _, _)).Times(1);
230 }
231
232 void expect_release_snap_id(MockImageCtx &mock_image_ctx) {
233 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
234 selfmanaged_snap_remove(_))
235 .WillOnce(DoDefault());
236 }
237
238 };
239
240 TEST_F(TestMockOperationSnapshotRemoveRequest, Success) {
241 librbd::ImageCtx *ictx;
242 ASSERT_EQ(0, open_image(m_image_name, &ictx));
243 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
244 ASSERT_EQ(0, ictx->state->refresh_if_required());
245
246 MockImageCtx mock_image_ctx(*ictx);
247
248 MockExclusiveLock mock_exclusive_lock;
249 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
250 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
251 }
252
253 MockObjectMap mock_object_map;
254 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
255 mock_image_ctx.object_map = &mock_object_map;
256 }
257
258 expect_op_work_queue(mock_image_ctx);
259
260 ::testing::InSequence seq;
261 expect_snapshot_trash_add(mock_image_ctx, 0);
262
263 uint64_t snap_id = ictx->snap_info.rbegin()->first;
264 expect_snapshot_get(mock_image_ctx,
265 {snap_id, {cls::rbd::UserSnapshotNamespace{}},
266 "snap1", 123, {}, 0}, 0);
267
268 expect_get_parent_spec(mock_image_ctx, 0);
269 expect_object_map_snap_remove(mock_image_ctx, 0);
270 expect_release_snap_id(mock_image_ctx);
271 expect_snap_remove(mock_image_ctx, 0);
272 expect_rm_snap(mock_image_ctx);
273
274 C_SaferCond cond_ctx;
275 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
276 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
277 snap_id);
278 {
279 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
280 req->send();
281 }
282 ASSERT_EQ(0, cond_ctx.wait());
283 }
284
285 TEST_F(TestMockOperationSnapshotRemoveRequest, SuccessCloneParent) {
286 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
287
288 librbd::ImageCtx *ictx;
289 ASSERT_EQ(0, open_image(m_image_name, &ictx));
290 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
291 ASSERT_EQ(0, ictx->state->refresh_if_required());
292
293 MockImageCtx mock_image_ctx(*ictx);
294
295 MockExclusiveLock mock_exclusive_lock;
296 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
297 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
298 }
299
300 MockObjectMap mock_object_map;
301 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
302 mock_image_ctx.object_map = &mock_object_map;
303 }
304
305 expect_op_work_queue(mock_image_ctx);
306
307 ::testing::InSequence seq;
308 expect_snapshot_trash_add(mock_image_ctx, 0);
309
310 uint64_t snap_id = ictx->snap_info.rbegin()->first;
311 expect_snapshot_get(mock_image_ctx,
312 {snap_id, {cls::rbd::UserSnapshotNamespace{}},
313 "snap1", 123, {}, 1}, 0);
314
315 const cls::rbd::ChildImageSpecs child_images;
316 expect_children_list(mock_image_ctx, child_images, 0);
317 expect_get_parent_spec(mock_image_ctx, 0);
318
319 C_SaferCond cond_ctx;
320 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
321 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
322 snap_id);
323 {
324 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
325 req->send();
326 }
327 ASSERT_EQ(0, cond_ctx.wait());
328 }
329
330 TEST_F(TestMockOperationSnapshotRemoveRequest, SuccessTrash) {
331 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
332
333 librbd::ImageCtx *ictx;
334 ASSERT_EQ(0, open_image(m_image_name, &ictx));
335 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
336 ASSERT_EQ(0, ictx->state->refresh_if_required());
337
338 MockImageCtx mock_image_ctx(*ictx);
339
340 MockExclusiveLock mock_exclusive_lock;
341 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
342 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
343 }
344
345 MockObjectMap mock_object_map;
346 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
347 mock_image_ctx.object_map = &mock_object_map;
348 }
349
350 expect_op_work_queue(mock_image_ctx);
351
352 ::testing::InSequence seq;
353 expect_snapshot_trash_add(mock_image_ctx, 0);
354
355 uint64_t snap_id = ictx->snap_info.rbegin()->first;
356 expect_snapshot_get(mock_image_ctx,
357 {snap_id,
358 {cls::rbd::TrashSnapshotNamespace{
359 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER, "snap1"}},
360 "snap1", 123, {}, 0}, 0);
361
362 expect_get_parent_spec(mock_image_ctx, 0);
363 expect_object_map_snap_remove(mock_image_ctx, 0);
364 expect_release_snap_id(mock_image_ctx);
365 expect_snap_remove(mock_image_ctx, 0);
366 expect_rm_snap(mock_image_ctx);
367
368 C_SaferCond cond_ctx;
369 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
370 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
371 snap_id);
372 {
373 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
374 req->send();
375 }
376 ASSERT_EQ(0, cond_ctx.wait());
377 }
378
379 TEST_F(TestMockOperationSnapshotRemoveRequest, FlattenedCloneRemovesChild) {
380 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
381 REQUIRE(!is_feature_enabled(RBD_FEATURE_DEEP_FLATTEN))
382
383 ASSERT_EQ(0, create_snapshot("snap1"));
384
385 int order = 22;
386 uint64_t features;
387 ASSERT_TRUE(::get_features(&features));
388 std::string clone_name = get_temp_image_name();
389 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
390 clone_name.c_str(), features, &order, 0, 0));
391
392 librbd::ImageCtx *ictx;
393 ASSERT_EQ(0, open_image(clone_name, &ictx));
394 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
395
396 librbd::NoOpProgressContext prog_ctx;
397 ASSERT_EQ(0, flatten(*ictx, prog_ctx));
398 ASSERT_EQ(0, ictx->state->refresh_if_required());
399
400 MockImageCtx mock_image_ctx(*ictx);
401
402 MockExclusiveLock mock_exclusive_lock;
403 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
404 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
405 }
406
407 MockObjectMap mock_object_map;
408 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
409 mock_image_ctx.object_map = &mock_object_map;
410 }
411
412 expect_op_work_queue(mock_image_ctx);
413
414 ::testing::InSequence seq;
415 expect_snapshot_trash_add(mock_image_ctx, 0);
416
417 uint64_t snap_id = ictx->snap_info.rbegin()->first;
418 expect_snapshot_get(mock_image_ctx,
419 {snap_id, {cls::rbd::UserSnapshotNamespace{}},
420 "snap1", 123, {}, 0}, 0);
421
422 expect_get_parent_spec(mock_image_ctx, 0);
423
424 MockDetachChildRequest mock_detach_child_request;
425 expect_detach_child(mock_image_ctx, mock_detach_child_request, -ENOENT);
426
427 expect_object_map_snap_remove(mock_image_ctx, 0);
428
429 expect_release_snap_id(mock_image_ctx);
430 expect_snap_remove(mock_image_ctx, 0);
431 expect_rm_snap(mock_image_ctx);
432
433 C_SaferCond cond_ctx;
434 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
435 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
436 snap_id);
437 {
438 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
439 req->send();
440 }
441 ASSERT_EQ(0, cond_ctx.wait());
442 }
443
444 TEST_F(TestMockOperationSnapshotRemoveRequest, TrashCloneParent) {
445 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
446
447 librbd::ImageCtx *ictx;
448 ASSERT_EQ(0, open_image(m_image_name, &ictx));
449 NoOpProgressContext prog_ctx;
450 ASSERT_EQ(0, ictx->operations->snap_create(
451 {cls::rbd::TrashSnapshotNamespace{}}, "snap1", 0, prog_ctx));
452 ASSERT_EQ(0, ictx->state->refresh_if_required());
453
454 MockImageCtx mock_image_ctx(*ictx);
455
456 MockExclusiveLock mock_exclusive_lock;
457 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
458 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
459 }
460
461 MockObjectMap mock_object_map;
462 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
463 mock_image_ctx.object_map = &mock_object_map;
464 }
465
466 expect_op_work_queue(mock_image_ctx);
467
468 ::testing::InSequence seq;
469
470 uint64_t snap_id = ictx->snap_info.rbegin()->first;
471 expect_snapshot_get(mock_image_ctx,
472 {snap_id, {cls::rbd::TrashSnapshotNamespace{}},
473 "snap1", 123, {}, 1}, 0);
474 const cls::rbd::ChildImageSpecs child_images;
475 expect_children_list(mock_image_ctx, child_images, 0);
476 expect_get_parent_spec(mock_image_ctx, 0);
477
478 C_SaferCond cond_ctx;
479 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
480 mock_image_ctx, &cond_ctx, cls::rbd::TrashSnapshotNamespace{}, "snap1",
481 snap_id);
482 {
483 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
484 req->send();
485 }
486 ASSERT_EQ(-EBUSY, cond_ctx.wait());
487 }
488
489 TEST_F(TestMockOperationSnapshotRemoveRequest, MirrorSnapshot) {
490 REQUIRE_FORMAT_V2();
491
492 librbd::ImageCtx *ictx;
493 ASSERT_EQ(0, open_image(m_image_name, &ictx));
494 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
495 ASSERT_EQ(0, ictx->state->refresh_if_required());
496
497 MockImageCtx mock_image_ctx(*ictx);
498
499 MockExclusiveLock mock_exclusive_lock;
500 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
501 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
502 }
503
504 MockObjectMap mock_object_map;
505 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
506 mock_image_ctx.object_map = &mock_object_map;
507 }
508
509 expect_op_work_queue(mock_image_ctx);
510
511 ::testing::InSequence seq;
512 expect_snapshot_trash_add(mock_image_ctx, 0);
513
514 uint64_t snap_id = ictx->snap_info.rbegin()->first;
515 expect_snapshot_get(mock_image_ctx,
516 {snap_id, {cls::rbd::MirrorSnapshotNamespace{}},
517 "mirror", 123, {}, 0}, 0);
518
519 expect_get_parent_spec(mock_image_ctx, 0);
520 expect_object_map_snap_remove(mock_image_ctx, 0);
521 MockRemoveImageStateRequest mock_remove_image_state_request;
522 expect_remove_image_state(mock_image_ctx, mock_remove_image_state_request, 0);
523 expect_release_snap_id(mock_image_ctx);
524 expect_snap_remove(mock_image_ctx, 0);
525 expect_rm_snap(mock_image_ctx);
526
527 C_SaferCond cond_ctx;
528 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
529 mock_image_ctx, &cond_ctx, cls::rbd::MirrorSnapshotNamespace(),
530 "mirror", snap_id);
531 {
532 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
533 req->send();
534 }
535 ASSERT_EQ(0, cond_ctx.wait());
536 }
537
538 TEST_F(TestMockOperationSnapshotRemoveRequest, SnapshotTrashAddNotSupported) {
539 REQUIRE_FORMAT_V2();
540
541 librbd::ImageCtx *ictx;
542 ASSERT_EQ(0, open_image(m_image_name, &ictx));
543 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
544 ASSERT_EQ(0, ictx->state->refresh_if_required());
545
546 MockImageCtx mock_image_ctx(*ictx);
547
548 MockExclusiveLock mock_exclusive_lock;
549 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
550 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
551 }
552
553 MockObjectMap mock_object_map;
554 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
555 mock_image_ctx.object_map = &mock_object_map;
556 }
557
558 expect_op_work_queue(mock_image_ctx);
559
560 ::testing::InSequence seq;
561 expect_snapshot_trash_add(mock_image_ctx, -EOPNOTSUPP);
562
563 uint64_t snap_id = ictx->snap_info.rbegin()->first;
564 expect_get_parent_spec(mock_image_ctx, 0);
565 expect_object_map_snap_remove(mock_image_ctx, 0);
566 expect_release_snap_id(mock_image_ctx);
567 expect_snap_remove(mock_image_ctx, 0);
568 expect_rm_snap(mock_image_ctx);
569
570 C_SaferCond cond_ctx;
571 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
572 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
573 snap_id);
574 {
575 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
576 req->send();
577 }
578 ASSERT_EQ(0, cond_ctx.wait());
579 }
580
581 TEST_F(TestMockOperationSnapshotRemoveRequest, SnapshotTrashAddError) {
582 REQUIRE_FORMAT_V2();
583
584 librbd::ImageCtx *ictx;
585 ASSERT_EQ(0, open_image(m_image_name, &ictx));
586 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
587 ASSERT_EQ(0, ictx->state->refresh_if_required());
588
589 MockImageCtx mock_image_ctx(*ictx);
590 expect_op_work_queue(mock_image_ctx);
591
592 ::testing::InSequence seq;
593 uint64_t snap_id = ictx->snap_info.rbegin()->first;
594 expect_snapshot_trash_add(mock_image_ctx, -EINVAL);
595
596 C_SaferCond cond_ctx;
597 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
598 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
599 snap_id);
600 {
601 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
602 req->send();
603 }
604 ASSERT_EQ(-EINVAL, cond_ctx.wait());
605 }
606
607 TEST_F(TestMockOperationSnapshotRemoveRequest, SnapshotGetError) {
608 REQUIRE_FORMAT_V2();
609
610 librbd::ImageCtx *ictx;
611 ASSERT_EQ(0, open_image(m_image_name, &ictx));
612 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
613 ASSERT_EQ(0, ictx->state->refresh_if_required());
614
615 MockImageCtx mock_image_ctx(*ictx);
616 expect_op_work_queue(mock_image_ctx);
617
618 ::testing::InSequence seq;
619 expect_snapshot_trash_add(mock_image_ctx, 0);
620
621 uint64_t snap_id = ictx->snap_info.rbegin()->first;
622 expect_snapshot_get(mock_image_ctx,
623 {snap_id, {cls::rbd::UserSnapshotNamespace{}},
624 "snap1", 123, {}, 0}, -EOPNOTSUPP);
625
626 C_SaferCond cond_ctx;
627 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
628 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
629 snap_id);
630 {
631 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
632 req->send();
633 }
634 ASSERT_EQ(-EOPNOTSUPP, cond_ctx.wait());
635 }
636
637 TEST_F(TestMockOperationSnapshotRemoveRequest, ObjectMapSnapRemoveError) {
638 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
639
640 librbd::ImageCtx *ictx;
641 ASSERT_EQ(0, open_image(m_image_name, &ictx));
642 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
643 ASSERT_EQ(0, ictx->state->refresh_if_required());
644
645 MockImageCtx mock_image_ctx(*ictx);
646 MockObjectMap mock_object_map;
647 mock_image_ctx.object_map = &mock_object_map;
648
649 expect_op_work_queue(mock_image_ctx);
650
651 ::testing::InSequence seq;
652 expect_snapshot_trash_add(mock_image_ctx, 0);
653
654 uint64_t snap_id = ictx->snap_info.rbegin()->first;
655 expect_snapshot_get(mock_image_ctx,
656 {snap_id, {cls::rbd::UserSnapshotNamespace{}},
657 "snap1", 123, {}, 0}, 0);
658
659 expect_get_parent_spec(mock_image_ctx, 0);
660
661 expect_object_map_snap_remove(mock_image_ctx, -EINVAL);
662
663 C_SaferCond cond_ctx;
664 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
665 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
666 snap_id);
667 {
668 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
669 req->send();
670 }
671 ASSERT_EQ(-EINVAL, cond_ctx.wait());
672 }
673
674 TEST_F(TestMockOperationSnapshotRemoveRequest, RemoveChildParentError) {
675 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
676
677 librbd::ImageCtx *ictx;
678 ASSERT_EQ(0, open_image(m_image_name, &ictx));
679 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
680 ASSERT_EQ(0, ictx->state->refresh_if_required());
681
682 MockImageCtx mock_image_ctx(*ictx);
683
684 MockObjectMap mock_object_map;
685 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
686 mock_image_ctx.object_map = &mock_object_map;
687 }
688
689 expect_op_work_queue(mock_image_ctx);
690
691 ::testing::InSequence seq;
692 expect_snapshot_trash_add(mock_image_ctx, 0);
693
694 uint64_t snap_id = ictx->snap_info.rbegin()->first;
695 expect_snapshot_get(mock_image_ctx,
696 {snap_id, {cls::rbd::UserSnapshotNamespace{}},
697 "snap1", 123, {}, 0}, 0);
698
699 expect_get_parent_spec(mock_image_ctx, -ENOENT);
700
701 C_SaferCond cond_ctx;
702 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
703 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
704 snap_id);
705 {
706 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
707 req->send();
708 }
709 ASSERT_EQ(-ENOENT, cond_ctx.wait());
710 }
711
712 TEST_F(TestMockOperationSnapshotRemoveRequest, RemoveChildError) {
713 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
714
715 ASSERT_EQ(0, create_snapshot("snap1"));
716
717 int order = 22;
718 uint64_t features;
719 ASSERT_TRUE(::get_features(&features));
720 std::string clone_name = get_temp_image_name();
721 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
722 clone_name.c_str(), features, &order, 0, 0));
723
724 librbd::ImageCtx *ictx;
725 ASSERT_EQ(0, open_image(clone_name, &ictx));
726 if (ictx->test_features(RBD_FEATURE_DEEP_FLATTEN)) {
727 GTEST_SKIP() << "Skipping due to enabled deep-flatten";
728 }
729
730 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
731
732 librbd::NoOpProgressContext prog_ctx;
733 ASSERT_EQ(0, flatten(*ictx, prog_ctx));
734 ASSERT_EQ(0, ictx->state->refresh_if_required());
735
736 MockImageCtx mock_image_ctx(*ictx);
737
738 MockObjectMap mock_object_map;
739 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
740 mock_image_ctx.object_map = &mock_object_map;
741 }
742
743 expect_op_work_queue(mock_image_ctx);
744
745 uint64_t snap_id = ictx->snap_info.rbegin()->first;
746 expect_get_parent_spec(mock_image_ctx, 0);
747
748 MockDetachChildRequest mock_detach_child_request;
749 expect_detach_child(mock_image_ctx, mock_detach_child_request, -EINVAL);
750
751 C_SaferCond cond_ctx;
752 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
753 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
754 snap_id);
755 {
756 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
757 req->send();
758 }
759 ASSERT_EQ(-EINVAL, cond_ctx.wait());
760 }
761
762 TEST_F(TestMockOperationSnapshotRemoveRequest, RemoveSnapError) {
763 librbd::ImageCtx *ictx;
764 ASSERT_EQ(0, open_image(m_image_name, &ictx));
765 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
766 ASSERT_EQ(0, ictx->state->refresh_if_required());
767
768 MockImageCtx mock_image_ctx(*ictx);
769
770 MockExclusiveLock mock_exclusive_lock;
771 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
772 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
773 }
774
775 MockObjectMap mock_object_map;
776 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
777 mock_image_ctx.object_map = &mock_object_map;
778 }
779
780 expect_op_work_queue(mock_image_ctx);
781
782 ::testing::InSequence seq;
783 expect_snapshot_trash_add(mock_image_ctx, 0);
784
785 uint64_t snap_id = ictx->snap_info.rbegin()->first;
786 expect_snapshot_get(mock_image_ctx,
787 {snap_id, {cls::rbd::UserSnapshotNamespace{}},
788 "snap1", 123, {}, 0}, 0);
789
790 expect_get_parent_spec(mock_image_ctx, 0);
791 expect_object_map_snap_remove(mock_image_ctx, 0);
792 expect_release_snap_id(mock_image_ctx);
793 expect_snap_remove(mock_image_ctx, -ENOENT);
794
795 C_SaferCond cond_ctx;
796 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
797 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
798 snap_id);
799 {
800 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
801 req->send();
802 }
803 ASSERT_EQ(-ENOENT, cond_ctx.wait());
804 }
805
806 TEST_F(TestMockOperationSnapshotRemoveRequest, MissingSnap) {
807 librbd::ImageCtx *ictx;
808 ASSERT_EQ(0, open_image(m_image_name, &ictx));
809
810 MockImageCtx mock_image_ctx(*ictx);
811
812 MockExclusiveLock mock_exclusive_lock;
813 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
814 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
815 }
816
817 MockObjectMap mock_object_map;
818 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
819 mock_image_ctx.object_map = &mock_object_map;
820 }
821
822 expect_op_work_queue(mock_image_ctx);
823
824 ::testing::InSequence seq;
825 uint64_t snap_id = 456;
826
827 C_SaferCond cond_ctx;
828 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
829 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
830 snap_id);
831 {
832 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
833 req->send();
834 }
835 ASSERT_EQ(-ENOENT, cond_ctx.wait());
836 }
837
838 TEST_F(TestMockOperationSnapshotRemoveRequest, ListChildrenError) {
839 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
840
841 librbd::ImageCtx *ictx;
842 ASSERT_EQ(0, open_image(m_image_name, &ictx));
843 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
844 ASSERT_EQ(0, ictx->state->refresh_if_required());
845
846 MockImageCtx mock_image_ctx(*ictx);
847
848 MockExclusiveLock mock_exclusive_lock;
849 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
850 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
851 }
852
853 MockObjectMap mock_object_map;
854 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
855 mock_image_ctx.object_map = &mock_object_map;
856 }
857
858 expect_op_work_queue(mock_image_ctx);
859
860 ::testing::InSequence seq;
861 expect_snapshot_trash_add(mock_image_ctx, 0);
862
863 uint64_t snap_id = ictx->snap_info.rbegin()->first;
864 expect_snapshot_get(mock_image_ctx,
865 {snap_id, {cls::rbd::UserSnapshotNamespace{}},
866 "snap1", 123, {}, 1}, 0);
867 const cls::rbd::ChildImageSpecs child_images;
868 expect_children_list(mock_image_ctx, child_images, -EINVAL);
869
870 C_SaferCond cond_ctx;
871 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
872 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
873 snap_id);
874 {
875 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
876 req->send();
877 }
878 ASSERT_EQ(-EINVAL, cond_ctx.wait());
879 }
880
881 TEST_F(TestMockOperationSnapshotRemoveRequest, DetachStaleChildError) {
882 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
883
884 ASSERT_EQ(0, create_snapshot("snap1"));
885
886 int order = 22;
887 uint64_t features;
888 ASSERT_TRUE(::get_features(&features));
889 std::string clone_name = get_temp_image_name();
890 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
891 clone_name.c_str(), features, &order, 0, 0));
892
893 librbd::ImageCtx *ictx;
894 ASSERT_EQ(0, open_image(clone_name, &ictx));
895 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
896 ASSERT_EQ(0, ictx->state->refresh_if_required());
897
898 MockImageCtx mock_image_ctx(*ictx);
899 expect_op_work_queue(mock_image_ctx);
900
901 ::testing::InSequence seq;
902 expect_snapshot_trash_add(mock_image_ctx, 0);
903
904 uint64_t snap_id = ictx->snap_info.rbegin()->first;
905 expect_snapshot_get(mock_image_ctx,
906 {snap_id, {cls::rbd::UserSnapshotNamespace{}},
907 "snap1", 123, {}, 1}, 0);
908 const cls::rbd::ChildImageSpecs child_images;
909 expect_children_list(mock_image_ctx, child_images, -EINVAL);
910
911 C_SaferCond cond_ctx;
912 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
913 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
914 snap_id);
915 {
916 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
917 req->send();
918 }
919 ASSERT_EQ(-EINVAL, cond_ctx.wait());
920 }
921
922 } // namespace operation
923 } // namespace librbd