]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: add list_descendants API method
authorMykola Golub <mgolub@suse.com>
Fri, 8 Feb 2019 17:53:59 +0000 (17:53 +0000)
committerMykola Golub <mgolub@suse.com>
Tue, 19 Feb 2019 08:43:09 +0000 (08:43 +0000)
Signed-off-by: Mykola Golub <mgolub@suse.com>
src/include/rbd/librbd.h
src/include/rbd/librbd.hpp
src/librbd/api/Image.cc
src/librbd/api/Image.h
src/librbd/librbd.cc
src/pybind/rbd/rbd.pyx
src/test/librbd/test_librbd.cc
src/test/pybind/test_rbd.py

index bd2b2a4a391ac38408546eb4a31d79ea5854e545..c872eb67630fe93f27b2f560474be99e6744894b 100644 (file)
@@ -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
  *
index 42c8c1927b2fa9c07a1463623987e9edff1877d4..4aaffd116f6f3f63d96c5a273bb92681d006a1c6 100644 (file)
@@ -485,6 +485,7 @@ public:
   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,
index 94154e9579546d0a9ef3d215c6921812d72a6ddf..8c3b63d2eaaa4ca987d6158ba5726b0ca570da55 100644 (file)
@@ -234,7 +234,51 @@ template <typename I>
 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) {
@@ -246,7 +290,7 @@ int Image<I>::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<I>::list_children(I *ictx,
 }
 
 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;
 
@@ -312,6 +364,10 @@ int Image<I>::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<I>::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
index 8c8ddbea27f93244cb8672121393b155b9565123..73687da37c02bf7c362b2124ea18c8997f83f921 100644 (file)
@@ -42,6 +42,17 @@ struct Image {
                            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);
index 033919f92a2a64c9a6967b35f80ec4bcffa29562..46c10381f81a33711f59fc27e6515e96c095f996 100644 (file)
@@ -1807,6 +1807,15 @@ namespace librbd {
     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)
   {
@@ -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<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,
index 0c5fb3335f9955f4ce50ef0fa0871bea04ed5228..9688f24ae0ffaef7e7869c71b05e2cc1a05186f0 100644 (file)
@@ -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 = <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:
index 04af0fc2fa18dd0abe0fe536dc89108a8c3a656c..c133292aa65add1366df4b9cbd6af89889bf886c 100644 (file)
@@ -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()
index bafd077af2296f9ea3f459829b4e1f1a38122384..718bf4e786b374bb533619b6d92551bf119eb783 100644 (file)
@@ -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):