mirror of
https://github.com/ceph/ceph
synced 2025-03-21 17:57:38 +00:00
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 <idryomov@gmail.com>
This commit is contained in:
parent
533943d08f
commit
d7fd66ec99
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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<CephContext *>(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);
|
||||
|
@ -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);
|
||||
|
@ -778,12 +778,26 @@ namespace librbd {
|
||||
{
|
||||
TracepointProvider::initialize<tracepoint_traits>(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<tracepoint_traits>(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<tracepoint_traits>(get_cct(io_ctx));
|
||||
@ -3978,12 +3992,30 @@ extern "C" int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name,
|
||||
TracepointProvider::initialize<tracepoint_traits>(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<tracepoint_traits>(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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user