]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
librbd: add new API methods and expand the rbd CLI to list trashed images
authorsongweibin <song.weibin@zte.com.cn>
Tue, 31 Oct 2017 10:25:59 +0000 (18:25 +0800)
committersongweibin <song.weibin@zte.com.cn>
Tue, 14 Nov 2017 06:17:03 +0000 (14:17 +0800)
implement librbd::RBD::list_children2 and rbd_list_children2
methods and expand the rbd CLI to list trashed images

Signed-off-by: songweibin <song.weibin@zte.com.cn>
src/include/rbd/librbd.h
src/include/rbd/librbd.hpp
src/librbd/librbd.cc
src/pybind/rbd/rbd.pyx
src/test/cli/rbd/help.t
src/test/librbd/test_librbd.cc
src/test/pybind/test_rbd.py
src/tools/rbd/action/Children.cc

index 28683851a72eb63b4dd12de27856e0cc0131bd08..c9796586e27929a5299648aaf89a9d8131d28a0e 100644 (file)
@@ -72,6 +72,13 @@ typedef struct {
   const char *name;
 } rbd_snap_info_t;
 
+typedef struct {
+  const char *pool_name;
+  const char *image_name;
+  const char *image_id;
+  bool trash;
+} rbd_child_info_t;
+
 #define RBD_MAX_IMAGE_NAME_SIZE 96
 #define RBD_MAX_BLOCK_NAME_SIZE 24
 
