From d7fd66ec9944653f3239ac82efe937efe71c4866 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Fri, 24 May 2024 12:06:09 +0200 Subject: [PATCH] librbd: add rbd_clone4() API to take parent snapshot by ID Allow cloning from non-user snapshots -- namely snapshots in group and mirror namespaces. The motivation is to provide a building block for cloning new groups from group snapshots ("rbd group snap create"). Otherwise, group snapshots as they are today can be used only for rolling back the group as a whole, which is very limiting. While at it, there doesn't seem to be anything wrong with making it possible to clone from mirror snapshots as well. Snapshots in a trash namespace can't be cloned from since they are considered to be deleted. Cloning from non-user snapshots is limited to clone v2 just because protecting/unprotecting is limited to snapshots in a user namespace. This happens to simplify some invariants. Fixes: https://tracker.ceph.com/issues/64662 Signed-off-by: Ilya Dryomov --- src/include/rbd/librbd.h | 3 + src/include/rbd/librbd.hpp | 2 + src/librbd/internal.cc | 34 +++-- src/librbd/internal.h | 4 +- src/librbd/librbd.cc | 40 +++++- src/pybind/rbd/c_rbd.pxd | 3 + src/pybind/rbd/mock_rbd.pxi | 4 + src/pybind/rbd/rbd.pyx | 27 +++- src/test/pybind/test_rbd.py | 166 ++++++++++++++++++++++- src/test/rbd_mirror/test_ImageDeleter.cc | 2 +- src/tracing/librbd.tp | 30 ++++ 11 files changed, 288 insertions(+), 27 deletions(-) diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index e94a72a719b..ce84b3ef749 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -479,6 +479,9 @@ CEPH_RBD_API int rbd_clone2(rados_ioctx_t p_ioctx, const char *p_name, CEPH_RBD_API int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name, const char *p_snapname, rados_ioctx_t c_ioctx, const char *c_name, rbd_image_options_t c_opts); +CEPH_RBD_API int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name, + uint64_t p_snap_id, rados_ioctx_t c_ioctx, + const char *c_name, rbd_image_options_t c_opts); CEPH_RBD_API int rbd_remove(rados_ioctx_t io, const char *name); CEPH_RBD_API int rbd_remove_with_progress(rados_ioctx_t io, const char *name, librbd_progress_fn_t cb, diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index 9224c850c55..cb6770ceb82 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -294,6 +294,8 @@ public: int *c_order, uint64_t stripe_unit, int stripe_count); int clone3(IoCtx& p_ioctx, const char *p_name, const char *p_snapname, IoCtx& c_ioctx, const char *c_name, ImageOptions& opts); + int clone4(IoCtx& p_ioctx, const char *p_name, uint64_t p_snap_id, + IoCtx& c_ioctx, const char *c_name, ImageOptions& opts); int remove(IoCtx& io_ctx, const char *name); int remove_with_progress(IoCtx& io_ctx, const char *name, ProgressContext& pctx); int rename(IoCtx& src_io_ctx, const char *srcname, const char *destname); diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc index 33b9dcfa5af..dd674f3a949 100644 --- a/src/librbd/internal.cc +++ b/src/librbd/internal.cc @@ -716,28 +716,40 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit); opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count); - int r = clone(p_ioctx, nullptr, p_name, p_snap_name, c_ioctx, nullptr, - c_name, opts, "", ""); + int r = clone(p_ioctx, nullptr, p_name, CEPH_NOSNAP, p_snap_name, + c_ioctx, nullptr, c_name, opts, "", ""); opts.get(RBD_IMAGE_OPTION_ORDER, &order); *c_order = order; return r; } int clone(IoCtx& p_ioctx, const char *p_id, const char *p_name, - const char *p_snap_name, IoCtx& c_ioctx, const char *c_id, - const char *c_name, ImageOptions& c_opts, + uint64_t p_snap_id, const char *p_snap_name, IoCtx& c_ioctx, + const char *c_id, const char *c_name, ImageOptions& c_opts, const std::string &non_primary_global_image_id, const std::string &primary_mirror_uuid) { CephContext *cct = (CephContext *)p_ioctx.cct(); + ldout(cct, 10) << __func__ + << " p_id=" << (p_id ?: "") + << ", p_name=" << (p_name ?: "") + << ", p_snap_id=" << p_snap_id + << ", p_snap_name=" << (p_snap_name ?: "") + << ", c_id=" << (c_id ?: "") + << ", c_name=" << c_name + << ", c_opts=" << c_opts + << ", non_primary_global_image_id=" << non_primary_global_image_id + << ", primary_mirror_uuid=" << primary_mirror_uuid + << dendl; if (((p_id == nullptr) ^ (p_name == nullptr)) == 0) { lderr(cct) << "must specify either parent image id or parent image name" << dendl; return -EINVAL; } - if (p_snap_name == nullptr) { - lderr(cct) << "image to be cloned must be a snapshot" << dendl; + if (((p_snap_id == CEPH_NOSNAP) ^ (p_snap_name == nullptr)) == 0) { + lderr(cct) << "must specify either parent snap id or parent snap name" + << dendl; return -EINVAL; } @@ -770,10 +782,8 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { clone_id = c_id; } - ldout(cct, 10) << __func__ << " " - << "c_name=" << c_name << ", " - << "c_id= " << clone_id << ", " - << "c_opts=" << c_opts << dendl; + ldout(cct, 10) << __func__ << " parent_id=" << parent_id + << ", clone_id=" << clone_id << dendl; ConfigProxy config{reinterpret_cast(c_ioctx.cct())->_conf}; api::Config<>::apply_pool_overrides(c_ioctx, &config); @@ -782,8 +792,8 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { C_SaferCond cond; auto *req = image::CloneRequest<>::create( - config, p_ioctx, parent_id, p_snap_name, - {cls::rbd::UserSnapshotNamespace{}}, CEPH_NOSNAP, c_ioctx, c_name, + config, p_ioctx, parent_id, (p_snap_name ?: ""), + {cls::rbd::UserSnapshotNamespace{}}, p_snap_id, c_ioctx, c_name, clone_id, c_opts, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, non_primary_global_image_id, primary_mirror_uuid, asio_engine.get_work_queue(), &cond); diff --git a/src/librbd/internal.h b/src/librbd/internal.h index 65e9a9d18fe..77a64137ddb 100644 --- a/src/librbd/internal.h +++ b/src/librbd/internal.h @@ -77,8 +77,8 @@ namespace librbd { uint64_t features, int *c_order, uint64_t stripe_unit, int stripe_count); int clone(IoCtx& p_ioctx, const char *p_id, const char *p_name, - const char *p_snap_name, IoCtx& c_ioctx, const char *c_id, - const char *c_name, ImageOptions& c_opts, + uint64_t p_snap_id, const char *p_snap_name, IoCtx& c_ioctx, + const char *c_id, const char *c_name, ImageOptions& c_opts, const std::string &non_primary_global_image_id, const std::string &primary_mirror_uuid); int rename(librados::IoCtx& io_ctx, const char *srcname, const char *dstname); diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 2fccdf5abb4..dfbf9f879c9 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -778,12 +778,26 @@ namespace librbd { { TracepointProvider::initialize(get_cct(p_ioctx)); tracepoint(librbd, clone3_enter, p_ioctx.get_pool_name().c_str(), p_ioctx.get_id(), p_name, p_snap_name, c_ioctx.get_pool_name().c_str(), c_ioctx.get_id(), c_name, c_opts.opts); - int r = librbd::clone(p_ioctx, nullptr, p_name, p_snap_name, c_ioctx, - nullptr, c_name, c_opts, "", ""); + int r = librbd::clone(p_ioctx, nullptr, p_name, CEPH_NOSNAP, p_snap_name, + c_ioctx, nullptr, c_name, c_opts, "", ""); tracepoint(librbd, clone3_exit, r); return r; } + int RBD::clone4(IoCtx& p_ioctx, const char *p_name, uint64_t p_snap_id, + IoCtx& c_ioctx, const char *c_name, ImageOptions& c_opts) + { + TracepointProvider::initialize(get_cct(p_ioctx)); + tracepoint(librbd, clone4_enter, p_ioctx.get_pool_name().c_str(), + p_ioctx.get_id(), p_name, p_snap_id, + c_ioctx.get_pool_name().c_str(), c_ioctx.get_id(), c_name, + c_opts.opts); + int r = librbd::clone(p_ioctx, nullptr, p_name, p_snap_id, nullptr, + c_ioctx, nullptr, c_name, c_opts, "", ""); + tracepoint(librbd, clone4_exit, r); + return r; + } + int RBD::remove(IoCtx& io_ctx, const char *name) { TracepointProvider::initialize(get_cct(io_ctx)); @@ -3978,12 +3992,30 @@ extern "C" int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name, TracepointProvider::initialize(get_cct(p_ioc)); tracepoint(librbd, clone3_enter, p_ioc.get_pool_name().c_str(), p_ioc.get_id(), p_name, p_snap_name, c_ioc.get_pool_name().c_str(), c_ioc.get_id(), c_name, c_opts); librbd::ImageOptions c_opts_(c_opts); - int r = librbd::clone(p_ioc, nullptr, p_name, p_snap_name, c_ioc, nullptr, - c_name, c_opts_, "", ""); + int r = librbd::clone(p_ioc, nullptr, p_name, CEPH_NOSNAP, p_snap_name, + c_ioc, nullptr, c_name, c_opts_, "", ""); tracepoint(librbd, clone3_exit, r); return r; } +extern "C" int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name, + uint64_t p_snap_id, rados_ioctx_t c_ioctx, + const char *c_name, rbd_image_options_t c_opts) +{ + librados::IoCtx p_ioc, c_ioc; + librados::IoCtx::from_rados_ioctx_t(p_ioctx, p_ioc); + librados::IoCtx::from_rados_ioctx_t(c_ioctx, c_ioc); + TracepointProvider::initialize(get_cct(p_ioc)); + tracepoint(librbd, clone4_enter, p_ioc.get_pool_name().c_str(), + p_ioc.get_id(), p_name, p_snap_id, c_ioc.get_pool_name().c_str(), + c_ioc.get_id(), c_name, c_opts); + librbd::ImageOptions c_opts_(c_opts); + int r = librbd::clone(p_ioc, nullptr, p_name, p_snap_id, nullptr, + c_ioc, nullptr, c_name, c_opts_, "", ""); + tracepoint(librbd, clone4_exit, r); + return r; +} + extern "C" int rbd_remove(rados_ioctx_t p, const char *name) { librados::IoCtx io_ctx; diff --git a/src/pybind/rbd/c_rbd.pxd b/src/pybind/rbd/c_rbd.pxd index e7d1958bf31..61f7bff389f 100644 --- a/src/pybind/rbd/c_rbd.pxd +++ b/src/pybind/rbd/c_rbd.pxd @@ -329,6 +329,9 @@ cdef extern from "rbd/librbd.h" nogil: int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name, const char *p_snapname, rados_ioctx_t c_ioctx, const char *c_name, rbd_image_options_t c_opts) + int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name, + uint64_t p_snap_id, rados_ioctx_t c_ioctx, + const char *c_name, rbd_image_options_t c_opts) int rbd_remove_with_progress(rados_ioctx_t io, const char *name, librbd_progress_fn_t cb, void *cbdata) int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname, diff --git a/src/pybind/rbd/mock_rbd.pxi b/src/pybind/rbd/mock_rbd.pxi index dc0d499664e..d88c136d3d6 100644 --- a/src/pybind/rbd/mock_rbd.pxi +++ b/src/pybind/rbd/mock_rbd.pxi @@ -350,6 +350,10 @@ cdef nogil: const char *p_snapname, rados_ioctx_t c_ioctx, const char *c_name, rbd_image_options_t c_opts): pass + int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name, + uint64_t p_snap_id, rados_ioctx_t c_ioctx, + const char *c_name, rbd_image_options_t c_opts): + pass int rbd_remove_with_progress(rados_ioctx_t io, const char *name, librbd_progress_fn_t cb, void *cbdata): pass diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index 6650b08c935..7902ded17e8 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -632,7 +632,7 @@ class RBD(object): if ret < 0: raise make_ex(ret, 'error creating image') - def clone(self, p_ioctx, p_name, p_snapname, c_ioctx, c_name, + def clone(self, p_ioctx, p_name, p_snapshot, c_ioctx, c_name, features=None, order=None, stripe_unit=None, stripe_count=None, data_pool=None, clone_format=None): """ @@ -642,7 +642,7 @@ class RBD(object): :type ioctx: :class:`rados.Ioctx` :param p_name: the parent image name :type name: str - :param p_snapname: the parent image snapshot name + :param p_snapshot: the parent image snapshot name or id :type name: str :param c_ioctx: the child context that represents the new clone :type ioctx: :class:`rados.Ioctx` @@ -666,7 +666,6 @@ class RBD(object): :raises: :class:`FunctionNotSupported` :raises: :class:`ArgumentOutOfRange` """ - p_snapname = cstr(p_snapname, 'p_snapname') p_name = cstr(p_name, 'p_name') c_name = cstr(c_name, 'c_name') data_pool = cstr(data_pool, 'data_pool', opt=True) @@ -674,9 +673,18 @@ class RBD(object): rados_ioctx_t _p_ioctx = convert_ioctx(p_ioctx) rados_ioctx_t _c_ioctx = convert_ioctx(c_ioctx) char *_p_name = p_name - char *_p_snapname = p_snapname + char *_p_snap_name + uint64_t _p_snap_id char *_c_name = c_name rbd_image_options_t opts + if isinstance(p_snapshot, str): + p_snap_name = cstr(p_snapshot, 'p_snapshot') + _p_snap_name = p_snap_name + elif isinstance(p_snapshot, int): + p_snap_name = None + _p_snap_id = p_snapshot + else: + raise TypeError("p_snapshot must be a string or an integer") rbd_image_options_create(&opts) try: @@ -698,9 +706,14 @@ class RBD(object): if clone_format is not None: rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_CLONE_FORMAT, clone_format) - with nogil: - ret = rbd_clone3(_p_ioctx, _p_name, _p_snapname, - _c_ioctx, _c_name, opts) + if p_snap_name is not None: + with nogil: + ret = rbd_clone3(_p_ioctx, _p_name, _p_snap_name, + _c_ioctx, _c_name, opts) + else: + with nogil: + ret = rbd_clone4(_p_ioctx, _p_name, _p_snap_id, + _c_ioctx, _c_name, opts) finally: rbd_image_options_destroy(opts) if ret < 0: diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index 3d8cefdb43f..deaf0458596 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -15,6 +15,7 @@ from assertions import (assert_equal as eq, assert_raises, assert_not_equal, assert_greater_equal) from datetime import datetime, timedelta from rados import (Rados, + LIBRADOS_SNAP_HEAD, LIBRADOS_OP_FLAG_FADVISE_DONTNEED, LIBRADOS_OP_FLAG_FADVISE_NOCACHE, LIBRADOS_OP_FLAG_FADVISE_RANDOM) @@ -33,6 +34,7 @@ from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists, RBD_MIRROR_IMAGE_MODE_JOURNAL, RBD_MIRROR_IMAGE_MODE_SNAPSHOT, RBD_LOCK_MODE_EXCLUSIVE, RBD_OPERATION_FEATURE_GROUP, RBD_OPERATION_FEATURE_CLONE_CHILD, + RBD_SNAP_NAMESPACE_TYPE_GROUP, RBD_SNAP_NAMESPACE_TYPE_TRASH, RBD_SNAP_NAMESPACE_TYPE_MIRROR, RBD_IMAGE_MIGRATION_STATE_PREPARED, RBD_CONFIG_SOURCE_CONFIG, @@ -1800,6 +1802,67 @@ class TestClone(object): # unprotect, remove parent snap happen in cleanup, and should succeed + def test_clone_by_snap_id(self): + clone_name2 = get_temp_image_name() + assert_raises(TypeError, self.rbd.clone, ioctx, image_name, + None, ioctx, clone_name2, features) + assert_raises(TypeError, self.rbd.clone, ioctx, image_name, + 1.0, ioctx, clone_name2, features) + assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name, + LIBRADOS_SNAP_HEAD, ioctx, clone_name2, features) + + self.image.create_snap('snap2') + snap_id = self.image.snap_get_id('snap2') + self.image.remove_snap('snap2') + assert_raises(ImageNotFound, self.image.snap_get_trash_namespace, + snap_id) + assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name, + snap_id, ioctx, clone_name2, features, clone_format=1) + assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name, + snap_id, ioctx, clone_name2, features, clone_format=2) + + snap_id = self.image.snap_get_id('snap1') + self.rbd.clone(ioctx, image_name, snap_id, ioctx, clone_name2, + features, clone_format=1) + with Image(ioctx, clone_name2) as clone2: + assert clone2.parent_info() == self.clone.parent_info() + assert clone2.op_features() == 0 + self.rbd.remove(ioctx, clone_name2) + self.rbd.clone(ioctx, image_name, snap_id, ioctx, clone_name2, + features, clone_format=2) + with Image(ioctx, clone_name2) as clone2: + assert clone2.parent_info() == self.clone.parent_info() + assert clone2.op_features() == RBD_OPERATION_FEATURE_CLONE_CHILD + self.rbd.remove(ioctx, clone_name2) + + self.image.create_snap('snap2') + snap_id = self.image.snap_get_id('snap2') + assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name, + snap_id, ioctx, clone_name2, features, clone_format=1) + self.rbd.clone(ioctx, image_name, snap_id, ioctx, clone_name2, + features, clone_format=2) + with Image(ioctx, clone_name2) as clone2: + clone2_parent_info = clone2.parent_info() + clone_parent_info = self.clone.parent_info() + assert clone2_parent_info[0] == clone_parent_info[0] + assert clone2_parent_info[1] == clone_parent_info[1] + assert clone2_parent_info[2] == 'snap2' + assert clone_parent_info[2] == 'snap1' + + self.image.remove_snap('snap2') + trash_snap = self.image.snap_get_trash_namespace(snap_id) + assert trash_snap == { + 'original_name' : 'snap2' + } + clone_name3 = get_temp_image_name() + assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name, + snap_id, ioctx, clone_name3, features, clone_format=1) + assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name, + snap_id, ioctx, clone_name3, features, clone_format=2) + self.rbd.remove(ioctx, clone_name2) + assert_raises(ImageNotFound, self.image.snap_get_trash_namespace, + snap_id) + def test_stat(self): image_info = self.image.stat() clone_info = self.clone.stat() @@ -2820,7 +2883,7 @@ class TestGroups(object): eq([snap_name], [snap['name'] for snap in self.group.list_snaps()]) for snap in self.image.list_snaps(): - eq(rbd.RBD_SNAP_NAMESPACE_TYPE_GROUP, snap['namespace']) + eq(RBD_SNAP_NAMESPACE_TYPE_GROUP, snap['namespace']) info = snap['group'] eq(group_name, info['group_name']) eq(snap_name, info['group_snap_name']) @@ -2885,6 +2948,107 @@ class TestGroups(object): self.group.remove_snap(new_snap_name) eq([], list(self.group.list_snaps())) + @require_features([RBD_FEATURE_LAYERING]) + def test_group_snap_clone(self): + data = rand_data(256) + with Image(ioctx, image_name) as image: + image.write(data, 0) + + self.group.add_image(ioctx, image_name) + self.group.create_snap(snap_name) + assert [s['name'] for s in self.group.list_snaps()] == [snap_name] + image_snaps = list(self.image.list_snaps()) + assert [s['namespace'] for s in image_snaps] == [RBD_SNAP_NAMESPACE_TYPE_GROUP] + image_snap_name = image_snaps[0]['name'] + image_snap_id = image_snaps[0]['id'] + assert image_snaps[0]['group'] == { + 'pool' : ioctx.get_pool_id(), + 'name' : group_name, + 'snap_name' : snap_name, + } + + clone_name = get_temp_image_name() + assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name, + image_snap_name, ioctx, clone_name, features, clone_format=1) + assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name, + image_snap_id, ioctx, clone_name, features, clone_format=1) + assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name, + image_snap_name, ioctx, clone_name, features, clone_format=2) + self.rbd.clone(ioctx, image_name, image_snap_id, ioctx, clone_name, + features, clone_format=2) + with Image(ioctx, clone_name) as clone: + parent_spec = clone.get_parent_image_spec() + assert parent_spec['pool_name'] == pool_name + assert parent_spec['image_name'] == image_name + assert parent_spec['snap_namespace_type'] == RBD_SNAP_NAMESPACE_TYPE_GROUP + assert parent_spec['snap_name'] == image_snap_name + assert parent_spec['snap_id'] == image_snap_id + read = clone.read(0, 256) + assert read == data + + self.group.remove_snap(snap_name) + assert list(self.group.list_snaps()) == [] + image_snaps = list(self.image.list_snaps()) + assert [s['namespace'] for s in image_snaps] == [RBD_SNAP_NAMESPACE_TYPE_TRASH] + trash_image_snap_name = image_snaps[0]['name'] + assert image_snaps[0]['id'] == image_snap_id + assert image_snaps[0]['trash'] == { + 'original_name' : image_snap_name + } + assert trash_image_snap_name != image_snap_name + + with Image(ioctx, clone_name) as clone: + parent_spec = clone.get_parent_image_spec() + assert parent_spec['pool_name'] == pool_name + assert parent_spec['image_name'] == image_name + assert parent_spec['snap_namespace_type'] == RBD_SNAP_NAMESPACE_TYPE_TRASH + assert parent_spec['snap_name'] == trash_image_snap_name + assert parent_spec['snap_id'] == image_snap_id + read = clone.read(0, 256) + assert read == data + + self.rbd.remove(ioctx, clone_name) + assert list(self.image.list_snaps()) == [] + + @require_features([RBD_FEATURE_LAYERING]) + def test_group_snap_clone_flatten(self): + data = rand_data(256) + with Image(ioctx, image_name) as image: + image.write(data, 0) + + self.group.add_image(ioctx, image_name) + self.group.create_snap(snap_name) + assert [s['name'] for s in self.group.list_snaps()] == [snap_name] + image_snaps = list(self.image.list_snaps()) + assert [s['namespace'] for s in image_snaps] == [RBD_SNAP_NAMESPACE_TYPE_GROUP] + image_snap_id = image_snaps[0]['id'] + + clone_name = get_temp_image_name() + self.rbd.clone(ioctx, image_name, image_snap_id, ioctx, clone_name, + features, clone_format=2) + self.group.remove_snap(snap_name) + assert list(self.group.list_snaps()) == [] + image_snaps = list(self.image.list_snaps()) + assert [s['namespace'] for s in image_snaps] == [RBD_SNAP_NAMESPACE_TYPE_TRASH] + assert image_snaps[0]['id'] == image_snap_id + + with Image(ioctx, clone_name) as clone: + parent_spec = clone.get_parent_image_spec() + assert parent_spec['pool_id'] == ioctx.get_pool_id() + assert parent_spec['image_id'] == self.image.id() + assert parent_spec['snap_id'] == image_snap_id + read = clone.read(0, 256) + assert read == data + clone.flatten() + + assert list(self.image.list_snaps()) == [] + with Image(ioctx, clone_name) as clone: + assert_raises(ImageNotFound, clone.get_parent_image_spec) + read = clone.read(0, 256) + assert read == data + + self.rbd.remove(ioctx, clone_name) + def test_group_snap_rollback(self): eq([], list(self.group.list_images())) self.group.add_image(ioctx, image_name) diff --git a/src/test/rbd_mirror/test_ImageDeleter.cc b/src/test/rbd_mirror/test_ImageDeleter.cc index 5fa5d6db512..6b5993591fd 100644 --- a/src/test/rbd_mirror/test_ImageDeleter.cc +++ b/src/test/rbd_mirror/test_ImageDeleter.cc @@ -202,7 +202,7 @@ public: librbd::ImageOptions clone_opts; clone_opts.set(RBD_IMAGE_OPTION_FEATURES, ictx->features); EXPECT_EQ(0, librbd::clone(m_local_io_ctx, m_local_image_id.c_str(), - nullptr, "snap1", m_local_io_ctx, + nullptr, CEPH_NOSNAP, "snap1", m_local_io_ctx, clone_id.c_str(), "clone1", clone_opts, GLOBAL_CLONE_IMAGE_ID, m_remote_mirror_uuid)); diff --git a/src/tracing/librbd.tp b/src/tracing/librbd.tp index b2624d5b184..5b7f2b31ff3 100644 --- a/src/tracing/librbd.tp +++ b/src/tracing/librbd.tp @@ -1481,6 +1481,36 @@ TRACEPOINT_EVENT(librbd, clone3_exit, ) ) +TRACEPOINT_EVENT(librbd, clone4_enter, + TP_ARGS( + const char*, parent_pool_name, + uint64_t, parent_pool_id, + const char*, parent_name, + uint64_t, parent_snap_id, + const char*, child_pool_name, + uint64_t, child_pool_id, + const char*, child_name, + void*, opts), + TP_FIELDS( + ctf_string(parent_pool_name, parent_pool_name) + ctf_integer(uint64_t, parent_pool_id, parent_pool_id) + ctf_string(parent_name, parent_name) + ctf_integer(uint64_t, parent_snap_id, parent_snap_id) + ctf_string(child_pool_name, child_pool_name) + ctf_integer(uint64_t, child_pool_id, child_pool_id) + ctf_string(child_name, child_name) + ctf_integer_hex(void*, opts, opts) + ) +) + +TRACEPOINT_EVENT(librbd, clone4_exit, + TP_ARGS( + int, retval), + TP_FIELDS( + ctf_integer(int, retval, retval) + ) +) + TRACEPOINT_EVENT(librbd, flatten_enter, TP_ARGS( void*, imagectx, -- 2.39.5