]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: add {rbd_}list_children() methods
authorDan Mick <dan.mick@inktank.com>
Tue, 21 Aug 2012 22:58:21 +0000 (15:58 -0700)
committerJosh Durgin <josh.durgin@inktank.com>
Thu, 30 Aug 2012 00:35:27 +0000 (17:35 -0700)
These iterate over all pools and check for children of a
particular snapshot.

Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
Reviewed-by: Dan Mick <dan.mick@inktank.com>
src/include/rbd/librbd.h
src/include/rbd/librbd.hpp
src/librbd/internal.cc
src/librbd/internal.h
src/librbd/librbd.cc
src/pybind/rbd.py
src/test/pybind/test_rbd.py
src/test/test_librbd.cc

index e237b7ff7ebf6a61aca873ed5775ff9640a1ae79..77ca1e3af32bc55ed003f7c3ea03eacf48d25b78 100644 (file)
@@ -133,6 +133,30 @@ int rbd_snap_set(rbd_image_t image, const char *snapname);
 
 int rbd_flatten(rbd_image_t image);
 
+/**
+ * List all images that are cloned from the image at the
+ * snapshot that is set via rbd_snap_set().
+ *
+ * This iterates over all pools, so it should be run by a user with
+ * read access to all of them. pools_len and images_len are filled in
+ * with the number of bytes put into the pools and images buffers.
+ *
+ * If the provided buffers are too short, the required lengths are
+ * still filled in, but the data is not and -ERANGE is returned.
+ * Otherwise, the buffers are filled with the pool and image names
+ * of the children, with a '\0' after each.
+ *
+ * @param image which image (and implicitly snapshot) to list clones of
+ * @param pools buffer in which to store pool names
+ * @param pools_len number of bytes in pools buffer
+ * @param images buffer in which to store image names
+ * @param images_len number of bytes in images buffer
+ * @returns number of children on success, negative error code on failure
+ * @returns -ERANGE if either buffer is too short
+ */
+ssize_t rbd_list_children(rbd_image_t image, char *pools, size_t *pools_len,
+                         char *images, size_t *images_len);
+
 /* cooperative locking */
 /**
  * in params:
index fbfbcc0ee3f57da2cee50f1956e62ddf6d6be7b5..1608e693850cfccb627acb235fc077c531448b69 100644 (file)
@@ -104,6 +104,12 @@ public:
 
   int flatten();
   int flatten_with_progress(ProgressContext &prog_ctx);
+  /**
+   * Returns a pair of poolname, imagename for each clone
+   * of this image at the currently set snapshot.
+   */
+  int list_children(std::set<std::pair<std::string, std::string> > *children);
+
   /* cooperative locking */
   int list_locks(std::set<std::pair<std::string, std::string> > &locks,
                  bool &exclusive);
index 386ec36e60a64281a4a1dbec5e471c5838a8dff3..4d8b0a4ba463af2151c328c980b60172599149d9 100644 (file)
@@ -26,6 +26,7 @@ using std::pair;
 using std::set;
 using std::string;
 using std::vector;
+// list binds to list() here, so std::list is explicitly used below
 
 using ceph::bufferlist;
 using librados::snap_t;
@@ -378,6 +379,57 @@ namespace librbd {
     return 0;
   }
 
