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