diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/include/rbd/librbd.h | 14 | ||||
-rw-r--r-- | src/include/rbd/librbd.hpp | 12 | ||||
-rw-r--r-- | src/librbd/internal.cc | 53 | ||||
-rw-r--r-- | src/librbd/internal.h | 2 | ||||
-rw-r--r-- | src/librbd/librbd.cc | 103 | ||||
-rw-r--r-- | src/pybind/rbd/rbd.pyx | 68 | ||||
-rw-r--r-- | src/test/cli/rbd/help.t | 3 | ||||
-rw-r--r-- | src/test/librbd/test_librbd.cc | 183 | ||||
-rw-r--r-- | src/test/pybind/test_rbd.py | 44 | ||||
-rw-r--r-- | src/tools/rbd/action/Children.cc | 44 |
10 files changed, 494 insertions, 32 deletions
diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index 23c166a58f5..47bb8e6e539 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -57,7 +57,6 @@ extern "C" { #define RBD_FLAG_OBJECT_MAP_INVALID (1<<0) #define RBD_FLAG_FAST_DIFF_INVALID (1<<1) -typedef void *rbd_snap_t; typedef void *rbd_image_t; typedef void *rbd_image_options_t; @@ -74,6 +73,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 @@ -541,6 +547,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 diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index 7e9825d2d45..c8238d5b469 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -101,6 +101,13 @@ namespace librbd { time_t deferment_end_time; } trash_image_info_t; + typedef struct { + std::string pool_name; + std::string image_name; + std::string image_id; + bool trash; + } child_info_t; + class CEPH_RBD_API RBD { public: @@ -311,6 +318,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, diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc index 9c24bb7f818..f583d9632a8 100644 --- a/src/librbd/internal.cc +++ b/src/librbd/internal.cc @@ -109,6 +109,15 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { return 0; } +bool compare_by_name(const child_info_t& c1, const child_info_t& c2) +{ + if (c1.pool_name != c2.pool_name) + return c1.pool_name < c2.pool_name; + else if (c1.image_name != c2.image_name) + return c1.image_name < c2.image_name; + else + return false; +} } // anonymous namespace @@ -620,7 +629,8 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { return 0; } - int list_children(ImageCtx *ictx, set<pair<string, string> >& names) + int list_children(ImageCtx *ictx, + vector<child_info_t> *names) { CephContext *cct = ictx->cct; ldout(cct, 20) << "children list " << ictx->name << dendl; @@ -640,7 +650,7 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { } Rados rados(ictx->md_ctx); - for ( auto &info : image_info){ + for (auto &info : image_info) { IoCtx ioctx; r = rados.ioctx_create2(info.first.first, ioctx); if (r < 0) { @@ -650,17 +660,38 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { } for (auto &id_it : info.second) { - 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 " << info.first.second << dendl; - return r; - } - names.insert(make_pair(info.first.second, name)); + string name; + bool trash = false; + r = cls_client::dir_get_name(&ioctx, RBD_DIRECTORY, id_it, &name); + if (r == -ENOENT) { + cls::rbd::TrashImageSpec trash_spec; + r = cls_client::trash_get(&ioctx, id_it, &trash_spec); + if (r < 0) { + if (r != -EOPNOTSUPP && r != -ENOENT) { + lderr(cct) << "Error looking up name for image id " << id_it + << " in rbd trash" << dendl; + return r; + } + return -ENOENT; + } + name = trash_spec.name; + trash = true; + } else if (r < 0 && r != -ENOENT) { + lderr(cct) << "Error looking up name for image id " << id_it + << " in pool " << info.first.second << dendl; + return r; + } + names->push_back( + child_info_t { + info.first.second, + name, + id_it, + trash + }); } } - + std::sort(names->begin(), names->end(), compare_by_name); + return 0; } diff --git a/src/librbd/internal.h b/src/librbd/internal.h index b08fab3be0d..cf5389caed1 100644 --- a/src/librbd/internal.h +++ b/src/librbd/internal.h @@ -64,7 +64,7 @@ namespace librbd { int list(librados::IoCtx& io_ctx, std::vector<std::string>& names); int list_children(ImageCtx *ictx, - std::set<std::pair<std::string, std::string> > & names); + std::vector<child_info_t> *names); int create(librados::IoCtx& io_ctx, const char *imgname, uint64_t size, int *order); int create(librados::IoCtx& io_ctx, const char *imgname, uint64_t size, diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index ddb6ef17769..86b690815da 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -1292,11 +1292,30 @@ namespace librbd { { 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); + vector<librbd::child_info_t> children2; + int r = librbd::list_children(ictx, &children2); if (r >= 0) { - for (set<pair<string, string> >::const_iterator it = children->begin(); - it != children->end(); ++it) { - tracepoint(librbd, list_children_entry, it->first.c_str(), it->second.c_str()); + for (std::vector<librbd::child_info_t>::iterator it = children2.begin(); + it != children2.end(); ++it) { + if (!it->trash) { + children->insert(make_pair(it->pool_name, it->image_name)); + 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_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); @@ -3364,13 +3383,21 @@ extern "C" ssize_t rbd_list_children(rbd_image_t image, char *pools, librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; tracepoint(librbd, list_children_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only); set<pair<string, string> > image_set; + vector<librbd::child_info_t> children; - int r = librbd::list_children(ictx, image_set); + int r = librbd::list_children(ictx, &children); if (r < 0) { tracepoint(librbd, list_children_exit, r); return r; } + for (std::vector<librbd::child_info_t>::iterator it = children.begin(); + it != children.end(); ++it) { + if (!it->trash) { + image_set.insert(make_pair(it->pool_name, it->image_name)); + } + } + size_t pools_total = 0; size_t images_total = 0; for (set<pair<string, string> >::const_iterator it = image_set.begin(); @@ -3409,6 +3436,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, diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index a4e1e1e6e83..949504a34d2 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -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) diff --git a/src/test/cli/rbd/help.t b/src/test/cli/rbd/help.t index eed9aab9e73..dfbfbe71d54 100644 --- a/src/test/cli/rbd/help.t +++ b/src/test/cli/rbd/help.t @@ -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) diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index 36b32b5587e..dd68e1d9b3f 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -3097,10 +3097,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); @@ -3109,6 +3158,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; @@ -3123,6 +3177,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)); @@ -3137,43 +3196,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")); @@ -3187,6 +3303,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); @@ -3217,6 +3334,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); @@ -3243,12 +3370,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; @@ -3262,34 +3398,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")); diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index e72045a5918..f46f09cedcf 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -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): diff --git a/src/tools/rbd/action/Children.cc b/src/tools/rbd/action/Children.cc index 2008bbec024..b0f815c6692 100644 --- a/src/tools/rbd/action/Children.cc +++ b/src/tools/rbd/action/Children.cc @@ -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; |