summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJason Dillaman <dillaman@redhat.com>2017-11-14 22:14:14 +0100
committerGitHub <noreply@github.com>2017-11-14 22:14:14 +0100
commit16715eccdcb79cb8a5462c341140263127e07cc0 (patch)
tree30f6ec4e711c9c62da8405aef440ab7f30635821 /src
parentMerge pull request #18007 from yehudasa/wip-rgw-config-desc (diff)
parentlibrbd: add new API methods and expand the rbd CLI to list trashed images (diff)
downloadceph-16715eccdcb79cb8a5462c341140263127e07cc0.tar.xz
ceph-16715eccdcb79cb8a5462c341140263127e07cc0.zip
Merge pull request #18483 from Songweibin/wip-list-children2
rbd: fix rbd children listing when child is in trash Reviewed-by: Jason Dillaman <dillaman@redhat.com>
Diffstat (limited to 'src')
-rw-r--r--src/include/rbd/librbd.h14
-rw-r--r--src/include/rbd/librbd.hpp12
-rw-r--r--src/librbd/internal.cc53
-rw-r--r--src/librbd/internal.h2
-rw-r--r--src/librbd/librbd.cc103
-rw-r--r--src/pybind/rbd/rbd.pyx68
-rw-r--r--src/test/cli/rbd/help.t3
-rw-r--r--src/test/librbd/test_librbd.cc183
-rw-r--r--src/test/pybind/test_rbd.py44
-rw-r--r--src/tools/rbd/action/Children.cc44
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;