]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: fix deep copy a child-image 20099/head
authorsongweibin <song.weibin@zte.com.cn>
Fri, 26 Jan 2018 03:22:27 +0000 (11:22 +0800)
committersongweibin <song.weibin@zte.com.cn>
Thu, 1 Feb 2018 00:17:19 +0000 (08:17 +0800)
* 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 <song.weibin@zte.com.cn>
qa/workunits/rbd/cli_generic.sh
src/librbd/api/Image.cc
src/test/librbd/test_librbd.cc
src/test/pybind/test_rbd.py

index a2a94385ce6352e6817e07947cb727b0e7397ea4..eb43258326b756c76371e2d4420e14eefc69d239 100755 (executable)
@@ -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
index d4983d5f299901b1342aef0a046ab6caadda1709..86087356023f009964a6fbffd7e915f4294d445c 100644 (file)
@@ -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<I>::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<I>::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;
index 7d019b4333f411f0f988149dd187c4e15cd463a6..6b9c75305a00540a40722ec58fa0410394682073 100644 (file)
@@ -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)
index e376e92a9a2c96cf63fbcb136cdcc101c730f2a9..ae3f975a33cef723cd6481b0e9a7ae468aead4e4 100644 (file)
@@ -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')