From 55c1754c2db7d48cc2a4988f00f53f999bc2def4 Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Fri, 8 Feb 2019 17:53:59 +0000 Subject: [PATCH] librbd: add list_descendants API method Signed-off-by: Mykola Golub --- src/include/rbd/librbd.h | 4 ++ src/include/rbd/librbd.hpp | 1 + src/librbd/api/Image.cc | 79 ++++++++++++++++++++++++++++++++-- src/librbd/api/Image.h | 11 +++++ src/librbd/librbd.cc | 40 +++++++++++++++++ src/pybind/rbd/rbd.pyx | 27 +++++++++--- src/test/librbd/test_librbd.cc | 35 +++++++++++++++ src/test/pybind/test_rbd.py | 16 +++++++ 8 files changed, 204 insertions(+), 9 deletions(-) diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index bd2b2a4a391..c872eb67630 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -780,6 +780,10 @@ CEPH_RBD_API int rbd_list_children3(rbd_image_t image, 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 * diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index 42c8c1927b2..4aaffd116f6 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -485,6 +485,7 @@ public: int list_children2(std::vector *children) __attribute__((deprecated)); int list_children3(std::vector *images); + int list_descendants(std::vector *images); /* advisory locking (see librbd.h for details) */ int list_lockers(std::list *lockers, diff --git a/src/librbd/api/Image.cc b/src/librbd/api/Image.cc index 94154e95795..8c3b63d2eaa 100644 --- a/src/librbd/api/Image.cc +++ b/src/librbd/api/Image.cc @@ -234,7 +234,51 @@ template int Image::list_children(I *ictx, std::vector *images) { images->clear(); + return list_descendants(ictx, 1, images); +} + +template +int Image::list_children(I *ictx, + const cls::rbd::ParentImageSpec &parent_spec, + std::vector *images) { + images->clear(); + return list_descendants(ictx, parent_spec, 1, images); +} + +template +int Image::list_descendants( + librados::IoCtx& io_ctx, const std::string &image_id, + const std::optional &max_level, + std::vector *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 +int Image::list_descendants( + I *ictx, const std::optional &max_level, + std::vector *images) { RWLock::RLocker l(ictx->snap_lock); std::vector snap_ids; if (ictx->snap_id != CEPH_NOSNAP) { @@ -246,7 +290,7 @@ int Image::list_children(I *ictx, 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; } @@ -255,9 +299,17 @@ int Image::list_children(I *ictx, } template -int Image::list_children(I *ictx, - const cls::rbd::ParentImageSpec &parent_spec, - std::vector *images) { +int Image::list_descendants( + I *ictx, const cls::rbd::ParentImageSpec &parent_spec, + const std::optional &max_level, + std::vector *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; @@ -312,6 +364,10 @@ int Image::list_children(I *ictx, 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; + } } } @@ -336,6 +392,21 @@ int Image::list_children(I *ictx, 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 diff --git a/src/librbd/api/Image.h b/src/librbd/api/Image.h index 8c8ddbea27f..73687da37c0 100644 --- a/src/librbd/api/Image.h +++ b/src/librbd/api/Image.h @@ -42,6 +42,17 @@ struct Image { const cls::rbd::ParentImageSpec &parent_spec, std::vector *images); + static int list_descendants(IoCtx& io_ctx, const std::string &image_id, + const std::optional &max_level, + std::vector *images); + static int list_descendants(ImageCtxT *ictx, + const std::optional &max_level, + std::vector *images); + static int list_descendants(ImageCtxT *ictx, + const cls::rbd::ParentImageSpec &parent_spec, + const std::optional &max_level, + std::vector *images); + static int deep_copy(ImageCtxT *ictx, librados::IoCtx& dest_md_ctx, const char *destname, ImageOptions& opts, ProgressContext &prog_ctx); diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 033919f92a2..46c10381f81 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -1807,6 +1807,15 @@ namespace librbd { return r; } + int Image::list_descendants(std::vector *images) + { + auto ictx = reinterpret_cast(ctx); + + images->clear(); + int r = librbd::api::Image<>::list_descendants(ictx, {}, images); + return r; + } + int Image::list_lockers(std::list *lockers, bool *exclusive, string *tag) { @@ -4850,6 +4859,37 @@ extern "C" int rbd_list_children3(rbd_image_t image, 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(image); + memset(images, 0, sizeof(*images) * *max_images); + + std::vector 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, diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index 0c5fb3335f9..9688f24ae0f 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -456,6 +456,9 @@ cdef extern from "rbd/librbd.h" nogil: 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, @@ -3774,12 +3777,20 @@ written." % (self.name, ret, length)) 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 @@ -4694,7 +4705,7 @@ cdef class TrashIterator(object): 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. @@ -4715,15 +4726,21 @@ cdef class ChildIterator(object): 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 = 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: diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index 04af0fc2fa1..c133292aa65 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -7729,6 +7729,41 @@ TEST_F(TestLibRBD, ImageSpec) { .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() diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index bafd077af22..718bf4e786b 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -1348,6 +1348,9 @@ class TestClone(object): 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() @@ -1361,10 +1364,15 @@ class TestClone(object): [{'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 = [] @@ -1379,6 +1387,7 @@ class TestClone(object): '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) @@ -1389,6 +1398,7 @@ class TestClone(object): 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))) @@ -1398,6 +1408,7 @@ class TestClone(object): 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)) @@ -1405,6 +1416,7 @@ class TestClone(object): 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()), []) @@ -1415,6 +1427,10 @@ class TestClone(object): [{'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): -- 2.39.5