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>
This commit is contained in:
Jason Dillaman 2017-11-14 16:14:14 -05:00 committed by GitHub
commit 16715eccdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 494 additions and 32 deletions

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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,

View File

@ -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,

View 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)

View 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)

View File

@ -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"));

View 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):

View 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;