From c3e119d0d39492cd92272c2f71c6ac5c0ec6d18d Mon Sep 17 00:00:00 2001 From: songweibin Date: Fri, 26 Jan 2018 11:22:27 +0800 Subject: [PATCH] librbd: fix deep copy a child-image * tweak create a cloned image when the source image is a clone (or at least one of its snapshots is a clone). Signed-off-by: songweibin --- qa/workunits/rbd/cli_generic.sh | 16 ++++++++ src/librbd/api/Image.cc | 71 ++++++++++++++++++++++++++++++++- src/test/librbd/test_librbd.cc | 68 +++++++++++++++++++++++++++++++ src/test/pybind/test_rbd.py | 28 +++++++++++++ 4 files changed, 182 insertions(+), 1 deletion(-) diff --git a/qa/workunits/rbd/cli_generic.sh b/qa/workunits/rbd/cli_generic.sh index a2a94385ce63..eb43258326b7 100755 --- a/qa/workunits/rbd/cli_generic.sh +++ b/qa/workunits/rbd/cli_generic.sh @@ -81,6 +81,22 @@ test_others() { rbd snap ls testimg4 | grep -v 'SNAPID' | wc -l | grep 1 rbd snap ls testimg4 | grep '.*snap1.*' + # deep copy clone-image + rbd snap rm testimg4@snap1 + rbd snap rm testimg5@snap1 + rbd rm testimg4 + rbd rm testimg5 + rbd snap protect testimg1@snap1 + rbd clone testimg1@snap1 testimg4 + rbd snap create testimg4@snap2 + rbd deep copy testimg4 testimg5 + rbd info testimg5 | grep 'size 256 MB' + rbd snap ls testimg5 | grep -v 'SNAPID' | wc -l | grep 1 + rbd snap ls testimg5 | grep '.*snap2.*' + rbd flatten testimg4 + rbd flatten testimg5 + rbd snap unprotect testimg1@snap1 + rbd export testimg1 /tmp/img1.new rbd export testimg2 /tmp/img2.new rbd export testimg3 /tmp/img3.new diff --git a/src/librbd/api/Image.cc b/src/librbd/api/Image.cc index d4983d5f2999..86087356023f 100644 --- a/src/librbd/api/Image.cc +++ b/src/librbd/api/Image.cc @@ -10,6 +10,8 @@ #include "librbd/ExclusiveLock.h" #include "librbd/ImageCtx.h" #include "librbd/ImageState.h" +#include "librbd/Utils.h" +#include "librbd/image/CloneRequest.h" #include "librbd/internal.h" #define dout_subsys ceph_subsys_rbd @@ -158,7 +160,74 @@ int Image::deep_copy(I *src, librados::IoCtx& dest_md_ctx, return -ENOSYS; } - int r = create(dest_md_ctx, destname, "", src_size, opts, "", "", false); + ParentSpec parent_spec; + { + RWLock::RLocker snap_locker(src->snap_lock); + RWLock::RLocker parent_locker(src->parent_lock); + + // use oldest snapshot or HEAD for parent spec + if (!src->snap_info.empty()) { + parent_spec = src->snap_info.begin()->second.parent.spec; + } else { + parent_spec = src->parent_md.spec; + } + } + + int r; + if (parent_spec.pool_id == -1) { + r = create(dest_md_ctx, destname, "", src_size, opts, "", "", false); + } else { + librados::Rados rados(src->md_ctx); + librados::IoCtx parent_io_ctx; + r = rados.ioctx_create2(parent_spec.pool_id, parent_io_ctx); + if (r < 0) { + lderr(cct) << "failed to open source parent pool: " + << cpp_strerror(r) << dendl; + return r; + } + ImageCtx *src_parent_image_ctx = + new ImageCtx("", parent_spec.image_id, nullptr, parent_io_ctx, false); + r = src_parent_image_ctx->state->open(true); + if (r < 0) { + if (r != -ENOENT) { + lderr(cct) << "failed to open source parent image: " + << cpp_strerror(r) << dendl; + } + return r; + } + std::string snap_name; + { + RWLock::RLocker parent_snap_locker(src_parent_image_ctx->snap_lock); + auto it = src_parent_image_ctx->snap_info.find(parent_spec.snap_id); + if (it == src_parent_image_ctx->snap_info.end()) { + return -ENOENT; + } + snap_name = it->second.name; + } + + C_SaferCond cond; + src_parent_image_ctx->state->snap_set(cls::rbd::UserSnapshotNamespace(), + snap_name, &cond); + r = cond.wait(); + if (r < 0) { + if (r != -ENOENT) { + lderr(cct) << "failed to set snapshot: " << cpp_strerror(r) << dendl; + } + return r; + } + + C_SaferCond ctx; + std::string dest_id = util::generate_image_id(dest_md_ctx); + auto *req = image::CloneRequest::create( + src_parent_image_ctx, dest_md_ctx, destname, dest_id, opts, + "", "", src->op_work_queue, &ctx); + req->send(); + r = ctx.wait(); + int close_r = src_parent_image_ctx->state->close(); + if (r == 0 && close_r < 0) { + r = close_r; + } + } if (r < 0) { lderr(cct) << "header creation failed" << dendl; return r; diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index 7d019b4333f4..6b9c75305a00 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -1082,6 +1082,7 @@ TEST_F(TestLibRBD, TestCopyPP) TEST_F(TestLibRBD, TestDeepCopy) { REQUIRE_FORMAT_V2(); + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); rados_ioctx_t ioctx; rados_ioctx_create(_cluster, create_pool(true).c_str(), &ioctx); @@ -1092,10 +1093,16 @@ TEST_F(TestLibRBD, TestDeepCopy) rbd_image_t image; rbd_image_t image2; rbd_image_t image3; + rbd_image_t image4; + rbd_image_t image5; + rbd_image_t image6; int order = 0; std::string name = get_temp_image_name(); std::string name2 = get_temp_image_name(); std::string name3 = get_temp_image_name(); + std::string name4 = get_temp_image_name(); + std::string name5 = get_temp_image_name(); + std::string name6 = get_temp_image_name(); uint64_t size = 2 << 20; @@ -1176,6 +1183,67 @@ TEST_F(TestLibRBD, TestDeepCopy) value_len = sizeof(value); } + + ASSERT_EQ(0, rbd_snap_create(image, "deep_snap")); + ASSERT_EQ(0, rbd_close(image)); + ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, "deep_snap")); + ASSERT_EQ(0, rbd_snap_protect(image, "deep_snap")); + ASSERT_EQ(0, rbd_clone3(ioctx, name.c_str(), "deep_snap", ioctx, + name4.c_str(), opts)); + + ASSERT_EQ(4, test_ls(ioctx, 4, name.c_str(), name2.c_str(), name3.c_str(), + name4.c_str())); + ASSERT_EQ(0, rbd_open(ioctx, name4.c_str(), &image4, NULL)); + BOOST_SCOPE_EXIT_ALL( (&image4) ) { + ASSERT_EQ(0, rbd_close(image4)); + }; + ASSERT_EQ(0, rbd_snap_create(image4, "deep_snap")); + + ASSERT_EQ(0, rbd_deep_copy(image4, ioctx, name5.c_str(), opts)); + ASSERT_EQ(5, test_ls(ioctx, 5, name.c_str(), name2.c_str(), name3.c_str(), + name4.c_str(), name5.c_str())); + ASSERT_EQ(0, rbd_open(ioctx, name5.c_str(), &image5, NULL)); + BOOST_SCOPE_EXIT_ALL( (&image5) ) { + ASSERT_EQ(0, rbd_close(image5)); + }; + ASSERT_EQ(0, rbd_metadata_list(image5, "", 70, keys, &keys_len, vals, + &vals_len)); + ASSERT_EQ(keys_len, sum_key_len); + ASSERT_EQ(vals_len, sum_value_len); + + for (int i = 1; i <= 70; i++) { + key = "key" + stringify(i); + val = "value" + stringify(i); + ASSERT_EQ(0, rbd_metadata_get(image5, key.c_str(), value, &value_len)); + ASSERT_STREQ(val.c_str(), value); + + value_len = sizeof(value); + } + + ASSERT_EQ(0, rbd_deep_copy_with_progress(image4, ioctx, name6.c_str(), opts, + print_progress_percent, NULL)); + ASSERT_EQ(6, test_ls(ioctx, 6, name.c_str(), name2.c_str(), name3.c_str(), + name4.c_str(), name5.c_str(), name6.c_str())); + + keys_len = sizeof(keys); + vals_len = sizeof(vals); + ASSERT_EQ(0, rbd_open(ioctx, name6.c_str(), &image6, NULL)); + BOOST_SCOPE_EXIT_ALL( (&image6) ) { + ASSERT_EQ(0, rbd_close(image6)); + }; + ASSERT_EQ(0, rbd_metadata_list(image6, "", 70, keys, &keys_len, vals, + &vals_len)); + ASSERT_EQ(keys_len, sum_key_len); + ASSERT_EQ(vals_len, sum_value_len); + + for (int i = 1; i <= 70; i++) { + key = "key" + stringify(i); + val = "value" + stringify(i); + ASSERT_EQ(0, rbd_metadata_get(image6, key.c_str(), value, &value_len)); + ASSERT_STREQ(val.c_str(), value); + + value_len = sizeof(value); + } } TEST_F(TestLibRBD, TestDeepCopyPP) diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index e376e92a9a2c..ae3f975a33ce 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -518,6 +518,34 @@ class TestImage(object): copy.remove_snap('snap1') self.rbd.remove(ioctx, dst_name) + def test_deep_copy_clone(self): + global ioctx + global features + self.image.write(b'a' * 256, 0) + self.image.create_snap('snap1') + self.image.write(b'b' * 256, 0) + self.image.protect_snap('snap1') + clone_name = get_temp_image_name() + dst_name = get_temp_image_name() + self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name) + with Image(ioctx, clone_name) as child: + child.create_snap('snap1') + child.deep_copy(ioctx, dst_name, features=features, + order=self.image.stat()['order'], + stripe_unit=self.image.stripe_unit(), + stripe_count=self.image.stripe_count(), + data_pool=None) + child.remove_snap('snap1') + + with Image(ioctx, dst_name) as copy: + copy_data = copy.read(0, 256) + eq(b'a' * 256, copy_data) + copy.remove_snap('snap1') + self.rbd.remove(ioctx, dst_name) + self.rbd.remove(ioctx, clone_name) + self.image.unprotect_snap('snap1') + self.image.remove_snap('snap1') + def test_create_snap(self): global ioctx self.image.create_snap('snap1') -- 2.47.3