+  int list_children(ImageCtx *ictx, set<pair<string, string> >& names)
+  {
+    CephContext *cct = ictx->cct;
+    ldout(cct, 20) << "children list " << ictx->name << dendl;
+
+    int r = ictx_check(ictx);
+    if (r < 0)
+      return r;
+
+    // no children for non-layered or old format image
+    if (!ictx->features & RBD_FEATURE_LAYERING)
+      return 0;
+
+    parent_spec parent_spec(ictx->md_ctx.get_id(), ictx->id, ictx->snap_id);
+    names.clear();
+
+    // search all pools for children depending on this snapshot
+    Rados rados(ictx->md_ctx);
+    std::list<string> pools;
+    rados.pool_list(pools);
+
+    for (std::list<string>::const_iterator it = pools.begin();
+        it != pools.end(); ++it) {
+      IoCtx ioctx;
+      rados.ioctx_create(it->c_str(), ioctx);
+      set<string> image_ids;
+      int r = cls_client::get_children(&ioctx, RBD_CHILDREN,
+                                      parent_spec, image_ids);
+      if (r < 0 && r != -ENOENT) {
+       lderr(cct) << "Error reading list of children from pool " << *it
+                  << dendl;
+       return r;
+      }
+
+      for (set<string>::const_iterator id_it = image_ids.begin();
+          id_it != image_ids.end(); ++id_it) {
+       string name;
+       r = cls_client::dir_get_name(&ioctx, RBD_DIRECTORY,
+                                    *id_it, &name);
+       if (r < 0) {
+         lderr(cct) << "Error looking up name for image id " << *id_it
+                    << " in pool " << *it << dendl;
+         return r;
+       }
+       names.insert(make_pair(*it, name));
+      }
+    }
+
+    return 0;
+  }
+
   int snap_create(ImageCtx *ictx, const char *snap_name)
   {
     ldout(ictx->cct, 20) << "snap_create " << ictx << " " << snap_name << dendl;
index 4f6adcfeffaf2394e59632e55abf4e72c9eb6a50..2421e0bc6624ae4ba4b906eb5d84d643e910c534 100644 (file)
@@ -76,6 +76,8 @@ namespace librbd {
 
   int snap_set(ImageCtx *ictx, const char *snap_name);
   int list(librados::IoCtx& io_ctx, std::vector<std::string>& names);
+  int list_children(ImageCtx *ictx,
+                   std::set<pair<std::string, std::string> > & names);
   int create(librados::IoCtx& io_ctx, const char *imgname, uint64_t size,
             bool old_format, uint64_t features, int *order);
   int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snap_name,
index 8aefe1e9050221fc787c6326dd5b42ff57a20277..fab565b4c9fa1cd82cda21cfb81de4aa3c2720d0 100644 (file)
@@ -266,6 +266,12 @@ namespace librbd {
     return librbd::flatten(ictx, prog_ctx);
   }
 
+  int Image::list_children(set<pair<string, string> > *children)
+  {
+    ImageCtx *ictx = (ImageCtx *)ctx;
+    return librbd::list_children(ictx, *children);
+  }
+
   int Image::list_locks(set<pair<string, string> > &locks,
                        bool &exclusive)
   {
@@ -736,6 +742,48 @@ extern "C" int rbd_snap_set(rbd_image_t image, const char *snap_name)
   return librbd::snap_set(ictx, snap_name);
 }
 
+extern "C" ssize_t rbd_list_children(rbd_image_t image, char *pools,
+                                    size_t *pools_len, char *images,
+                                    size_t *images_len)
+{
+  librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
+  set<pair<string, string> > image_set;
+
+  int r = librbd::list_children(ictx, image_set);
+  if (r < 0)
+    return r;
+
+  size_t pools_total = 0;
+  size_t images_total = 0;
+  for (set<pair<string, string> >::const_iterator it = image_set.begin();
+       it != image_set.end(); ++it) {
+    pools_total += it->first.length() + 1;
+    images_total += it->second.length() + 1;
+  }
+
+  bool too_short = false;
+  if (pools_total > *pools_len)
+    too_short = true;
+  if (images_total > *images_len)
+    too_short = true;
+  *pools_len = pools_total;
+  *images_len = images_total;
+  if (too_short)
+    return -ERANGE;
+
+  char *pools_p = pools;
+  char *images_p = images;
+  for (set<pair<string, string> >::const_iterator it = image_set.begin();
+       it != image_set.end(); ++it) {
+    strcpy(pools_p, it->first.c_str());
+    pools_p += it->first.length() + 1;
+    strcpy(images_p, it->second.c_str());
+    images_p += it->second.length() + 1;
+  }
+
+  return image_set.size();
+}
+
 extern "C" int rbd_list_lockers(rbd_image_t image, int *exclusive,
                                 char **lockers_and_cookies, int *max_entries)
 {
index 98524d3cb14ff84186970a738ae5b900091e53c9..2bb911dafb33ddacb0708ca42ea083cae731354b 100644 (file)
@@ -620,6 +620,33 @@ written." % (self.name, ret, length))
         if (ret < 0):
             raise make_ex(ret, "error flattening %s" % self.name)
 
+    def list_children(self):
+        """
+        List children of the currently set snapshot (set via set_snap()).
+
+        :returns: list - a list of (pool name, image name) tuples
+        """
+        pools_size = c_size_t(512)
+        images_size = c_size_t(512)
+        while True:
+            c_pools = create_string_buffer(pools_size.value)
+            c_images = create_string_buffer(images_size.value)
+            ret = self.librbd.rbd_list_children(self.image,
+                                                byref(c_pools),
+                                                byref(pools_size),
+                                                byref(c_images),
+                                                byref(images_size))
+            if ret >= 0:
+                break
+            elif ret != -errno.ERANGE:
+                raise make_ex(ret, 'error listing images')
+        if ret == 0:
+            return []
+        pools = c_pools.raw[:pools_size.value - 1].split('\0')
+        images = c_images.raw[:images_size.value - 1].split('\0')
+        return zip(pools, images)
+
+
 class SnapIterator(object):
     """
     Iterator over snapshot info for an image.
index 7e15b6f7d8ae2baff746005867e9c4025197ac41..d898d8e7374dc7f8f10ab103a042fe70757a2e1f 100644 (file)
@@ -520,6 +520,29 @@ class TestClone(object):
         parent_data = self.image.read(IMG_SIZE / 2 + 256, 256)
         eq(parent_data, '\0' * 256)
 
+    def test_list_children(self):
+        global ioctx
+        global features
+        self.image.set_snap('snap1')
+        eq(self.image.list_children(), [('rbd', 'clone')])
+        self.rbd.remove(ioctx, 'clone')
+        eq(self.image.list_children(), [])
+
+        expected_children = []
+        for i in xrange(10):
+            self.rbd.clone(ioctx, IMG_NAME, 'snap1', ioctx, 'clone%d' % i, features)
+            expected_children.append(('rbd', 'clone%d' % i))
+            eq(self.image.list_children(), expected_children)
+
+        for i in xrange(10):
+            self.rbd.remove(ioctx, 'clone%d' % i)
+            expected_children.pop(0)
+            eq(self.image.list_children(), expected_children)
+
+        eq(self.image.list_children(), [])
+        self.rbd.clone(ioctx, IMG_NAME, 'snap1', ioctx, 'clone', features)
+        eq(self.image.list_children(), [('rbd', 'clone')])
+
 class TestFlatten(TestClone):
 
     def test_errors(self):
index c6ba3f8277c8f5527df4704495b86c0c3d7313a2..023d39c1055e0a98256799848cfc395b856b3321 100644 (file)
@@ -1104,3 +1104,128 @@ TEST(LibRBD, TestClone)
   rados_ioctx_destroy(ioctx);
   ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
 }
+
+static void test_list_children(rbd_image_t image, ssize_t num_expected, ...)
+{
+  va_list ap;
+  va_start(ap, num_expected);
+  size_t pools_len = 100;
+  size_t children_len = 100;
+  char *pools = NULL;
+  char *children = NULL;
+  ssize_t num_children;
+
+  do {
+    free(pools);
+    free(children);
+    pools = (char *) malloc(pools_len);
+    children = (char *) malloc(children_len);
+    num_children = rbd_list_children(image, pools, &pools_len,
+                                    children, &children_len);
+  } while (num_children == -ERANGE);
+
+  ASSERT_EQ(num_expected, num_children);
+  for (ssize_t i = num_expected; i > 0; --i) {
+    char *expected_pool = va_arg(ap, char *);
+    char *expected_image = va_arg(ap, char *);
+    char *pool = pools;
+    char *image = children;
+    bool found = 0;
+    printf("\ntrying to find %s/%s\n", expected_pool, expected_image);
+    for (ssize_t j = 0; j < num_children; ++j) {
+      printf("checking %s/%s\n", pool, image);
+      if (strcmp(expected_pool, pool) == 0 &&
+         strcmp(expected_image, image) == 0) {
+       printf("found child %s/%s\n\n", pool, image);
+       found = 1;
+       break;
+      }
+      pool += strlen(pool) + 1;
+      image += strlen(image) + 1;
+      if (j == num_children - 1) {
+       ASSERT_EQ(pool - pools - 1, (ssize_t) pools_len);
+       ASSERT_EQ(image - children - 1, (ssize_t) children_len);
+      }
+    }
+    ASSERT_TRUE(found);
+  }
+  va_end(ap);
+}
+
+TEST(LibRBD, ListChildren)
+{
+  rados_t cluster;
+  rados_ioctx_t ioctx1, ioctx2;
+  string pool_name1 = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool(pool_name1, &cluster));
+  string pool_name2 = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool(pool_name2, &cluster));
+  rados_ioctx_create(cluster, pool_name1.c_str(), &ioctx1);
+  rados_ioctx_create(cluster, pool_name2.c_str(), &ioctx2);
+
+  int features = RBD_FEATURE_LAYERING;
+  rbd_image_t parent;
+  int order = 0;
+
+  // make a parent to clone from
+  ASSERT_EQ(0, create_image_full(ioctx1, "parent", 4<<20, &order,
+                                false, features));
+  ASSERT_EQ(0, rbd_open(ioctx1, "parent", &parent, NULL));
+  // create a snapshot, reopen as the parent we're interested in
+  ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
+  ASSERT_EQ(0, rbd_snap_set(parent, "parent_snap"));
+  ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap"));
+
+  ASSERT_EQ(0, rbd_close(parent));
+  ASSERT_EQ(0, rbd_open(ioctx1, "parent", &parent, "parent_snap"));
+
+  ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx2, "child1",
+           features, &order));
+  test_list_children(parent, 1, pool_name2.c_str(), "child1");
+
+  ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx1, "child2",
+           features, &order));
+  test_list_children(parent, 2, pool_name2.c_str(), "child1",
+                    pool_name1.c_str(), "child2");
+
+  ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx2, "child3",
+           features, &order));
+  test_list_children(parent, 3, pool_name2.c_str(), "child1",
+                    pool_name1.c_str(), "child2",
+                    pool_name2.c_str(), "child3");
+
+  ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx2, "child4",
+           features, &order));
+  test_list_children(parent, 4, pool_name2.c_str(), "child1",
+                    pool_name1.c_str(), "child2",
+                    pool_name2.c_str(), "child3",
+                    pool_name2.c_str(), "child4");
+
+  ASSERT_EQ(0, rbd_remove(ioctx2, "child1"));
+  test_list_children(parent, 3,
+                    pool_name1.c_str(), "child2",
+                    pool_name2.c_str(), "child3",
+                    pool_name2.c_str(), "child4");
+
+  ASSERT_EQ(0, rbd_remove(ioctx2, "child3"));
+  test_list_children(parent, 2,
+                    pool_name1.c_str(), "child2",
+                    pool_name2.c_str(), "child4");
+
+  ASSERT_EQ(0, rbd_remove(ioctx2, "child4"));
+  test_list_children(parent, 1,
+                    pool_name1.c_str(), "child2");
+
+  ASSERT_EQ(0, rbd_remove(ioctx1, "child2"));
+  test_list_children(parent, 0);
+
+  ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap"));
+  ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap"));
+  ASSERT_EQ(0, rbd_close(parent));
+  ASSERT_EQ(0, rbd_remove(ioctx1, "parent"));
+  rados_ioctx_destroy(ioctx1);
+  rados_ioctx_destroy(ioctx2);
+  // destroy_one_pool also closes the cluster; do this one step at a time
+  ASSERT_EQ(0, rados_pool_delete(cluster, pool_name1.c_str()));
+  ASSERT_EQ(0, destroy_one_pool(pool_name2, &cluster));
+}