rbd_linked_image_spec_t *images,
size_t *max_images);
+CEPH_RBD_API int rbd_list_descendants(rbd_image_t image,
+ rbd_linked_image_spec_t *images,
+ size_t *max_images);
+
/**
* @defgroup librbd_h_locking Advisory Locking
*
int list_children2(std::vector<librbd::child_info_t> *children)
__attribute__((deprecated));
int list_children3(std::vector<linked_image_spec_t> *images);
+ int list_descendants(std::vector<linked_image_spec_t> *images);
/* advisory locking (see librbd.h for details) */
int list_lockers(std::list<locker_t> *lockers,
int Image<I>::list_children(I *ictx,
std::vector<librbd::linked_image_spec_t> *images) {
images->clear();
+ return list_descendants(ictx, 1, images);
+}
+
+template <typename I>
+int Image<I>::list_children(I *ictx,
+ const cls::rbd::ParentImageSpec &parent_spec,
+ std::vector<librbd::linked_image_spec_t> *images) {
+ images->clear();
+ return list_descendants(ictx, parent_spec, 1, images);
+}
+
+template <typename I>
+int Image<I>::list_descendants(
+ librados::IoCtx& io_ctx, const std::string &image_id,
+ const std::optional<size_t> &max_level,
+ std::vector<librbd::linked_image_spec_t> *images) {
+ ImageCtx *ictx = new librbd::ImageCtx("", image_id, nullptr,
+ io_ctx, true);
+ int r = ictx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT);
+ if (r < 0) {
+ if (r == -ENOENT) {
+ return 0;
+ }
+ lderr(ictx->cct) << "failed to open descendant " << image_id
+ << " from pool " << io_ctx.get_pool_name() << ":"
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ r = list_descendants(ictx, max_level, images);
+
+ int r1 = ictx->state->close();
+ if (r1 < 0) {
+ lderr(ictx->cct) << "error when closing descendant " << image_id
+ << " from pool " << io_ctx.get_pool_name() << ":"
+ << cpp_strerror(r) << dendl;
+ }
+ return r;
+}
+
+template <typename I>
+int Image<I>::list_descendants(
+ I *ictx, const std::optional<size_t> &max_level,
+ std::vector<librbd::linked_image_spec_t> *images) {
RWLock::RLocker l(ictx->snap_lock);
std::vector<librados::snap_t> snap_ids;
if (ictx->snap_id != CEPH_NOSNAP) {
cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
ictx->md_ctx.get_namespace(),
ictx->id, snap_id};
- int r = list_children(ictx, parent_spec, images);
+ int r = list_descendants(ictx, parent_spec, max_level, images);
if (r < 0) {
return r;
}
}
template <typename I>
-int Image<I>::list_children(I *ictx,
- const cls::rbd::ParentImageSpec &parent_spec,
- std::vector<librbd::linked_image_spec_t> *images) {
+int Image<I>::list_descendants(
+ I *ictx, const cls::rbd::ParentImageSpec &parent_spec,
+ const std::optional<size_t> &max_level,
+ std::vector<librbd::linked_image_spec_t> *images) {
+ auto child_max_level = max_level;
+ if (child_max_level) {
+ if (child_max_level == 0) {
+ return 0;
+ }
+ (*child_max_level)--;
+ }
CephContext *cct = ictx->cct;
ldout(cct, 20) << "ictx=" << ictx << dendl;
for (auto& image_id : image_ids) {
images->push_back({
it.first, "", ictx->md_ctx.get_namespace(), image_id, "", false});
+ r = list_descendants(ictx->md_ctx, image_id, child_max_level, images);
+ if (r < 0) {
+ return r;
+ }
}
}
images->push_back({
child_image.pool_id, "", child_image.pool_namespace,
child_image.image_id, "", false});
+ if (!child_max_level || *child_max_level > 0) {
+ IoCtx ioctx;
+ r = util::create_ioctx(ictx->md_ctx, "child image", child_image.pool_id,
+ child_image.pool_namespace, &ioctx);
+ if (r == -ENOENT) {
+ continue;
+ } else if (r < 0) {
+ return r;
+ }
+ r = list_descendants(ioctx, child_image.image_id, child_max_level,
+ images);
+ if (r < 0) {
+ return r;
+ }
+ }
}
// batch lookups by pool + namespace
const cls::rbd::ParentImageSpec &parent_spec,
std::vector<librbd::linked_image_spec_t> *images);
+ static int list_descendants(IoCtx& io_ctx, const std::string &image_id,
+ const std::optional<size_t> &max_level,
+ std::vector<librbd::linked_image_spec_t> *images);
+ static int list_descendants(ImageCtxT *ictx,
+ const std::optional<size_t> &max_level,
+ std::vector<librbd::linked_image_spec_t> *images);
+ static int list_descendants(ImageCtxT *ictx,
+ const cls::rbd::ParentImageSpec &parent_spec,
+ const std::optional<size_t> &max_level,
+ std::vector<librbd::linked_image_spec_t> *images);
+
static int deep_copy(ImageCtxT *ictx, librados::IoCtx& dest_md_ctx,
const char *destname, ImageOptions& opts,
ProgressContext &prog_ctx);
return r;
}
+ int Image::list_descendants(std::vector<linked_image_spec_t> *images)
+ {
+ auto ictx = reinterpret_cast<ImageCtx*>(ctx);
+
+ images->clear();
+ int r = librbd::api::Image<>::list_descendants(ictx, {}, images);
+ return r;
+ }
+
int Image::list_lockers(std::list<librbd::locker_t> *lockers,
bool *exclusive, string *tag)
{
return 0;
}
+extern "C" int rbd_list_descendants(rbd_image_t image,
+ rbd_linked_image_spec_t *images,
+ size_t *max_images)
+{
+ auto ictx = reinterpret_cast<librbd::ImageCtx*>(image);
+ memset(images, 0, sizeof(*images) * *max_images);
+
+ std::vector<librbd::linked_image_spec_t> cpp_children;
+ int r = librbd::api::Image<>::list_descendants(ictx, {}, &cpp_children);
+ if (r < 0) {
+ return r;
+ }
+
+ if (*max_images < cpp_children.size()) {
+ *max_images = cpp_children.size();
+ return -ERANGE;
+ }
+
+ *max_images = cpp_children.size();
+ for (size_t idx = 0; idx < cpp_children.size(); ++idx) {
+ images[idx] = {
+ .pool_id = cpp_children[idx].pool_id,
+ .pool_name = strdup(cpp_children[idx].pool_name.c_str()),
+ .pool_namespace = strdup(cpp_children[idx].pool_namespace.c_str()),
+ .image_id = strdup(cpp_children[idx].image_id.c_str()),
+ .image_name = strdup(cpp_children[idx].image_name.c_str()),
+ .trash = cpp_children[idx].trash};
+ }
+ return 0;
+}
+
extern "C" ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive,
char *tag, size_t *tag_len,
char *clients, size_t *clients_len,
void *cbdata)
int rbd_list_children3(rbd_image_t image, rbd_linked_image_spec_t *children,
size_t *max_children)
+ int rbd_list_descendants(rbd_image_t image,
+ rbd_linked_image_spec_t *descendants,
+ size_t *max_descendants)
ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive,
char *tag, size_t *tag_len,
def list_children2(self):
"""
- Iterate over the children of a snapshot.
+ Iterate over the children of the image or its snapshot.
:returns: :class:`ChildIterator`
"""
return ChildIterator(self)
+ def list_descendants(self):
+ """
+ Iterate over the descendants of the image.
+
+ :returns: :class:`ChildIterator`
+ """
+ return ChildIterator(self, True)
+
def list_lockers(self):
"""
List clients that have locked the image and information
cdef class ChildIterator(object):
"""
- Iterator over child info for a snapshot.
+ Iterator over child info for the image or its snapshot.
Yields a dictionary containing information about a child.
cdef size_t num_children
cdef object image
- def __init__(self, Image image):
+ def __init__(self, Image image, descendants=False):
self.image = image
self.children = NULL
self.num_children = 10
while True:
self.children = <rbd_linked_image_spec_t*>realloc_chk(
self.children, self.num_children * sizeof(rbd_linked_image_spec_t))
- with nogil:
- ret = rbd_list_children3(image.image, self.children, &self.num_children)
+ if descendants:
+ with nogil:
+ ret = rbd_list_descendants(image.image, self.children,
+ &self.num_children)
+ else:
+ with nogil:
+ ret = rbd_list_children3(image.image, self.children,
+ &self.num_children)
if ret >= 0:
break
elif ret != -errno.ERANGE:
.trash = false
}
};
+ ASSERT_EQ(expected_children, children);
+
+ children.clear();
+ ASSERT_EQ(0, parent_image.list_descendants(&children));
+ ASSERT_EQ(expected_children, children);
+
+ ASSERT_EQ(0, clone_image.snap_create("snap"));
+ ASSERT_EQ(0, clone_image.snap_protect("snap"));
+
+ auto grand_clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, clone_name.c_str(), "snap", ioctx,
+ grand_clone_name.c_str(), features, &order));
+ librbd::Image grand_clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, grand_clone_image, grand_clone_name.c_str(),
+ nullptr));
+ std::string grand_clone_id;
+ ASSERT_EQ(0, grand_clone_image.get_id(&grand_clone_id));
+
+ children.clear();
+ ASSERT_EQ(0, parent_image.list_children3(&children));
+ ASSERT_EQ(expected_children, children);
+
+ children.clear();
+ ASSERT_EQ(0, parent_image.list_descendants(&children));
+ expected_children.push_back(
+ {
+ .pool_id = ioctx.get_id(),
+ .pool_name = ioctx.get_pool_name(),
+ .pool_namespace = ioctx.get_namespace(),
+ .image_id = grand_clone_id,
+ .image_name = grand_clone_name,
+ .trash = false
+ }
+ );
+ ASSERT_EQ(expected_children, children);
}
// poorman's ceph_assert()
for x in self.image.list_children2()]
eq(actual, expected)
+ def check_descendants(self, expected):
+ eq(list(self.image.list_descendants()), expected)
+
def get_image_id(self, ioctx, name):
with Image(ioctx, name) as image:
return image.id()
[{'pool': pool_name, 'pool_namespace': '',
'image': self.clone_name, 'trash': False,
'id': self.get_image_id(ioctx, self.clone_name)}])
+ self.check_descendants(
+ [{'pool': pool_name, 'pool_namespace': '',
+ 'image': self.clone_name, 'trash': False,
+ 'id': self.get_image_id(ioctx, self.clone_name)}])
self.clone.close()
self.rbd.remove(ioctx, self.clone_name)
eq(self.image.list_children(), [])
eq(list(self.image.list_children2()), [])
+ eq(list(self.image.list_descendants()), [])
clone_name = get_temp_image_name() + '_'
expected_children = []
'id': self.get_image_id(ioctx, clone_name + str(i))})
self.check_children(expected_children)
self.check_children2(expected_children2)
+ self.check_descendants(expected_children2)
image6_id = self.get_image_id(ioctx, clone_name + str(5))
RBD().trash_move(ioctx, clone_name + str(5), 0)
item["trash"] = True
self.check_children(expected_children)
self.check_children2(expected_children2)
+ self.check_descendants(expected_children2)
RBD().trash_restore(ioctx, image6_id, clone_name + str(5))
expected_children.append((pool_name, clone_name + str(5)))
item["trash"] = False
self.check_children(expected_children)
self.check_children2(expected_children2)
+ self.check_descendants(expected_children2)
for i in range(10):
self.rbd.remove(ioctx, clone_name + str(i))
expected_children2.pop(0)
self.check_children(expected_children)
self.check_children2(expected_children2)
+ self.check_descendants(expected_children2)
eq(self.image.list_children(), [])
eq(list(self.image.list_children2()), [])
[{'pool': pool_name, 'pool_namespace': '',
'image': self.clone_name, 'trash': False,
'id': self.get_image_id(ioctx, self.clone_name)}])
+ self.check_descendants(
+ [{'pool': pool_name, 'pool_namespace': '',
+ 'image': self.clone_name, 'trash': False,
+ 'id': self.get_image_id(ioctx, self.clone_name)}])
self.clone = Image(ioctx, self.clone_name)
def test_flatten_errors(self):