@@ -528,6 +535,12 @@ CEPH_RBD_API int rbd_flatten_with_progress(rbd_image_t image,
 CEPH_RBD_API ssize_t rbd_list_children(rbd_image_t image, char *pools,
                                        size_t *pools_len, char *images,
                                        size_t *images_len);
+CEPH_RBD_API int rbd_list_children2(rbd_image_t image,
+                                    rbd_child_info_t *children,
+                                    int *max_children);
+CEPH_RBD_API void rbd_list_child_cleanup(rbd_child_info_t *child);
+CEPH_RBD_API void rbd_list_children_cleanup(rbd_child_info_t *children,
+                                            size_t num_children);
 
 /**
  * @defgroup librbd_h_locking Advisory Locking
index 4539ed43d0b4a51a88402417d3578d407be8060a..b70a627cb5cdfbee18024e54251d1e159c540abf 100644 (file)
@@ -313,6 +313,11 @@ public:
    * of this image at the currently set snapshot.
    */
   int list_children(std::set<std::pair<std::string, std::string> > *children);
+  /**
+  * Returns a structure of poolname, imagename, imageid and trash flag
+  * for each clone of this image at the currently set snapshot.
+  */
+  int list_children2(std::vector<librbd::child_info_t> *children);
 
   /* advisory locking (see librbd.h for details) */
   int list_lockers(std::list<locker_t> *lockers,
index 48f5c2504f3c5f9244b1812858e9a732ef31c586..732be3ff56560eedda5ed460f30834b703c9a43e 100644 (file)
@@ -1276,6 +1276,21 @@ namespace librbd {
     return r;
   }
 
+  int Image::list_children2(vector<librbd::child_info_t> *children)
+  {
+    ImageCtx *ictx = (ImageCtx *)ctx;
+    tracepoint(librbd, list_children_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only);
+    int r = librbd::list_children(ictx, children);
+    if (r >= 0) {
+      for (std::vector<librbd::child_info_t>::iterator it = children->begin();
+           it != children->end(); ++it) {
+        tracepoint(librbd, list_children_entry, it->pool_name.c_str(), it->image_name.c_str());
+      }
+    }
+    tracepoint(librbd, list_children_exit, r);
+    return r;
+  }
+
   int Image::list_lockers(std::list<librbd::locker_t> *lockers,
                          bool *exclusive, string *tag)
   {
@@ -3352,6 +3367,72 @@ extern "C" ssize_t rbd_list_children(rbd_image_t image, char *pools,
   return ret;
 }
 
+extern "C" int rbd_list_children2(rbd_image_t image,
+                                  rbd_child_info_t *children,
+                                 int *max_children)
+{
+  vector<librbd::child_info_t> cpp_children;
+  librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
+  tracepoint(librbd, list_children_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only);
+
+  if (!max_children) {
+    tracepoint(librbd, list_children_exit, -EINVAL);
+    return -EINVAL;
+  }
+
+  int r = librbd::list_children(ictx, &cpp_children);
+  if (r == -ENOENT) {
+    tracepoint(librbd, list_children_exit, *max_children);
+    r = 0;
+  }
+  if (r < 0) {
+    tracepoint(librbd, list_children_exit, *max_children);
+    return r;
+  }
+  if (*max_children < (int)cpp_children.size() + 1) {
+    *max_children = (int)cpp_children.size() + 1;
+    tracepoint(librbd, list_children_exit, *max_children);
+    return -ERANGE;
+  }
+
+  int i;
+  for (i = 0; i < (int)cpp_children.size(); i++) {
+    children[i].pool_name = strdup(cpp_children[i].pool_name.c_str());
+    children[i].image_name = strdup(cpp_children[i].image_name.c_str());
+    children[i].image_id = strdup(cpp_children[i].image_id.c_str());
+    children[i].trash = cpp_children[i].trash;
+    if (!children[i].pool_name || !children[i].image_name ||
+        !children[i].image_id) {
+      rbd_list_children_cleanup(&children[i], i);
+      tracepoint(librbd, list_children_exit, *max_children);
+      return -ENOMEM;
+    }
+    tracepoint(librbd, list_children_entry, children[i].pool_name, children[i].image_name);
+  }
+  children[i].pool_name = NULL;
+  children[i].image_name = NULL;
+  children[i].image_id = NULL;
+
+  r = (int)cpp_children.size();
+  tracepoint(librbd, list_children_exit, *max_children);
+  return r;
+}
+
+extern "C" void rbd_list_child_cleanup(rbd_child_info_t *child)
+{
+  free((void *)child->pool_name);
+  free((void *)child->image_name);
+  free((void *)child->image_id);
+}
+
+extern "C" void rbd_list_children_cleanup(rbd_child_info_t *children,
+                                          size_t num_children)
+{
+  for (size_t i=0; i < num_children; i++) {
+    rbd_list_child_cleanup(&children[i]);
+  }
+}
+
 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 a4e1e1e6e830b73e58fcc45cd2d5c52ff9aa96ee..949504a34d2bcfa8ff57f100dfeae28379215980 100644 (file)
@@ -96,6 +96,12 @@ cdef extern from "rbd/librbd.h" nogil:
         uint64_t size
         char *name
 
+    ctypedef struct rbd_child_info_t:
+        char *pool_name
+        char *image_name
+        char *image_id
+        bint trash
+
     ctypedef enum rbd_mirror_mode_t:
         _RBD_MIRROR_MODE_DISABLED "RBD_MIRROR_MODE_DISABLED"
         _RBD_MIRROR_MODE_IMAGE "RBD_MIRROR_MODE_IMAGE"
@@ -269,6 +275,10 @@ cdef extern from "rbd/librbd.h" nogil:
                                void *cbdata)
     ssize_t rbd_list_children(rbd_image_t image, char *pools, size_t *pools_len,
                               char *images, size_t *images_len)
+    int rbd_list_children2(rbd_image_t image, rbd_child_info_t *children,
+                           int *max_children)
+    void rbd_list_children_cleanup(rbd_child_info_t *children,
+                                   size_t num_children)
     ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive,
                              char *tag, size_t *tag_len,
                              char *clients, size_t *clients_len,
@@ -2232,6 +2242,14 @@ written." % (self.name, ret, length))
             free(c_pools)
             free(c_images)
 
+    def list_children2(self):
+        """
+        Iterate over the children of a snapshot.
+
+        :returns: :class:`ChildIterator`
+        """
+        return ChildIterator(self)
+
     def list_lockers(self):
         """
         List clients that have locked the image and information
@@ -2958,3 +2976,53 @@ cdef class TrashIterator(object):
         if self.entries:
             free(self.entries)
 
+cdef class ChildIterator(object):
+    """
+    Iterator over child info for a snapshot.
+
+    Yields a dictionary containing information about a child.
+
+    Keys are:
+
+    * ``pool`` (str) - name of the pool
+
+    * ``image`` (str) - name of the child
+
+    * ``id`` (str) - id of the child
+
+    * ``trash`` (bool) - True if child is in trash bin
+    """
+
+    cdef rbd_child_info_t *children
+    cdef int num_children
+    cdef object image
+
+    def __init__(self, Image image):
+        self.image = image
+        self.children = NULL
+        self.num_children = 10
+        while True:
+            self.children = <rbd_child_info_t*>realloc_chk(self.children,
+                                                           self.num_children *
+                                                           sizeof(rbd_child_info_t))
+            with nogil:
+                ret = rbd_list_children2(image.image, self.children, &self.num_children)
+            if ret >= 0:
+                self.num_children = ret
+                break
+            elif ret != -errno.ERANGE:
+                raise make_ex(ret, 'error listing children.')
+
+    def __iter__(self):
+        for i in range(self.num_children):
+            yield {
+                'pool'  : decode_cstr(self.children[i].pool_name),
+                'image' : decode_cstr(self.children[i].image_name),
+                'id'    : decode_cstr(self.children[i].image_id),
+                'trash' : self.children[i].trash
+                }
+
+    def __dealloc__(self):
+        if self.children:
+            rbd_list_children_cleanup(self.children, self.num_children)
+            free(self.children)
index eed9aab9e738f5c0201d59bc5ee56510c3986000..dfbfbe71d543b969d063adc12a134d3a4579f7ba 100644 (file)
@@ -144,7 +144,7 @@ Skip test on FreeBSD as it generates different output there.
     --io-type arg        IO type (read , write, or readwrite(rw))
   
   rbd help children
-  usage: rbd children [--pool <pool>] [--image <image>] [--snap <snap>] 
+  usage: rbd children [--pool <pool>] [--image <image>] [--snap <snap>] [--all] 
                       [--format <format>] [--pretty-format] 
                       <snap-spec> 
   
@@ -158,6 +158,7 @@ Skip test on FreeBSD as it generates different output there.
     -p [ --pool ] arg    pool name
     --image arg          image name
     --snap arg           snapshot name
+    -a [ --all ]         list all children of snapshot (include trash)
     --format arg         output format (plain, json, or xml) [default: plain]
     --pretty-format      pretty formatting (json and xml)
   
index 7893244ed85d54bc6a24ca3281af9a5f0164669e..4304ac7cafa3882f39d66b626f531ec3ca56c97d 100644 (file)
@@ -2767,10 +2767,59 @@ static void test_list_children(rbd_image_t image, ssize_t num_expected, ...)
     free(children);
 }
 
+static void test_list_children2(rbd_image_t image, int num_expected, ...)
+{
+  int num_children, i, j, max_size = 10;
+  va_list ap;
+  rbd_child_info_t children[max_size];
+  num_children = rbd_list_children2(image, children, &max_size);
+  printf("num children is: %d\nexpected: %d\n", num_children, num_expected);
+
+  for (i = 0; i < num_children; i++) {
+    printf("child: %s\n", children[i].image_name);
+  }
+
+  va_start(ap, num_expected);
+  for (i = num_expected; i > 0; i--) {
+    char *expected_id = va_arg(ap, char *);
+    char *expected_pool = va_arg(ap, char *);
+    char *expected_image = va_arg(ap, char *);
+    bool expected_trash = va_arg(ap, int);
+    bool found = false;
+    for (j = 0; j < num_children; j++) {
+      if (children[j].pool_name == NULL ||
+          children[j].image_name == NULL ||
+          children[j].image_id == NULL)
+        continue;
+      if (strcmp(children[j].image_id, expected_id) == 0 &&
+          strcmp(children[j].pool_name, expected_pool) == 0 &&
+          strcmp(children[j].image_name, expected_image) == 0 &&
+          children[j].trash == expected_trash) {
+        printf("found child %s/%s/%s\n\n", children[j].pool_name, children[j].image_name, children[j].image_id);
+        rbd_list_child_cleanup(&children[j]);
+        children[j].pool_name = NULL;
+        children[j].image_name = NULL;
+        children[j].image_id = NULL;
+        found = true;
+        break;
+      }
+    }
+    EXPECT_TRUE(found);
+  }
+  va_end(ap);
+
+  for (i = 0; i < num_children; i++) {
+    EXPECT_EQ((const char *)0, children[i].pool_name);
+    EXPECT_EQ((const char *)0, children[i].image_name);
+    EXPECT_EQ((const char *)0, children[i].image_id);
+  }
+}
+
 TEST_F(TestLibRBD, ListChildren)
 {
   REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
 
+  librbd::RBD rbd;
   rados_ioctx_t ioctx1, ioctx2;
   string pool_name1 = create_pool(true);
   string pool_name2 = create_pool(true);
@@ -2779,6 +2828,11 @@ TEST_F(TestLibRBD, ListChildren)
   rados_ioctx_create(_cluster, pool_name1.c_str(), &ioctx1);
   rados_ioctx_create(_cluster, pool_name2.c_str(), &ioctx2);
 
+  rbd_image_t image1;
+  rbd_image_t image2;
+  rbd_image_t image3;
+  rbd_image_t image4;
+
   bool old_format;
   uint64_t features;
   rbd_image_t parent;
@@ -2793,6 +2847,11 @@ TEST_F(TestLibRBD, ListChildren)
   std::string child_name3 = get_temp_image_name();
   std::string child_name4 = get_temp_image_name();
 
+  char child_id1[4096];
+  char child_id2[4096];
+  char child_id3[4096];
+  char child_id4[4096];
+
   // make a parent to clone from
   ASSERT_EQ(0, create_image_full(ioctx1, parent_name.c_str(), 4<<20, &order,
                                 false, features));
@@ -2807,43 +2866,100 @@ TEST_F(TestLibRBD, ListChildren)
 
   ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
                            ioctx2, child_name1.c_str(), features, &order));
+  ASSERT_EQ(0, rbd_open(ioctx2, child_name1.c_str(), &image1, NULL));
+  ASSERT_EQ(0, rbd_get_id(image1, child_id1, sizeof(child_id1)));
   test_list_children(parent, 1, pool_name2.c_str(), child_name1.c_str());
+  test_list_children2(parent, 1,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false);
 
   ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
                            ioctx1, child_name2.c_str(), features, &order));
+  ASSERT_EQ(0, rbd_open(ioctx1, child_name2.c_str(), &image2, NULL));
+  ASSERT_EQ(0, rbd_get_id(image2, child_id2, sizeof(child_id2)));
   test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
                     pool_name1.c_str(), child_name2.c_str());
+  test_list_children2(parent, 2,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false);
 
   ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
                            ioctx2, child_name3.c_str(), features, &order));
+  ASSERT_EQ(0, rbd_open(ioctx2, child_name3.c_str(), &image3, NULL));
+  ASSERT_EQ(0, rbd_get_id(image3, child_id3, sizeof(child_id3)));
   test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
                     pool_name1.c_str(), child_name2.c_str(),
                     pool_name2.c_str(), child_name3.c_str());
+  test_list_children2(parent, 3,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                      child_id3, pool_name2.c_str(), child_name3.c_str(), false);
+
+  librados::IoCtx ioctx3;
+  ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx3));
+  ASSERT_EQ(0, rbd.trash_move(ioctx3, child_name3.c_str(), 0));
+  test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
+                    pool_name1.c_str(), child_name2.c_str());
+  test_list_children2(parent, 3,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                      child_id3, pool_name2.c_str(), child_name3.c_str(), true);
 
   ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
                            ioctx2, child_name4.c_str(), features, &order));
+  ASSERT_EQ(0, rbd_open(ioctx2, child_name4.c_str(), &image4, NULL));
+  ASSERT_EQ(0, rbd_get_id(image4, child_id4, sizeof(child_id4)));
+  test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
+                    pool_name1.c_str(), child_name2.c_str(),
+                    pool_name2.c_str(), child_name4.c_str());
+  test_list_children2(parent, 4,
+                     child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                     child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                     child_id3, pool_name2.c_str(), child_name3.c_str(), true,
+                     child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+  ASSERT_EQ(0, rbd.trash_restore(ioctx3, child_id3, ""));
   test_list_children(parent, 4, pool_name2.c_str(), child_name1.c_str(),
                     pool_name1.c_str(), child_name2.c_str(),
                     pool_name2.c_str(), child_name3.c_str(),
                     pool_name2.c_str(), child_name4.c_str());
+  test_list_children2(parent, 4,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                      child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+                      child_id4, pool_name2.c_str(), child_name4.c_str(), false);
 
+  ASSERT_EQ(0, rbd_close(image1));
   ASSERT_EQ(0, rbd_remove(ioctx2, child_name1.c_str()));
   test_list_children(parent, 3,
                     pool_name1.c_str(), child_name2.c_str(),
                     pool_name2.c_str(), child_name3.c_str(),
                     pool_name2.c_str(), child_name4.c_str());
+  test_list_children2(parent, 3,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                      child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+                      child_id4, pool_name2.c_str(), child_name4.c_str(), false);
 
+  ASSERT_EQ(0, rbd_close(image3));
   ASSERT_EQ(0, rbd_remove(ioctx2, child_name3.c_str()));
   test_list_children(parent, 2,
                     pool_name1.c_str(), child_name2.c_str(),
                     pool_name2.c_str(), child_name4.c_str());
+  test_list_children2(parent, 2,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                      child_id4, pool_name2.c_str(), child_name4.c_str(), false);
 
+  ASSERT_EQ(0, rbd_close(image4));
   ASSERT_EQ(0, rbd_remove(ioctx2, child_name4.c_str()));
   test_list_children(parent, 1,
                     pool_name1.c_str(), child_name2.c_str());
+  test_list_children2(parent, 1,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false);
+  
 
+  ASSERT_EQ(0, rbd_close(image2));
   ASSERT_EQ(0, rbd_remove(ioctx1, child_name2.c_str()));
   test_list_children(parent, 0);
+  test_list_children2(parent, 0);
 
   ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap"));
   ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap"));
@@ -2857,6 +2973,7 @@ TEST_F(TestLibRBD, ListChildrenTiered)
 {
   REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
 
+  librbd::RBD rbd;
   string pool_name1 = m_pool_name;
   string pool_name2 = create_pool(true);
   string pool_name3 = create_pool(true);
@@ -2887,6 +3004,16 @@ TEST_F(TestLibRBD, ListChildrenTiered)
   string child_name3 = get_temp_image_name();
   string child_name4 = get_temp_image_name();
 
+  char child_id1[4096];
+  char child_id2[4096];
+  char child_id3[4096];
+  char child_id4[4096];
+
+  rbd_image_t image1;
+  rbd_image_t image2;
+  rbd_image_t image3;
+  rbd_image_t image4;
+
   rados_ioctx_t ioctx1, ioctx2;
   rados_ioctx_create(_cluster, pool_name1.c_str(), &ioctx1);
   rados_ioctx_create(_cluster, pool_name2.c_str(), &ioctx2);
@@ -2913,12 +3040,21 @@ TEST_F(TestLibRBD, ListChildrenTiered)
 
   ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
                            ioctx2, child_name1.c_str(), features, &order));
+  ASSERT_EQ(0, rbd_open(ioctx2, child_name1.c_str(), &image1, NULL));
+  ASSERT_EQ(0, rbd_get_id(image1, child_id1, sizeof(child_id1)));
   test_list_children(parent, 1, pool_name2.c_str(), child_name1.c_str());
+  test_list_children2(parent, 1,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false);
 
   ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
                            ioctx1, child_name2.c_str(), features, &order));
+  ASSERT_EQ(0, rbd_open(ioctx1, child_name2.c_str(), &image2, NULL));
+  ASSERT_EQ(0, rbd_get_id(image2, child_id2, sizeof(child_id2)));
   test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
                     pool_name1.c_str(), child_name2.c_str());
+  test_list_children2(parent, 2,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false);
 
   // read from the cache to populate it
   rbd_image_t tier_image;
@@ -2932,34 +3068,81 @@ TEST_F(TestLibRBD, ListChildrenTiered)
 
   ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
                            ioctx2, child_name3.c_str(), features, &order));
+  ASSERT_EQ(0, rbd_open(ioctx2, child_name3.c_str(), &image3, NULL));
+  ASSERT_EQ(0, rbd_get_id(image3, child_id3, sizeof(child_id3)));
   test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
                     pool_name1.c_str(), child_name2.c_str(),
                     pool_name2.c_str(), child_name3.c_str());
+  test_list_children2(parent, 3,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                      child_id3, pool_name2.c_str(), child_name3.c_str(), false);
+
+  librados::IoCtx ioctx3;
+  ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx3));
+  ASSERT_EQ(0, rbd.trash_move(ioctx3, child_name3.c_str(), 0));
+  test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
+                    pool_name1.c_str(), child_name2.c_str());
+  test_list_children2(parent, 3,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                      child_id3, pool_name2.c_str(), child_name3.c_str(), true);
 
   ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
                            ioctx2, child_name4.c_str(), features, &order));
+  ASSERT_EQ(0, rbd_open(ioctx2, child_name4.c_str(), &image4, NULL));
+  ASSERT_EQ(0, rbd_get_id(image4, child_id4, sizeof(child_id4)));
+  test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
+                    pool_name1.c_str(), child_name2.c_str(),
+                    pool_name2.c_str(), child_name4.c_str());
+  test_list_children2(parent, 4,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                      child_id3, pool_name2.c_str(), child_name3.c_str(), true,
+                      child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+  
+  ASSERT_EQ(0, rbd.trash_restore(ioctx3, child_id3, ""));
   test_list_children(parent, 4, pool_name2.c_str(), child_name1.c_str(),
                     pool_name1.c_str(), child_name2.c_str(),
                     pool_name2.c_str(), child_name3.c_str(),
                     pool_name2.c_str(), child_name4.c_str());
+  test_list_children2(parent, 4,
+                      child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                      child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+                      child_id4, pool_name2.c_str(), child_name4.c_str(), false);
 
+  ASSERT_EQ(0, rbd_close(image1));
   ASSERT_EQ(0, rbd_remove(ioctx2, child_name1.c_str()));
   test_list_children(parent, 3,
                     pool_name1.c_str(), child_name2.c_str(),
                     pool_name2.c_str(), child_name3.c_str(),
                     pool_name2.c_str(), child_name4.c_str());
+  test_list_children2(parent, 3,
+                     child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                     child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+                     child_id4, pool_name2.c_str(), child_name4.c_str(), false);
 
+  ASSERT_EQ(0, rbd_close(image3));
   ASSERT_EQ(0, rbd_remove(ioctx2, child_name3.c_str()));
   test_list_children(parent, 2,
                     pool_name1.c_str(), child_name2.c_str(),
                     pool_name2.c_str(), child_name4.c_str());
+  test_list_children2(parent, 2,
+                     child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+                     child_id4, pool_name2.c_str(), child_name4.c_str(), false);
 
+  ASSERT_EQ(0, rbd_close(image4));
   ASSERT_EQ(0, rbd_remove(ioctx2, child_name4.c_str()));
   test_list_children(parent, 1,
                     pool_name1.c_str(), child_name2.c_str());
+  test_list_children2(parent, 1,
+                      child_id2, pool_name1.c_str(), child_name2.c_str(), false);
 
+  ASSERT_EQ(0, rbd_close(image2));
   ASSERT_EQ(0, rbd_remove(ioctx1, child_name2.c_str()));
   test_list_children(parent, 0);
+  test_list_children2(parent, 0);
 
   ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap"));
   ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap"));
index e72045a5918887c5f56d6e8658aa7665d7f53f0f..f46f09cedcf9f1ef11738ed066d98049c8ca66b6 100644 (file)
@@ -1043,32 +1043,74 @@ class TestClone(object):
         deduped = set([(pool_name, image[1]) for image in actual])
         eq(deduped, set(expected))
 
+    def check_children2(self, expected):
+        actual = list(self.image.list_children2())
+        eq(actual, expected)
+
+    def get_image_id(self, ioctx, name):
+        with Image(ioctx, name) as image:
+            return image.id()
+
     def test_list_children(self):
         global ioctx
         global features
         self.image.set_snap('snap1')
         self.check_children([(pool_name, self.clone_name)])
+        self.check_children2(
+            [{'pool': pool_name, '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()), [])
 
         clone_name = get_temp_image_name() + '_'
         expected_children = []
+        expected_children2 = []
         for i in range(10):
             self.rbd.clone(ioctx, image_name, 'snap1', ioctx,
                            clone_name + str(i), features)
             expected_children.append((pool_name, clone_name + str(i)))
+            expected_children2.append(
+                {'pool': pool_name, 'image': clone_name + str(i), 'trash': False,
+                 'id': self.get_image_id(ioctx, clone_name + str(i))})
             self.check_children(expected_children)
+            self.check_children2(expected_children2)
+
+        image6_id = self.get_image_id(ioctx, clone_name + str(5))
+        RBD().trash_move(ioctx, clone_name + str(5), 0)
+        expected_children.remove((pool_name, clone_name + str(5)))
+        for item in expected_children2:
+          for k, v in item.items():
+            if v == image6_id:
+              item["trash"] = True
+        self.check_children(expected_children)
+        self.check_children2(expected_children2)
+
+        RBD().trash_restore(ioctx, image6_id, clone_name + str(5))
+        expected_children.append((pool_name, clone_name + str(5)))
+        for item in expected_children2:
+          for k, v in item.items():
+            if v == image6_id:
+              item["trash"] = False
+        self.check_children(expected_children)
+        self.check_children2(expected_children2)
 
         for i in range(10):
             self.rbd.remove(ioctx, clone_name + str(i))
-            expected_children.pop(0)
+            expected_children.remove((pool_name, clone_name + str(i)))
+            expected_children2.pop(0)
             self.check_children(expected_children)
+            self.check_children2(expected_children2)
 
         eq(self.image.list_children(), [])
+        eq(list(self.image.list_children2()), [])
         self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
                        features)
         self.check_children([(pool_name, self.clone_name)])
+        self.check_children2(
+            [{'pool': pool_name, '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):
index 2008bbec02447b116d626d38192206a737884b91..b0f815c6692b4fcc2b61b05ddf86ee120697c1b1 100644 (file)
@@ -16,26 +16,44 @@ namespace children {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
-int do_list_children(librbd::Image &image, Formatter *f)
+int do_list_children(librados::IoCtx &io_ctx, librbd::Image &image,
+                     bool all_flag, Formatter *f)
 {
-  std::set<std::pair<std::string, std::string> > children;
-  int r;
-
-  r = image.list_children(&children);
+  std::vector<librbd::child_info_t> children;
+  librbd::RBD rbd;
+  int r = image.list_children2(&children);
   if (r < 0)
     return r;
 
   if (f)
     f->open_array_section("children");
 
-  for (auto &child_it : children) {
+  for (std::vector<librbd::child_info_t>::iterator it = children.begin();
+       it != children.end(); ++it) {
+    bool trash = it->trash;
     if (f) {
-      f->open_object_section("child");
-      f->dump_string("pool", child_it.first);
-      f->dump_string("image", child_it.second);
-      f->close_section();
+      if (all_flag) {
+        f->open_object_section("child");
+        f->dump_string("pool", it->pool_name);
+        f->dump_string("image", it->image_name);
+        f->dump_string("id", it->image_id);
+        f->dump_bool("trash", it->trash);
+        f->close_section();
+      } else if (!trash) {
+        f->open_object_section("child");
+        f->dump_string("pool", it->pool_name);
+        f->dump_string("image", it->image_name);
+        f->close_section();
+      }
     } else {
-      std::cout << child_it.first << "/" << child_it.second << std::endl;
+      if (all_flag) {
+        std::cout << it->pool_name << "/" << it->image_name;
+        if (trash)
+          std::cout << " (trash " << it->image_id << ")";
+        std::cout << std::endl;
+      } else if (!trash) {
+        std::cout << it->pool_name << "/" << it->image_name << std::endl;
+      }
     }
   }
 
@@ -50,6 +68,8 @@ int do_list_children(librbd::Image &image, Formatter *f)
 void get_arguments(po::options_description *positional,
                    po::options_description *options) {
   at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  options->add_options()
+    ("all,a", po::bool_switch(), "list all children of snapshot (include trash)");
   at::add_format_options(options);
 }
 
@@ -80,7 +100,7 @@ int execute(const po::variables_map &vm) {
     return r;
   }
 
-  r = do_list_children(image, formatter.get());
+  r = do_list_children(io_ctx, image, vm["all"].as<bool>(), formatter.get());
   if (r < 0) {
     std::cerr << "rbd: listing children failed: " << cpp_strerror(r)
               << std::endl;