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
#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
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;
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);
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;
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)
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')