Skip to content

Commit

Permalink
Merge pull request #20099 from Songweibin/wip-fix-deep-cp
Browse files Browse the repository at this point in the history
librbd: fix deep copy a child-image

Reviewed-by: Jason Dillaman <dillaman@redhat.com>
  • Loading branch information
Jason Dillaman committed Feb 2, 2018
2 parents d9637b7 + c3e119d commit 4b69976
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 1 deletion.
16 changes: 16 additions & 0 deletions qa/workunits/rbd/cli_generic.sh
Expand Up @@ -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
Expand Down
71 changes: 70 additions & 1 deletion src/librbd/api/Image.cc
Expand Up @@ -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
Expand Down Expand Up @@ -173,7 +175,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;
Expand Down
68 changes: 68 additions & 0 deletions src/test/librbd/test_librbd.cc
Expand Up @@ -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);
Expand All @@ -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;

Expand Down Expand Up @@ -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)
Expand Down
28 changes: 28 additions & 0 deletions src/test/pybind/test_rbd.py
Expand Up @@ -518,6 +518,34 @@ def test_deep_copy(self):
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')
Expand Down

0 comments on commit 4b69976

Please sign in to comment.