From 3d5f055a0796c4e059c22b46f6f1b840bb9d10ef Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 20 Nov 2018 19:06:23 -0500 Subject: [PATCH] librbd: expand API to include namespaces and image ids The get parent and list children methods now return a common struct that includes the full pool details (including the optional namespace). Additionally, a new image list method has been added which includes both the name and id (of v2 images). Fixes: http://tracker.ceph.com/issues/36650 Signed-off-by: Jason Dillaman --- src/include/rbd/librbd.h | 85 ++++-- src/include/rbd/librbd.hpp | 45 +++- src/librbd/api/Image.cc | 265 ++++++++++++++++-- src/librbd/api/Image.h | 16 +- src/librbd/api/Mirror.cc | 56 ++-- src/librbd/api/Pool.cc | 2 +- src/librbd/internal.cc | 264 ++++-------------- src/librbd/internal.h | 6 - src/librbd/librbd.cc | 477 +++++++++++++++++++++++++-------- src/pybind/rbd/rbd.pyx | 220 +++++++++------ src/test/librbd/test_librbd.cc | 100 +++++++ src/test/pybind/test_rbd.py | 13 +- 12 files changed, 1044 insertions(+), 505 deletions(-) diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index cbbd42b90e1..976a6cbeff9 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -68,6 +68,32 @@ typedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void *ptr); typedef void (*rbd_update_callback_t)(void *arg); +typedef enum { + RBD_SNAP_NAMESPACE_TYPE_USER = 0, + RBD_SNAP_NAMESPACE_TYPE_GROUP = 1, + RBD_SNAP_NAMESPACE_TYPE_TRASH = 2 +} rbd_snap_namespace_type_t; + +typedef struct { + char *id; + char *name; +} rbd_image_spec_t; + +typedef struct { + int64_t pool_id; + char *pool_name; + char *pool_namespace; + char *image_id; + char *image_name; + bool trash; +} rbd_linked_image_spec_t; + +typedef struct { + uint64_t id; + rbd_snap_namespace_type_t namespace_type; + char *name; +} rbd_snap_spec_t; + typedef struct { uint64_t id; uint64_t size; @@ -179,12 +205,6 @@ typedef struct { rbd_group_snap_state_t state; } rbd_group_snap_info_t; -typedef enum { - RBD_SNAP_NAMESPACE_TYPE_USER = 0, - RBD_SNAP_NAMESPACE_TYPE_GROUP = 1, - RBD_SNAP_NAMESPACE_TYPE_TRASH = 2 -} rbd_snap_namespace_type_t; - typedef struct { int64_t group_pool; char *group_name; @@ -296,8 +316,21 @@ CEPH_RBD_API int rbd_image_options_unset(rbd_image_options_t opts, int optname); CEPH_RBD_API void rbd_image_options_clear(rbd_image_options_t opts); CEPH_RBD_API int rbd_image_options_is_empty(rbd_image_options_t opts); +/* helpers */ +CEPH_RBD_API void rbd_image_spec_cleanup(rbd_image_spec_t *image); +CEPH_RBD_API void rbd_image_spec_list_cleanup(rbd_image_spec_t *images, + size_t num_images); +CEPH_RBD_API void rbd_linked_image_spec_cleanup(rbd_linked_image_spec_t *image); +CEPH_RBD_API void rbd_linked_image_spec_list_cleanup( + rbd_linked_image_spec_t *images, size_t num_images); +CEPH_RBD_API void rbd_snap_spec_cleanup(rbd_snap_spec_t *snap); + /* images */ -CEPH_RBD_API int rbd_list(rados_ioctx_t io, char *names, size_t *size); +CEPH_RBD_API int rbd_list(rados_ioctx_t io, char *names, size_t *size) + __attribute__((deprecated)); +CEPH_RBD_API int rbd_list2(rados_ioctx_t io, rbd_image_spec_t* images, + size_t *max_images); + CEPH_RBD_API int rbd_create(rados_ioctx_t io, const char *name, uint64_t size, int *order); CEPH_RBD_API int rbd_create2(rados_ioctx_t io, const char *name, uint64_t size, @@ -352,8 +385,10 @@ CEPH_RBD_API int rbd_trash_list(rados_ioctx_t io, CEPH_RBD_API void rbd_trash_list_cleanup(rbd_trash_image_info_t *trash_entries, size_t num_entries); CEPH_RBD_API int rbd_trash_remove(rados_ioctx_t io, const char *id, bool force); -CEPH_RBD_API int rbd_trash_remove_with_progress(rados_ioctx_t io, const char *id, - bool force, librbd_progress_fn_t cb, +CEPH_RBD_API int rbd_trash_remove_with_progress(rados_ioctx_t io, + const char *id, + bool force, + librbd_progress_fn_t cb, void *cbdata); CEPH_RBD_API int rbd_trash_restore(rados_ioctx_t io, const char *id, const char *name); @@ -527,18 +562,25 @@ CEPH_RBD_API int rbd_get_id(rbd_image_t image, char *id, size_t id_len); CEPH_RBD_API int rbd_get_block_name_prefix(rbd_image_t image, char *prefix, size_t prefix_len); CEPH_RBD_API int64_t rbd_get_data_pool_id(rbd_image_t image); + CEPH_RBD_API int rbd_get_parent_info(rbd_image_t image, char *parent_poolname, size_t ppoolnamelen, char *parent_name, size_t pnamelen, char *parent_snapname, - size_t psnapnamelen); + size_t psnapnamelen) + __attribute__((deprecated)); CEPH_RBD_API int rbd_get_parent_info2(rbd_image_t image, char *parent_poolname, size_t ppoolnamelen, char *parent_name, size_t pnamelen, char *parent_id, size_t pidlen, char *parent_snapname, - size_t psnapnamelen); + size_t psnapnamelen) + __attribute__((deprecated)); +CEPH_RBD_API int rbd_get_parent(rbd_image_t image, + rbd_linked_image_spec_t *parent_image, + rbd_snap_spec_t *parent_snap); + CEPH_RBD_API int rbd_get_flags(rbd_image_t image, uint64_t *flags); CEPH_RBD_API int rbd_get_group(rbd_image_t image, rbd_group_info_t *group_info, size_t group_info_size); @@ -603,8 +645,9 @@ CEPH_RBD_API int rbd_snap_list(rbd_image_t image, rbd_snap_info_t *snaps, CEPH_RBD_API void rbd_snap_list_end(rbd_snap_info_t *snaps); CEPH_RBD_API int rbd_snap_create(rbd_image_t image, const char *snapname); CEPH_RBD_API int rbd_snap_remove(rbd_image_t image, const char *snapname); -CEPH_RBD_API int rbd_snap_remove2(rbd_image_t image, const char *snap_name, uint32_t flags, - librbd_progress_fn_t cb, void *cbdata); +CEPH_RBD_API int rbd_snap_remove2(rbd_image_t image, const char *snap_name, + uint32_t flags, librbd_progress_fn_t cb, + void *cbdata); CEPH_RBD_API int rbd_snap_remove_by_id(rbd_image_t image, uint64_t snap_id); CEPH_RBD_API int rbd_snap_rollback(rbd_image_t image, const char *snapname); CEPH_RBD_API int rbd_snap_rollback_with_progress(rbd_image_t image, @@ -710,13 +753,21 @@ 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); + size_t *images_len) + __attribute__((deprecated)); 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); + int *max_children) + __attribute__((deprecated)); +CEPH_RBD_API void rbd_list_child_cleanup(rbd_child_info_t *child) + __attribute__((deprecated)); CEPH_RBD_API void rbd_list_children_cleanup(rbd_child_info_t *children, - size_t num_children); + size_t num_children) + __attribute__((deprecated)); + +CEPH_RBD_API int rbd_list_children3(rbd_image_t image, + rbd_linked_image_spec_t *images, + size_t *max_images); /** * @defgroup librbd_h_locking Advisory Locking diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index 93703b1b40a..926158e1a14 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -34,14 +34,34 @@ namespace librbd { typedef void *completion_t; typedef void (*callback_t)(completion_t cb, void *arg); + typedef struct { + std::string id; + std::string name; + } image_spec_t; + + typedef struct { + int64_t pool_id; + std::string pool_name; + std::string pool_namespace; + std::string image_id; + std::string image_name; + bool trash; + } linked_image_spec_t; + + typedef rbd_snap_namespace_type_t snap_namespace_type_t; + + typedef struct { + uint64_t id; + snap_namespace_type_t namespace_type; + std::string name; + } snap_spec_t; + typedef struct { uint64_t id; uint64_t size; std::string name; } snap_info_t; - typedef rbd_snap_namespace_type_t snap_namespace_type_t; - typedef struct { int64_t group_pool; std::string group_name; @@ -190,7 +210,11 @@ public: const char *snapname, RBD::AioCompletion *c); int aio_open_by_id_read_only(IoCtx& io_ctx, Image& image, const char *id, const char *snapname, RBD::AioCompletion *c); - int list(IoCtx& io_ctx, std::vector& names); + + int list(IoCtx& io_ctx, std::vector& names) + __attribute__((deprecated)); + int list2(IoCtx& io_ctx, std::vector* images); + int create(IoCtx& io_ctx, const char *name, uint64_t size, int *order); int create2(IoCtx& io_ctx, const char *name, uint64_t size, uint64_t features, int *order); @@ -383,9 +407,13 @@ public: std::string get_block_name_prefix(); int64_t get_data_pool_id(); int parent_info(std::string *parent_poolname, std::string *parent_name, - std::string *parent_snapname); + std::string *parent_snapname) + __attribute__((deprecated)); int parent_info2(std::string *parent_poolname, std::string *parent_name, - std::string *parent_id, std::string *parent_snapname); + std::string *parent_id, std::string *parent_snapname) + __attribute__((deprecated)); + int get_parent(linked_image_spec_t *parent_image, snap_spec_t *parent_snap); + int old_format(uint8_t *old); int size(uint64_t *size); int get_group(group_info_t *group_info, size_t group_info_size); @@ -442,12 +470,15 @@ public: * Returns a pair of poolname, imagename for each clone * of this image at the currently set snapshot. */ - int list_children(std::set > *children); + int list_children(std::set > *children) + __attribute__((deprecated)); /** * 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 *children); + int list_children2(std::vector *children) + __attribute__((deprecated)); + int list_children3(std::vector *images); /* advisory locking (see librbd.h for details) */ int list_lockers(std::list *lockers, diff --git a/src/librbd/api/Image.cc b/src/librbd/api/Image.cc index c2d887e48f9..a13e60bc31c 100644 --- a/src/librbd/api/Image.cc +++ b/src/librbd/api/Image.cc @@ -25,6 +25,39 @@ namespace librbd { namespace api { +namespace { + +bool compare_by_pool(const librbd::linked_image_spec_t& lhs, + const librbd::linked_image_spec_t& rhs) +{ + if (lhs.pool_id != rhs.pool_id) { + return lhs.pool_id < rhs.pool_id; + } else if (lhs.pool_namespace != rhs.pool_namespace) { + return lhs.pool_namespace < rhs.pool_namespace; + } + return false; +} + +bool compare(const librbd::linked_image_spec_t& lhs, + const librbd::linked_image_spec_t& rhs) +{ + if (lhs.pool_name != rhs.pool_name) { + return lhs.pool_name < rhs.pool_name; + } else if (lhs.pool_id != rhs.pool_id) { + return lhs.pool_id < rhs.pool_id; + } else if (lhs.pool_namespace != rhs.pool_namespace) { + return lhs.pool_namespace < rhs.pool_namespace; + } else if (lhs.image_name != rhs.image_name) { + return lhs.image_name < rhs.image_name; + } else if (lhs.image_id != rhs.image_id) { + return lhs.image_id < rhs.image_id; + } + return false; +} + + +} // anonymous namespace + template int Image::get_op_features(I *ictx, uint64_t *op_features) { CephContext *cct = ictx->cct; @@ -41,7 +74,54 @@ int Image::get_op_features(I *ictx, uint64_t *op_features) { } template -int Image::list_images(librados::IoCtx& io_ctx, ImageNameToIds *images) { +int Image::list_images(librados::IoCtx& io_ctx, + std::vector *images) { + CephContext *cct = (CephContext *)io_ctx.cct(); + ldout(cct, 20) << "list " << &io_ctx << dendl; + + int r; + images->clear(); + + if (io_ctx.get_namespace().empty()) { + bufferlist bl; + r = io_ctx.read(RBD_DIRECTORY, bl, 0, 0); + if (r == -ENOENT) { + return 0; + } else if (r < 0) { + lderr(cct) << "error listing v1 images: " << cpp_strerror(r) << dendl; + return r; + } + + // old format images are in a tmap + if (bl.length()) { + auto p = bl.cbegin(); + bufferlist header; + std::map m; + decode(header, p); + decode(m, p); + for (auto& it : m) { + images->push_back({.id ="", .name = it.first}); + } + } + } + + std::map image_names_to_ids; + r = list_images_v2(io_ctx, &image_names_to_ids); + if (r < 0) { + lderr(cct) << "error listing v2 images: " << cpp_strerror(r) << dendl; + return r; + } + + for (const auto& img_pair : image_names_to_ids) { + images->push_back({.id = img_pair.second, + .name = img_pair.first}); + } + + return 0; +} + +template +int Image::list_images_v2(librados::IoCtx& io_ctx, ImageNameToIds *images) { CephContext *cct = (CephContext *)io_ctx.cct(); ldout(cct, 20) << "io_ctx=" << &io_ctx << dendl; @@ -51,8 +131,8 @@ int Image::list_images(librados::IoCtx& io_ctx, ImageNameToIds *images) { string last_read = ""; do { map images_page; - r = cls_client::dir_list(&io_ctx, RBD_DIRECTORY, - last_read, max_read, &images_page); + r = cls_client::dir_list(&io_ctx, RBD_DIRECTORY, last_read, max_read, + &images_page); if (r < 0 && r != -ENOENT) { lderr(cct) << "error listing image in directory: " << cpp_strerror(r) << dendl; @@ -73,23 +153,103 @@ int Image::list_images(librados::IoCtx& io_ctx, ImageNameToIds *images) { return 0; } +template +int Image::get_parent(I *ictx, + librbd::linked_image_spec_t *parent_image, + librbd::snap_spec_t *parent_snap) { + auto cct = ictx->cct; + ldout(cct, 20) << "image_ctx=" << ictx << dendl; + + int r = ictx->state->refresh_if_required(); + if (r < 0) { + return r; + } + + RWLock::RLocker snap_locker(ictx->snap_lock); + RWLock::RLocker parent_locker(ictx->parent_lock); + if (ictx->parent == nullptr) { + return -ENOENT; + } + + cls::rbd::ParentImageSpec parent_spec; + if (ictx->snap_id == CEPH_NOSNAP) { + parent_spec = ictx->parent_md.spec; + } else { + r = ictx->get_parent_spec(ictx->snap_id, &parent_spec); + if (r < 0) { + lderr(cct) << "error looking up snapshot id " << ictx->snap_id << dendl; + return r; + } + if (parent_spec.pool_id == -1) { + return -ENOENT; + } + } + + parent_image->pool_id = parent_spec.pool_id; + parent_image->pool_name = ictx->parent->md_ctx.get_pool_name(); + parent_image->pool_namespace = ictx->parent->md_ctx.get_namespace(); + + RWLock::RLocker parent_snap_locker(ictx->parent->snap_lock); + parent_snap->id = parent_spec.snap_id; + parent_snap->namespace_type = RBD_SNAP_NAMESPACE_TYPE_USER; + if (parent_spec.snap_id != CEPH_NOSNAP) { + auto snap_info = ictx->parent->get_snap_info(parent_spec.snap_id); + if (snap_info == nullptr) { + lderr(cct) << "error finding parent snap name: " << cpp_strerror(r) + << dendl; + return -ENOENT; + } + + parent_snap->namespace_type = static_cast( + cls::rbd::get_snap_namespace_type(snap_info->snap_namespace)); + parent_snap->name = snap_info->name; + } + + parent_image->image_id = ictx->parent->id; + parent_image->image_name = ictx->parent->name; + parent_image->trash = true; + + librbd::trash_image_info_t trash_info; + r = Trash::get(ictx->parent->md_ctx, ictx->parent->id, + &trash_info); + if (r == -ENOENT || r == -EOPNOTSUPP) { + parent_image->trash = false; + } else if (r < 0) { + lderr(cct) << "error looking up trash status: " << cpp_strerror(r) + << dendl; + return r; + } + + return 0; +} + +template +int Image::list_children(I *ictx, + std::vector *images) { + RWLock::RLocker l(ictx->snap_lock); + cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(), + ictx->md_ctx.get_namespace(), + ictx->id, ictx->snap_id}; + return list_children(ictx, parent_spec, images); +} + template int Image::list_children(I *ictx, const cls::rbd::ParentImageSpec &parent_spec, - PoolImageIds *pool_image_ids) -{ + std::vector *images) { CephContext *cct = ictx->cct; + ldout(cct, 20) << "ictx=" << ictx << dendl; // no children for non-layered or old format image if (!ictx->test_features(RBD_FEATURE_LAYERING, ictx->snap_lock)) { return 0; } - pool_image_ids->clear(); + images->clear(); librados::Rados rados(ictx->md_ctx); - // search all pools for clone v1 children depending on this snapshot + // search all pools for clone v1 children dependent on this snapshot std::list > pools; int r = rados.pool_list2(pools); if (r < 0) { @@ -97,40 +257,43 @@ int Image::list_children(I *ictx, return r; } - for (auto it = pools.begin(); it != pools.end(); ++it) { + for (auto& it : pools) { int64_t base_tier; - r = rados.pool_get_base_tier(it->first, &base_tier); + r = rados.pool_get_base_tier(it.first, &base_tier); if (r == -ENOENT) { - ldout(cct, 1) << "pool " << it->second << " no longer exists" << dendl; + ldout(cct, 1) << "pool " << it.second << " no longer exists" << dendl; continue; } else if (r < 0) { - lderr(cct) << "error retrieving base tier for pool " << it->second + lderr(cct) << "error retrieving base tier for pool " << it.second << dendl; return r; } - if (it->first != base_tier) { + if (it.first != base_tier) { // pool is a cache; skip it continue; } IoCtx ioctx; - r = util::create_ioctx(ictx->md_ctx, "child image", it->first, {}, &ioctx); + r = util::create_ioctx(ictx->md_ctx, "child image", it.first, {}, &ioctx); if (r == -ENOENT) { continue; } else if (r < 0) { return r; } - set image_ids; + std::set image_ids; r = cls_client::get_children(&ioctx, RBD_CHILDREN, parent_spec, image_ids); if (r < 0 && r != -ENOENT) { - lderr(cct) << "error reading list of children from pool " << it->second + lderr(cct) << "error reading list of children from pool " << it.second << dendl; return r; } - pool_image_ids->insert({ - {it->first, it->second, ictx->md_ctx.get_namespace()}, image_ids}); + + for (auto& image_id : image_ids) { + images->push_back({ + it.first, "", ictx->md_ctx.get_namespace(), image_id, "", false}); + } } // retrieve clone v2 children attached to this snapshot @@ -151,18 +314,70 @@ int Image::list_children(I *ictx, } for (auto& child_image : child_images) { - IoCtx io_ctx; - r = util::create_ioctx(ictx->md_ctx, "child image", child_image.pool_id, - child_image.pool_namespace, &io_ctx); - if (r == -ENOENT) { - continue; + images->push_back({ + child_image.pool_id, "", child_image.pool_namespace, + child_image.image_id, "", false}); + } + + // batch lookups by pool + namespace + std::sort(images->begin(), images->end(), compare_by_pool); + + int64_t child_pool_id = -1; + librados::IoCtx child_io_ctx; + std::map> child_image_id_to_info; + for (auto& image : *images) { + if (child_pool_id == -1 || child_pool_id != image.pool_id || + child_io_ctx.get_namespace() != image.pool_namespace) { + r = util::create_ioctx(ictx->md_ctx, "child image", image.pool_id, + image.pool_namespace, &child_io_ctx); + if (r < 0) { + return r; + } + child_pool_id = image.pool_id; + + child_image_id_to_info.clear(); + + std::map image_names_to_ids; + r = list_images_v2(child_io_ctx, &image_names_to_ids); + if (r < 0) { + lderr(cct) << "error listing v2 images: " << cpp_strerror(r) << dendl; + return r; + } + + for (auto& it : image_names_to_ids) { + child_image_id_to_info[it.second] = {it.first, false}; + } + + std::vector trash_images; + r = Trash::list(child_io_ctx, trash_images); + if (r < 0 && r != -EOPNOTSUPP) { + lderr(cct) << "error listing trash images: " << cpp_strerror(r) + << dendl; + return r; + } + + for (auto& it : trash_images) { + child_image_id_to_info[it.id] = {it.name, true}; + } + } + + auto it = child_image_id_to_info.find(image.image_id); + if (it == child_image_id_to_info.end()) { + lderr(cct) << "error looking up name for image id " + << image.image_id << " in pool " + << child_io_ctx.get_pool_name() + << (image.pool_namespace.empty() ? + "" : "/" + image.pool_namespace) << dendl; + return -ENOENT; } - PoolSpec pool_spec = {child_image.pool_id, io_ctx.get_pool_name(), - child_image.pool_namespace}; - (*pool_image_ids)[pool_spec].insert(child_image.image_id); + image.pool_name = child_io_ctx.get_pool_name(); + image.image_name = it->second.first; + image.trash = it->second.second; } + // final sort by pool + image names + std::sort(images->begin(), images->end(), compare); return 0; } diff --git a/src/librbd/api/Image.h b/src/librbd/api/Image.h index a46104d5226..8c8ddbea27f 100644 --- a/src/librbd/api/Image.h +++ b/src/librbd/api/Image.h @@ -4,6 +4,7 @@ #ifndef LIBRBD_API_IMAGE_H #define LIBRBD_API_IMAGE_H +#include "include/rbd/librbd.hpp" #include "librbd/Types.h" #include #include @@ -22,19 +23,24 @@ namespace api { template struct Image { - typedef std::tuple PoolSpec; - typedef std::set ImageIds; - typedef std::map PoolImageIds; typedef std::map ImageNameToIds; static int get_op_features(ImageCtxT *ictx, uint64_t *op_features); static int list_images(librados::IoCtx& io_ctx, - ImageNameToIds *images); + std::vector *images); + static int list_images_v2(librados::IoCtx& io_ctx, + ImageNameToIds *images); + static int get_parent(ImageCtxT *ictx, + librbd::linked_image_spec_t *parent_image, + librbd::snap_spec_t *parent_snap); + + static int list_children(ImageCtxT *ictx, + std::vector *images); static int list_children(ImageCtxT *ictx, const cls::rbd::ParentImageSpec &parent_spec, - PoolImageIds *pool_image_ids); + std::vector *images); static int deep_copy(ImageCtxT *ictx, librados::IoCtx& dest_md_ctx, const char *destname, ImageOptions& opts, diff --git a/src/librbd/api/Mirror.cc b/src/librbd/api/Mirror.cc index f02503828c7..9fe240bc670 100644 --- a/src/librbd/api/Mirror.cc +++ b/src/librbd/api/Mirror.cc @@ -303,36 +303,44 @@ int Mirror::image_disable(I *ictx, bool force) { cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(), ictx->md_ctx.get_namespace(), ictx->id, info.first}; - map< tuple, set > image_info; - - r = Image::list_children(ictx, parent_spec, &image_info); + std::vector child_images; + r = Image::list_children(ictx, parent_spec, &child_images); if (r < 0) { rollback = true; return r; } - if (image_info.empty()) - continue; - for (auto &info: image_info) { - librados::IoCtx ioctx; - r = util::create_ioctx(ictx->md_ctx, "child image", - std::get<0>(info.first), - std::get<2>(info.first), &ioctx); - if (r < 0) { - rollback = true; - return r; - } + if (child_images.empty()) { + continue; + } - for (auto &id_it : info.second) { - cls::rbd::MirrorImage mirror_image_internal; - r = cls_client::mirror_image_get(&ioctx, id_it, - &mirror_image_internal); - if (r != -ENOENT) { + librados::IoCtx child_io_ctx; + int64_t child_pool_id = -1; + for (auto &child_image : child_images){ + std::string pool = child_image.pool_name; + if (child_pool_id == -1 || + child_pool_id != child_image.pool_id || + child_io_ctx.get_namespace() != child_image.pool_namespace) { + r = util::create_ioctx(ictx->md_ctx, "child image", + child_image.pool_id, + child_image.pool_namespace, + &child_io_ctx); + if (r < 0) { rollback = true; - lderr(cct) << "mirroring is enabled on one or more children " - << dendl; - return -EBUSY; + return r; } + + child_pool_id = child_image.pool_id; + } + + cls::rbd::MirrorImage mirror_image_internal; + r = cls_client::mirror_image_get(&child_io_ctx, child_image.image_id, + &mirror_image_internal); + if (r != -ENOENT) { + rollback = true; + lderr(cct) << "mirroring is enabled on one or more children " + << dendl; + return -EBUSY; } } } @@ -634,7 +642,7 @@ int Mirror::mode_set(librados::IoCtx& io_ctx, if (next_mirror_mode == cls::rbd::MIRROR_MODE_POOL) { map images; - r = Image::list_images(io_ctx, &images); + r = Image::list_images_v2(io_ctx, &images); if (r < 0) { lderr(cct) << "failed listing images: " << cpp_strerror(r) << dendl; return r; @@ -984,7 +992,7 @@ int Mirror::image_status_list(librados::IoCtx& io_ctx, map id_to_name; { map name_to_id; - r = Image::list_images(io_ctx, &name_to_id); + r = Image::list_images_v2(io_ctx, &name_to_id); if (r < 0) { return r; } diff --git a/src/librbd/api/Pool.cc b/src/librbd/api/Pool.cc index 6e362311995..533106d0e61 100644 --- a/src/librbd/api/Pool.cc +++ b/src/librbd/api/Pool.cc @@ -294,7 +294,7 @@ int Pool::get_stats(librados::IoCtx& io_ctx, StatOptions* stat_options) { if (image_count != nullptr || provisioned_bytes != nullptr || max_provisioned_bytes != nullptr || snapshot_count != nullptr) { typename Image::ImageNameToIds images; - int r = Image::list_images(io_ctx, &images); + int r = Image::list_images_v2(io_ctx, &images); if (r < 0) { return r; } diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc index 12d8da2f905..52a3ae9cc1a 100644 --- a/src/librbd/internal.cc +++ b/src/librbd/internal.cc @@ -113,16 +113,6 @@ 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 int detect_format(IoCtx &io_ctx, const string &name, @@ -520,45 +510,6 @@ bool compare_by_name(const child_info_t& c1, const child_info_t& c2) return (*opts_)->empty(); } - int list(IoCtx& io_ctx, vector& names) - { - CephContext *cct = (CephContext *)io_ctx.cct(); - ldout(cct, 20) << "list " << &io_ctx << dendl; - - bufferlist bl; - int r = io_ctx.read(RBD_DIRECTORY, bl, 0, 0); - if (r < 0) { - if (r == -ENOENT) { - r = 0; - } - return r; - } - - // old format images are in a tmap - if (bl.length()) { - auto p = bl.cbegin(); - bufferlist header; - map m; - decode(header, p); - decode(m, p); - for (map::iterator q = m.begin(); q != m.end(); ++q) { - names.push_back(q->first); - } - } - - map images; - r = api::Image<>::list_images(io_ctx, &images); - if (r < 0) { - lderr(cct) << "error listing v2 images: " << cpp_strerror(r) << dendl; - return r; - } - for (const auto& img_pair : images) { - names.push_back(img_pair.first); - } - - return 0; - } - int flatten_children(ImageCtx *ictx, const char* snap_name, ProgressContext& pctx) { @@ -577,135 +528,71 @@ bool compare_by_name(const child_info_t& c1, const child_info_t& c2) cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(), ictx->md_ctx.get_namespace(), ictx->id, snap_id}; - map< tuple, set > image_info; - - r = api::Image<>::list_children(ictx, parent_spec, &image_info); + std::vector child_images; + r = api::Image<>::list_children(ictx, parent_spec, &child_images); if (r < 0) { return r; } - size_t size = image_info.size(); - if (size == 0) + size_t size = child_images.size(); + if (size == 0) { return 0; + } + librados::IoCtx child_io_ctx; + int64_t child_pool_id = -1; size_t i = 0; - for ( auto &info : image_info){ - string pool = std::get<1>(info.first); - IoCtx ioctx; - r = util::create_ioctx(ictx->md_ctx, "child image", - std::get<0>(info.first), std::get<2>(info.first), - &ioctx); - if (r < 0) { - return r; - } - ioctx.set_namespace(std::get<2>(info.first)); - - for (auto &id_it : info.second) { - ImageCtx *imctx = new ImageCtx("", id_it, nullptr, ioctx, false); - int r = imctx->state->open(0); - if (r < 0) { - lderr(cct) << "error opening image: " - << cpp_strerror(r) << dendl; - return r; - } - - if ((imctx->features & RBD_FEATURE_DEEP_FLATTEN) == 0 && - !imctx->snaps.empty()) { - lderr(cct) << "snapshot in-use by " << pool << "/" << imctx->name - << dendl; - imctx->state->close(); - return -EBUSY; - } - - librbd::NoOpProgressContext prog_ctx; - r = imctx->operations->flatten(prog_ctx); - if (r < 0) { - lderr(cct) << "error flattening image: " << pool << "/" << id_it - << cpp_strerror(r) << dendl; - imctx->state->close(); - return r; - } - - r = imctx->state->close(); + for (auto &child_image : child_images){ + std::string pool = child_image.pool_name; + if (child_pool_id == -1 || + child_pool_id != child_image.pool_id || + child_io_ctx.get_namespace() != child_image.pool_namespace) { + r = util::create_ioctx(ictx->md_ctx, "child image", + child_image.pool_id, child_image.pool_namespace, + &child_io_ctx); if (r < 0) { - lderr(cct) << "failed to close image: " << cpp_strerror(r) << dendl; return r; } - } - pctx.update_progress(++i, size); - ceph_assert(i <= size); - } - - return 0; - } - - int list_children(ImageCtx *ictx, - vector *names) - { - CephContext *cct = ictx->cct; - ldout(cct, 20) << "children list " << ictx->name << dendl; - int r = ictx->state->refresh_if_required(); - if (r < 0) { - return r; - } + child_pool_id = child_image.pool_id; + } - RWLock::RLocker l(ictx->snap_lock); - cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(), - ictx->md_ctx.get_namespace(), - ictx->id, ictx->snap_id}; - map< tuple, set > image_info; + ImageCtx *imctx = new ImageCtx("", child_image.image_id, nullptr, + child_io_ctx, false); + r = imctx->state->open(0); + if (r < 0) { + lderr(cct) << "error opening image: " << cpp_strerror(r) << dendl; + return r; + } - r = api::Image<>::list_children(ictx, parent_spec, &image_info); - if (r < 0) { - return r; - } + if ((imctx->features & RBD_FEATURE_DEEP_FLATTEN) == 0 && + !imctx->snaps.empty()) { + lderr(cct) << "snapshot in-use by " << pool << "/" << imctx->name + << dendl; + imctx->state->close(); + return -EBUSY; + } - for (auto &info : image_info) { - IoCtx ioctx; - r = util::create_ioctx(ictx->md_ctx, "child image", - std::get<0>(info.first), std::get<2>(info.first), - &ioctx); + librbd::NoOpProgressContext prog_ctx; + r = imctx->operations->flatten(prog_ctx); if (r < 0) { + lderr(cct) << "error flattening image: " << pool << "/" + << (child_image.pool_namespace.empty() ? + "" : "/" + child_image.pool_namespace) + << child_image.image_name << cpp_strerror(r) << dendl; + imctx->state->close(); return r; } - for (auto &id_it : info.second) { - 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 " << std::get<1>(info.first) - << (std::get<2>(info.first).empty() ? - "" : "/" + std::get<2>(info.first)) << dendl; - return r; - } - - // TODO support namespaces - names->push_back( - child_info_t { - std::get<1>(info.first), - name, - id_it, - trash - }); + r = imctx->state->close(); + if (r < 0) { + lderr(cct) << "failed to close image: " << cpp_strerror(r) << dendl; + return r; } + + pctx.update_progress(++i, size); + ceph_assert(i <= size); } - std::sort(names->begin(), names->end(), compare_by_name); return 0; } @@ -1092,67 +979,6 @@ bool compare_by_name(const child_info_t& c1, const child_info_t& c2) return ictx->get_parent_overlap(ictx->snap_id, overlap); } - int get_parent_info(ImageCtx *ictx, string *parent_pool_name, - string *parent_name, string *parent_id, - string *parent_snap_name) - { - int r = ictx->state->refresh_if_required(); - if (r < 0) - return r; - - RWLock::RLocker l(ictx->snap_lock); - RWLock::RLocker l2(ictx->parent_lock); - if (ictx->parent == NULL) { - return -ENOENT; - } - - cls::rbd::ParentImageSpec parent_spec; - - if (ictx->snap_id == CEPH_NOSNAP) { - parent_spec = ictx->parent_md.spec; - } else { - r = ictx->get_parent_spec(ictx->snap_id, &parent_spec); - if (r < 0) { - lderr(ictx->cct) << "Can't find snapshot id = " << ictx->snap_id - << dendl; - return r; - } - if (parent_spec.pool_id == -1) - return -ENOENT; - } - if (parent_pool_name) { - Rados rados(ictx->md_ctx); - r = rados.pool_reverse_lookup(parent_spec.pool_id, - parent_pool_name); - if (r < 0) { - lderr(ictx->cct) << "error looking up pool name: " << cpp_strerror(r) - << dendl; - return r; - } - } - - if (parent_snap_name && parent_spec.snap_id != CEPH_NOSNAP) { - RWLock::RLocker l(ictx->parent->snap_lock); - r = ictx->parent->get_snap_name(parent_spec.snap_id, - parent_snap_name); - if (r < 0) { - lderr(ictx->cct) << "error finding parent snap name: " - << cpp_strerror(r) << dendl; - return r; - } - } - - if (parent_name) { - RWLock::RLocker snap_locker(ictx->parent->snap_lock); - *parent_name = ictx->parent->name; - } - if (parent_id) { - *parent_id = ictx->parent->id; - } - - return 0; - } - int get_flags(ImageCtx *ictx, uint64_t *flags) { int r = ictx->state->refresh_if_required(); diff --git a/src/librbd/internal.h b/src/librbd/internal.h index abb618d9971..1e1864f3a39 100644 --- a/src/librbd/internal.h +++ b/src/librbd/internal.h @@ -60,9 +60,6 @@ namespace librbd { void image_options_clear(rbd_image_options_t opts); bool image_options_is_empty(rbd_image_options_t opts); - int list(librados::IoCtx& io_ctx, std::vector& names); - int list_children(ImageCtx *ictx, - std::vector *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, @@ -88,9 +85,6 @@ namespace librbd { int get_size(ImageCtx *ictx, uint64_t *size); int get_features(ImageCtx *ictx, uint64_t *features); int get_overlap(ImageCtx *ictx, uint64_t *overlap); - int get_parent_info(ImageCtx *ictx, std::string *parent_pool_name, - std::string *parent_name, std::string *parent_id, - std::string *parent_snap_name); int get_flags(ImageCtx *ictx, uint64_t *flags); int set_image_notification(ImageCtx *ictx, int fd, int type); int is_exclusive_lock_owner(ImageCtx *ictx, bool *is_owner); diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 5f9aacd191d..12450be630f 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -677,13 +677,30 @@ namespace librbd { } int RBD::list(IoCtx& io_ctx, vector& names) + { + std::vector image_specs; + int r = list2(io_ctx, &image_specs); + if (r < 0) { + return r; + } + + names.clear(); + for (auto& it : image_specs) { + names.push_back(it.name); + } + return 0; + } + + int RBD::list2(IoCtx& io_ctx, std::vector *images) { TracepointProvider::initialize(get_cct(io_ctx)); - tracepoint(librbd, list_enter, io_ctx.get_pool_name().c_str(), io_ctx.get_id()); - int r = librbd::list(io_ctx, names); + tracepoint(librbd, list_enter, io_ctx.get_pool_name().c_str(), + io_ctx.get_id()); + + int r = librbd::api::Image<>::list_images(io_ctx, images); if (r >= 0) { - for (vector::iterator itr = names.begin(), end = names.end(); itr != end; ++itr) { - tracepoint(librbd, list_entry, itr->c_str()); + for (auto& it : *images) { + tracepoint(librbd, list_entry, it.name.c_str()); } } tracepoint(librbd, list_exit, r, r); @@ -1425,23 +1442,60 @@ namespace librbd { int Image::parent_info(string *parent_pool_name, string *parent_name, string *parent_snap_name) { - return parent_info2(parent_pool_name, parent_name, nullptr, - parent_snap_name); + librbd::linked_image_spec_t parent_image; + librbd::snap_spec_t parent_snap; + int r = get_parent(&parent_image, &parent_snap); + if (r >= 0) { + if (parent_pool_name != nullptr) { + *parent_pool_name = parent_image.pool_name; + } + if (parent_name != nullptr) { + *parent_name = parent_image.image_name; + } + if (parent_snap_name != nullptr) { + *parent_snap_name = parent_snap.name; + } + } + return r; } int Image::parent_info2(string *parent_pool_name, string *parent_name, string *parent_id, string *parent_snap_name) { - ImageCtx *ictx = (ImageCtx *)ctx; + librbd::linked_image_spec_t parent_image; + librbd::snap_spec_t parent_snap; + int r = get_parent(&parent_image, &parent_snap); + if (r >= 0) { + if (parent_pool_name != nullptr) { + *parent_pool_name = parent_image.pool_name; + } + if (parent_name != nullptr) { + *parent_name = parent_image.image_name; + } + if (parent_id != nullptr) { + *parent_id = parent_image.image_id; + } + if (parent_snap_name != nullptr) { + *parent_snap_name = parent_snap.name; + } + } + return r; + } + + int Image::get_parent(linked_image_spec_t *parent_image, + snap_spec_t *parent_snap) + { + auto ictx = reinterpret_cast(ctx); tracepoint(librbd, get_parent_info_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only); - int r = librbd::get_parent_info(ictx, parent_pool_name, parent_name, - parent_id, parent_snap_name); + + int r = librbd::api::Image<>::get_parent(ictx, parent_image, parent_snap); + tracepoint(librbd, get_parent_info_exit, r, - parent_pool_name ? parent_pool_name->c_str() : NULL, - parent_name ? parent_name->c_str() : NULL, - parent_id ? parent_id->c_str() : NULL, - parent_snap_name ? parent_snap_name->c_str() : NULL); + parent_image->pool_name.c_str(), + parent_image->image_name.c_str(), + parent_image->image_id.c_str(), + parent_snap->name.c_str()); return r; } @@ -1659,34 +1713,53 @@ namespace librbd { int Image::list_children(set > *children) { - ImageCtx *ictx = (ImageCtx *)ctx; - tracepoint(librbd, list_children_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only); - vector children2; - int r = librbd::list_children(ictx, &children2); - if (r >= 0) { - for (std::vector::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()); - } + std::vector images; + int r = list_children3(&images); + if (r < 0) { + return r; + } + + for (auto& image : images) { + if (!image.trash) { + children->insert({image.pool_name, image.image_name}); } } - tracepoint(librbd, list_children_exit, r); - return r; + return 0; } int Image::list_children2(vector *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); + std::vector images; + int r = list_children3(&images); + if (r < 0) { + return r; + } + + for (auto& image : images) { + children->push_back({ + .pool_name = image.pool_name, + .image_name = image.image_name, + .image_id = image.image_id, + .trash = image.trash}); + } + + return 0; + } + + int Image::list_children3(std::vector *images) + { + auto ictx = reinterpret_cast(ctx); + tracepoint(librbd, list_children_enter, ictx, ictx->name.c_str(), + ictx->snap_name.c_str(), ictx->read_only); + + int r = librbd::api::Image<>::list_children(ictx, images); if (r >= 0) { - for (std::vector::iterator it = children->begin(); - it != children->end(); ++it) { - tracepoint(librbd, list_children_entry, it->pool_name.c_str(), it->image_name.c_str()); + for (auto& it : *images) { + tracepoint(librbd, list_children_entry, it.pool_name.c_str(), + it.image_name.c_str()); } } + tracepoint(librbd, list_children_exit, r); return r; } @@ -2802,15 +2875,54 @@ extern "C" void rbd_mirror_image_instance_id_list_cleanup( } } +/* helpers */ + +extern "C" void rbd_image_spec_cleanup(rbd_image_spec_t *image) +{ + free(image->id); + free(image->name); +} + +extern "C" void rbd_image_spec_list_cleanup(rbd_image_spec_t *images, + size_t num_images) +{ + for (size_t idx = 0; idx < num_images; ++idx) { + rbd_image_spec_cleanup(&images[idx]); + } +} + +extern "C" void rbd_linked_image_spec_cleanup(rbd_linked_image_spec_t *image) +{ + free(image->pool_name); + free(image->pool_namespace); + free(image->image_id); + free(image->image_name); +} + +extern "C" void rbd_linked_image_spec_list_cleanup( + rbd_linked_image_spec_t *images, size_t num_images) +{ + for (size_t idx = 0; idx < num_images; ++idx) { + rbd_linked_image_spec_cleanup(&images[idx]); + } +} + +extern "C" void rbd_snap_spec_cleanup(rbd_snap_spec_t *snap) +{ + free(snap->name); +} + /* images */ extern "C" int rbd_list(rados_ioctx_t p, char *names, size_t *size) { librados::IoCtx io_ctx; librados::IoCtx::from_rados_ioctx_t(p, io_ctx); + TracepointProvider::initialize(get_cct(io_ctx)); - tracepoint(librbd, list_enter, io_ctx.get_pool_name().c_str(), io_ctx.get_id()); - vector cpp_names; - int r = librbd::list(io_ctx, cpp_names); + tracepoint(librbd, list_enter, io_ctx.get_pool_name().c_str(), + io_ctx.get_id()); + std::vector cpp_image_specs; + int r = librbd::api::Image<>::list_images(io_ctx, &cpp_image_specs); if (r < 0) { tracepoint(librbd, list_exit, r, *size); return r; @@ -2818,8 +2930,8 @@ extern "C" int rbd_list(rados_ioctx_t p, char *names, size_t *size) size_t expected_size = 0; - for (size_t i = 0; i < cpp_names.size(); i++) { - expected_size += cpp_names[i].size() + 1; + for (auto& it : cpp_image_specs) { + expected_size += it.name.size() + 1; } if (*size < expected_size) { *size = expected_size; @@ -2832,8 +2944,8 @@ extern "C" int rbd_list(rados_ioctx_t p, char *names, size_t *size) return -EINVAL; } - for (int i = 0; i < (int)cpp_names.size(); i++) { - const char* name = cpp_names[i].c_str(); + for (auto& it : cpp_image_specs) { + const char* name = it.name.c_str(); tracepoint(librbd, list_entry, name); strcpy(names, name); names += strlen(names) + 1; @@ -2842,6 +2954,38 @@ extern "C" int rbd_list(rados_ioctx_t p, char *names, size_t *size) return (int)expected_size; } +extern "C" int rbd_list2(rados_ioctx_t p, rbd_image_spec_t *images, + size_t *size) +{ + librados::IoCtx io_ctx; + librados::IoCtx::from_rados_ioctx_t(p, io_ctx); + + TracepointProvider::initialize(get_cct(io_ctx)); + tracepoint(librbd, list_enter, io_ctx.get_pool_name().c_str(), + io_ctx.get_id()); + std::vector cpp_image_specs; + int r = librbd::api::Image<>::list_images(io_ctx, &cpp_image_specs); + if (r < 0) { + tracepoint(librbd, list_exit, r, *size); + return r; + } + + size_t expected_size = cpp_image_specs.size(); + if (*size < expected_size) { + *size = expected_size; + tracepoint(librbd, list_exit, -ERANGE, *size); + return -ERANGE; + } + + *size = expected_size; + for (size_t idx = 0; idx < expected_size; ++idx) { + images[idx].id = strdup(cpp_image_specs[idx].id.c_str()); + images[idx].name = strdup(cpp_image_specs[idx].name.c_str()); + } + tracepoint(librbd, list_exit, 0, *size); + return 0; +} + extern "C" int rbd_create(rados_ioctx_t p, const char *name, uint64_t size, int *order) { librados::IoCtx io_ctx; @@ -3985,9 +4129,48 @@ extern "C" int rbd_get_parent_info(rbd_image_t image, char *parent_name, size_t pnamelen, char *parent_snap_name, size_t psnap_namelen) { - return rbd_get_parent_info2(image, parent_pool_name, ppool_namelen, - parent_name, pnamelen, nullptr, 0, - parent_snap_name, psnap_namelen); + auto ictx = reinterpret_cast(image); + tracepoint(librbd, get_parent_info_enter, ictx, ictx->name.c_str(), + ictx->snap_name.c_str(), ictx->read_only); + + librbd::linked_image_spec_t parent_image; + librbd::snap_spec_t parent_snap; + int r = librbd::api::Image<>::get_parent(ictx, &parent_image, &parent_snap); + if (r >= 0) { + if (parent_pool_name) { + if (parent_image.pool_name.length() + 1 > ppool_namelen) { + r = -ERANGE; + } else { + strcpy(parent_pool_name, parent_image.pool_name.c_str()); + } + } + if (parent_name) { + if (parent_image.image_name.length() + 1 > pnamelen) { + r = -ERANGE; + } else { + strcpy(parent_name, parent_image.image_name.c_str()); + } + } + if (parent_snap_name) { + if (parent_snap.name.length() + 1 > psnap_namelen) { + r = -ERANGE; + } else { + strcpy(parent_snap_name, parent_snap.name.c_str()); + } + } + } + + if (r < 0) { + tracepoint(librbd, get_parent_info_exit, r, NULL, NULL, NULL, NULL); + return r; + } + + tracepoint(librbd, get_parent_info_exit, r, + parent_image.pool_name.c_str(), + parent_image.image_name.c_str(), + parent_image.image_id.c_str(), + parent_snap.name.c_str()); + return 0; } extern "C" int rbd_get_parent_info2(rbd_image_t image, @@ -3998,56 +4181,94 @@ extern "C" int rbd_get_parent_info2(rbd_image_t image, char *parent_snap_name, size_t psnap_namelen) { - librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; + auto ictx = reinterpret_cast(image); tracepoint(librbd, get_parent_info_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only); - string p_pool_name, p_name, p_id, p_snap_name; - - int r = librbd::get_parent_info(ictx, &p_pool_name, &p_name, &p_id, - &p_snap_name); - if (r < 0) { - tracepoint(librbd, get_parent_info_exit, r, NULL, NULL, NULL, NULL); - return r; - } - if (parent_pool_name) { - if (p_pool_name.length() + 1 > ppool_namelen) { - tracepoint(librbd, get_parent_info_exit, -ERANGE, NULL, NULL, NULL, NULL); - return -ERANGE; + librbd::linked_image_spec_t parent_image; + librbd::snap_spec_t parent_snap; + int r = librbd::api::Image<>::get_parent(ictx, &parent_image, &parent_snap); + if (r >= 0) { + if (parent_pool_name) { + if (parent_image.pool_name.length() + 1 > ppool_namelen) { + r = -ERANGE; + } else { + strcpy(parent_pool_name, parent_image.pool_name.c_str()); + } } - - strcpy(parent_pool_name, p_pool_name.c_str()); - } - if (parent_name) { - if (p_name.length() + 1 > pnamelen) { - tracepoint(librbd, get_parent_info_exit, -ERANGE, NULL, NULL, NULL, NULL); - return -ERANGE; + if (parent_name) { + if (parent_image.image_name.length() + 1 > pnamelen) { + r = -ERANGE; + } else { + strcpy(parent_name, parent_image.image_name.c_str()); + } } - - strcpy(parent_name, p_name.c_str()); + if (parent_id) { + if (parent_image.image_id.length() + 1 > pidlen) { + r = -ERANGE; + } else { + strcpy(parent_id, parent_image.image_id.c_str()); + } } - if (parent_id) { - if (p_id.length() + 1 > pidlen) { - tracepoint(librbd, get_parent_info_exit, -ERANGE, NULL, NULL, NULL, NULL); - return -ERANGE; + if (parent_snap_name) { + if (parent_snap.name.length() + 1 > psnap_namelen) { + r = -ERANGE; + } else { + strcpy(parent_snap_name, parent_snap.name.c_str()); + } } - - strcpy(parent_id, p_id.c_str()); } - if (parent_snap_name) { - if (p_snap_name.length() + 1 > psnap_namelen) { - tracepoint(librbd, get_parent_info_exit, -ERANGE, NULL, NULL, NULL, NULL); - return -ERANGE; - } - strcpy(parent_snap_name, p_snap_name.c_str()); + if (r < 0) { + tracepoint(librbd, get_parent_info_exit, r, NULL, NULL, NULL, NULL); + return r; } - tracepoint(librbd, get_parent_info_exit, 0, parent_pool_name, parent_name, - parent_id, parent_snap_name); + tracepoint(librbd, get_parent_info_exit, r, + parent_image.pool_name.c_str(), + parent_image.image_name.c_str(), + parent_image.image_id.c_str(), + parent_snap.name.c_str()); return 0; } +extern "C" int rbd_get_parent(rbd_image_t image, + rbd_linked_image_spec_t *parent_image, + rbd_snap_spec_t *parent_snap) +{ + auto ictx = reinterpret_cast(image); + tracepoint(librbd, get_parent_info_enter, ictx, ictx->name.c_str(), + ictx->snap_name.c_str(), ictx->read_only); + + librbd::linked_image_spec_t cpp_parent_image; + librbd::snap_spec_t cpp_parent_snap; + int r = librbd::api::Image<>::get_parent(ictx, &cpp_parent_image, + &cpp_parent_snap); + if (r < 0) { + memset(parent_image, 0, sizeof(rbd_linked_image_spec_t)); + memset(parent_snap, 0, sizeof(rbd_snap_spec_t)); + } else { + *parent_image = { + .pool_id = cpp_parent_image.pool_id, + .pool_name = strdup(cpp_parent_image.pool_name.c_str()), + .pool_namespace = strdup(cpp_parent_image.pool_namespace.c_str()), + .image_id = strdup(cpp_parent_image.image_id.c_str()), + .image_name = strdup(cpp_parent_image.image_name.c_str()), + .trash = cpp_parent_image.trash}; + *parent_snap = { + .id = cpp_parent_snap.id, + .namespace_type = cpp_parent_snap.namespace_type, + .name = strdup(cpp_parent_snap.name.c_str())}; + } + + tracepoint(librbd, get_parent_info_exit, r, + parent_image->pool_name, + parent_image->image_name, + parent_image->image_id, + parent_snap->name); + return r; +} + extern "C" int rbd_get_flags(rbd_image_t image, uint64_t *flags) { librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; @@ -4379,30 +4600,29 @@ extern "C" ssize_t rbd_list_children(rbd_image_t image, char *pools, size_t *pools_len, char *images, size_t *images_len) { - librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; - tracepoint(librbd, list_children_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only); - set > image_set; - vector children; + auto ictx = reinterpret_cast(image); + 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); + std::vector cpp_images; + int r = librbd::api::Image<>::list_children(ictx, &cpp_images); if (r < 0) { tracepoint(librbd, list_children_exit, r); return r; } - for (std::vector::iterator it = children.begin(); - it != children.end(); ++it) { - if (!it->trash) { - image_set.insert(make_pair(it->pool_name, it->image_name)); + std::set> image_set; + for (auto& image : cpp_images) { + if (!image.trash) { + image_set.insert({image.pool_name, image.image_name}); } } size_t pools_total = 0; size_t images_total = 0; - for (set >::const_iterator it = image_set.begin(); - it != image_set.end(); ++it) { - pools_total += it->first.length() + 1; - images_total += it->second.length() + 1; + for (auto it : image_set) { + pools_total += it.first.length() + 1; + images_total += it.second.length() + 1; } bool too_short = false; @@ -4419,14 +4639,13 @@ extern "C" ssize_t rbd_list_children(rbd_image_t image, char *pools, char *pools_p = pools; char *images_p = images; - for (set >::const_iterator it = image_set.begin(); - it != image_set.end(); ++it) { - const char* pool = it->first.c_str(); + for (auto it : image_set) { + const char* pool = it.first.c_str(); strcpy(pools_p, pool); - pools_p += it->first.length() + 1; - const char* image = it->second.c_str(); + pools_p += it.first.length() + 1; + const char* image = it.second.c_str(); strcpy(images_p, image); - images_p += it->second.length() + 1; + images_p += it.second.length() + 1; tracepoint(librbd, list_children_entry, pool, image); } @@ -4439,24 +4658,22 @@ extern "C" int rbd_list_children2(rbd_image_t image, rbd_child_info_t *children, int *max_children) { - vector 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); + auto ictx = reinterpret_cast(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; - } + std::vector cpp_children; + int r = librbd::api::Image<>::list_children(ictx, &cpp_children); if (r < 0) { - tracepoint(librbd, list_children_exit, *max_children); + tracepoint(librbd, list_children_exit, r); return r; } + if (*max_children < (int)cpp_children.size() + 1) { *max_children = (int)cpp_children.size() + 1; tracepoint(librbd, list_children_exit, *max_children); @@ -4469,13 +4686,8 @@ extern "C" int rbd_list_children2(rbd_image_t image, 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); + tracepoint(librbd, list_children_entry, children[i].pool_name, + children[i].image_name); } children[i].pool_name = NULL; children[i].image_name = NULL; @@ -4497,8 +4709,45 @@ 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]); + free((void *)children[i].pool_name); + free((void *)children[i].image_name); + free((void *)children[i].image_id); + } +} + +extern "C" int rbd_list_children3(rbd_image_t image, + rbd_linked_image_spec_t *images, + size_t *max_images) +{ + auto ictx = reinterpret_cast(image); + tracepoint(librbd, list_children_enter, ictx, ictx->name.c_str(), + ictx->snap_name.c_str(), ictx->read_only); + + std::vector cpp_children; + int r = librbd::api::Image<>::list_children(ictx, &cpp_children); + if (r < 0) { + tracepoint(librbd, list_children_exit, r); + return r; } + + if (*max_images < cpp_children.size()) { + *max_images = cpp_children.size(); + return -ERANGE; + } + + *max_images = cpp_children.size(); + for (size_t idx = 0; idx < cpp_children.size(); ++idx) { + images[idx] = { + .pool_id = cpp_children[idx].pool_id, + .pool_name = strdup(cpp_children[idx].pool_name.c_str()), + .pool_namespace = strdup(cpp_children[idx].pool_namespace.c_str()), + .image_id = strdup(cpp_children[idx].image_id.c_str()), + .image_name = strdup(cpp_children[idx].image_name.c_str()), + .trash = cpp_children[idx].trash}; + tracepoint(librbd, list_children_entry, images[idx].pool_name, + images[idx].image_name); + } + return 0; } extern "C" ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive, diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index 69f4493fb81..78b1c4c0c8d 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -114,12 +114,28 @@ cdef extern from "rbd/librbd.h" nogil: char *name int64_t pool - ctypedef struct rbd_child_info_t: + ctypedef struct rbd_image_spec_t: + char *id + char *name + + ctypedef struct rbd_linked_image_spec_t: + int64_t pool_id char *pool_name - char *image_name + char *pool_namespace char *image_id + char *image_name bint trash + ctypedef enum rbd_snap_namespace_type_t: + _RBD_SNAP_NAMESPACE_TYPE_USER "RBD_SNAP_NAMESPACE_TYPE_USER" + _RBD_SNAP_NAMESPACE_TYPE_GROUP "RBD_SNAP_NAMESPACE_TYPE_GROUP" + _RBD_SNAP_NAMESPACE_TYPE_TRASH "RBD_SNAP_NAMESPACE_TYPE_TRASH" + + ctypedef struct rbd_snap_spec_t: + uint64_t id + rbd_snap_namespace_type_t namespace_type + char *name + ctypedef enum rbd_mirror_mode_t: _RBD_MIRROR_MODE_DISABLED "RBD_MIRROR_MODE_DISABLED" _RBD_MIRROR_MODE_IMAGE "RBD_MIRROR_MODE_IMAGE" @@ -160,11 +176,6 @@ cdef extern from "rbd/librbd.h" nogil: time_t last_update bint up - ctypedef enum rbd_snap_namespace_type_t: - _RBD_SNAP_NAMESPACE_TYPE_USER "RBD_SNAP_NAMESPACE_TYPE_USER" - _RBD_SNAP_NAMESPACE_TYPE_GROUP "RBD_SNAP_NAMESPACE_TYPE_GROUP" - _RBD_SNAP_NAMESPACE_TYPE_TRASH "RBD_SNAP_NAMESPACE_TYPE_TRASH" - ctypedef enum rbd_lock_mode_t: _RBD_LOCK_MODE_EXCLUSIVE "RBD_LOCK_MODE_EXCLUSIVE" _RBD_LOCK_MODE_SHARED "RBD_LOCK_MODE_SHARED" @@ -248,6 +259,12 @@ cdef extern from "rbd/librbd.h" nogil: void rbd_version(int *major, int *minor, int *extra) + void rbd_image_spec_list_cleanup(rbd_image_spec_t *image, size_t num_images) + void rbd_linked_image_spec_cleanup(rbd_linked_image_spec_t *image) + void rbd_linked_image_spec_list_cleanup(rbd_linked_image_spec_t *images, + size_t num_images) + void rbd_snap_spec_cleanup(rbd_snap_spec_t *snap) + void rbd_image_options_create(rbd_image_options_t* opts) void rbd_image_options_destroy(rbd_image_options_t opts) int rbd_image_options_set_string(rbd_image_options_t opts, int optname, @@ -263,6 +280,8 @@ cdef extern from "rbd/librbd.h" nogil: int rbd_image_options_is_empty(rbd_image_options_t opts) int rbd_list(rados_ioctx_t io, char *names, size_t *size) + int rbd_list2(rados_ioctx_t io, rbd_image_spec_t *images, + size_t *num_images) int rbd_create(rados_ioctx_t io, const char *name, uint64_t size, int *order) int rbd_create4(rados_ioctx_t io, const char *name, uint64_t size, @@ -379,11 +398,9 @@ cdef extern from "rbd/librbd.h" nogil: int rbd_get_block_name_prefix(rbd_image_t image, char *prefix, size_t prefix_len) int64_t rbd_get_data_pool_id(rbd_image_t image) - int rbd_get_parent_info2(rbd_image_t image, - char *parent_poolname, size_t ppoolnamelen, - char *parent_name, size_t pnamelen, - char *parent_id, size_t pidlen, - char *parent_snapname, size_t psnapnamelen) + int rbd_get_parent(rbd_image_t image, + rbd_linked_image_spec_t *parent_image, + rbd_snap_spec_t *parent_snap) int rbd_get_flags(rbd_image_t image, uint64_t *flags) int rbd_get_group(rbd_image_t image, rbd_group_info_t *group_info, size_t group_info_size) @@ -428,16 +445,12 @@ cdef extern from "rbd/librbd.h" nogil: int rbd_snap_get_trash_namespace(rbd_image_t image, uint64_t snap_id, char *original_name, size_t max_length) - int rbd_flatten(rbd_image_t image) int rbd_rebuild_object_map(rbd_image_t image, librbd_progress_fn_t cb, 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) + int rbd_list_children3(rbd_image_t image, rbd_linked_image_spec_t *children, + size_t *max_children) + ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive, char *tag, size_t *tag_len, char *clients, size_t *clients_len, @@ -1141,6 +1154,16 @@ class RBD(object): finally: free(c_names) + def list2(self, ioctx): + """ + Iterate over the images in the pool. + + :param ioctx: determines which RADOS pool the image is in + :type ioctx: :class:`rados.Ioctx` + :returns: :class:`ImageIterator` + """ + return ImageIterator(ioctx) + def remove(self, ioctx, name): """ Delete an RBD image. This may take a long time, since it does @@ -2796,29 +2819,20 @@ cdef class Image(object): :raises: :class:`ImageNotFound` if the image doesn't have a parent """ cdef: - int ret = -errno.ERANGE - size_t size = 8 - char *pool = NULL - char *name = NULL - char *snapname = NULL - try: - while ret == -errno.ERANGE and size <= 4096: - pool = realloc_chk(pool, size) - name = realloc_chk(name, size) - snapname = realloc_chk(snapname, size) - with nogil: - ret = rbd_get_parent_info2(self.image, pool, size, name, - size, NULL, 0, snapname, size) - if ret == -errno.ERANGE: - size *= 2 + rbd_linked_image_spec_t parent_spec + rbd_snap_spec_t snap_spec + with nogil: + ret = rbd_get_parent(self.image, &parent_spec, &snap_spec) + if ret != 0: + raise make_ex(ret, 'error getting parent info for image %s' % self.name) - if ret != 0: - raise make_ex(ret, 'error getting parent info for image %s' % self.name) - return (decode_cstr(pool), decode_cstr(name), decode_cstr(snapname)) - finally: - free(pool) - free(name) - free(snapname) + result = (decode_cstr(parent_spec.pool_name), + decode_cstr(parent_spec.image_name), + decode_cstr(snap_spec.name)) + + rbd_linked_image_spec_cleanup(&parent_spec) + rbd_snap_spec_cleanup(&snap_spec) + return result def parent_id(self): """ @@ -2828,23 +2842,18 @@ cdef class Image(object): :raises: :class:`ImageNotFound` if the image doesn't have a parent """ cdef: - int ret = -errno.ERANGE - size_t size = 32 - char *parent_id = NULL - try: - while ret == -errno.ERANGE and size <= 4096: - parent_id = realloc_chk(parent_id, size) - with nogil: - ret = rbd_get_parent_info2(self.image, NULL, 0, NULL, 0, - parent_id, size, NULL, 0) - if ret == -errno.ERANGE: - size *= 2 + rbd_linked_image_spec_t parent_spec + rbd_snap_spec_t snap_spec + with nogil: + ret = rbd_get_parent(self.image, &parent_spec, &snap_spec) + if ret != 0: + raise make_ex(ret, 'error getting parent info for image %s' % self.name) - if ret != 0: - raise make_ex(ret, 'error getting parent id for image %s' % self.name) - return decode_cstr(parent_id) - finally: - free(parent_id) + result = decode_cstr(parent_spec.image_id) + + rbd_linked_image_spec_cleanup(&parent_spec) + rbd_snap_spec_cleanup(&snap_spec) + return result def old_format(self): """ @@ -3598,28 +3607,27 @@ written." % (self.name, ret, length)) :returns: list - a list of (pool name, image name) tuples """ cdef: - size_t pools_size = 512, images_size = 512 - char *c_pools = NULL - char *c_images = NULL + rbd_linked_image_spec_t *children = NULL + size_t num_children = 10 + try: while True: - c_pools = realloc_chk(c_pools, pools_size) - c_images = realloc_chk(c_images, images_size) + children = realloc_chk( + children, num_children * sizeof(rbd_linked_image_spec_t)) with nogil: - ret = rbd_list_children(self.image, c_pools, &pools_size, - c_images, &images_size) + ret = rbd_list_children3(self.image, children, + &num_children) if ret >= 0: break elif ret != -errno.ERANGE: - raise make_ex(ret, 'error listing images') - if ret == 0: - return [] - pools = map(decode_cstr, c_pools[:pools_size - 1].split(b'\0')) - images = map(decode_cstr, c_images[:images_size - 1].split(b'\0')) - return list(zip(pools, images)) + raise make_ex(ret, 'error listing children.') + + return [(decode_cstr(x.pool_name), decode_cstr(x.image_name)) for x + in children[:num_children] if not x.trash] finally: - free(c_pools) - free(c_images) + if children: + rbd_linked_image_spec_list_cleanup(children, num_children) + free(children) def list_children2(self): """ @@ -4254,6 +4262,49 @@ written." % (self.name, ret, length)) free(_name) +cdef class ImageIterator(object): + """ + Iterator over RBD images in a pool + + Yields a dictionary containing information about the images + + Keys are: + + * ``id`` (str) - image id + + * ``name`` (str) - image name + """ + cdef rados_ioctx_t ioctx + cdef rbd_image_spec_t *images + cdef size_t num_images + + def __init__(self, ioctx): + self.ioctx = convert_ioctx(ioctx) + self.images = NULL + self.num_images = 32 + while True: + self.images = realloc_chk( + self.images, self.num_images * sizeof(rbd_image_spec_t)) + with nogil: + ret = rbd_list2(self.ioctx, self.images, &self.num_images) + if ret >= 0: + break + elif ret != -errno.ERANGE: + raise make_ex(ret, 'error listing images.') + + def __iter__(self): + for i in range(self.num_images): + yield { + 'id' : decode_cstr(self.images[i].id), + 'name' : decode_cstr(self.images[i].name) + } + + def __dealloc__(self): + if self.images: + rbd_image_spec_list_cleanup(self.images, self.num_images) + free(self.images) + + cdef class LockOwnerIterator(object): """ Iterator over managed lock owners for an image @@ -4506,6 +4557,8 @@ cdef class ChildIterator(object): * ``pool`` (str) - name of the pool + * ``pool_namespace`` (str) - namespace of the pool + * ``image`` (str) - name of the child * ``id`` (str) - id of the child @@ -4513,8 +4566,8 @@ cdef class ChildIterator(object): * ``trash`` (bool) - True if child is in trash bin """ - cdef rbd_child_info_t *children - cdef int num_children + cdef rbd_linked_image_spec_t *children + cdef size_t num_children cdef object image def __init__(self, Image image): @@ -4522,13 +4575,11 @@ cdef class ChildIterator(object): self.children = NULL self.num_children = 10 while True: - self.children = realloc_chk(self.children, - self.num_children * - sizeof(rbd_child_info_t)) + self.children = realloc_chk( + self.children, self.num_children * sizeof(rbd_linked_image_spec_t)) with nogil: - ret = rbd_list_children2(image.image, self.children, &self.num_children) + ret = rbd_list_children3(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.') @@ -4536,15 +4587,16 @@ cdef class ChildIterator(object): 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 + 'pool' : decode_cstr(self.children[i].pool_name), + 'pool_namespace' : decode_cstr(self.children[i].pool_namespace), + '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) + rbd_linked_image_spec_list_cleanup(self.children, self.num_children) free(self.children) cdef class WatcherIterator(object): diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index ea679360301..bbc4777268c 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -54,6 +54,10 @@ #include #endif +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + using namespace std; using std::chrono::seconds; @@ -6131,6 +6135,20 @@ TEST_F(TestLibRBD, ImagePollIO) namespace librbd { +static bool operator==(const image_spec_t &lhs, const image_spec_t &rhs) { + return (lhs.id == rhs.id && lhs.name == rhs.name); +} + +static bool operator==(const linked_image_spec_t &lhs, + const linked_image_spec_t &rhs) { + return (lhs.pool_id == rhs.pool_id && + lhs.pool_name == rhs.pool_name && + lhs.pool_namespace == rhs.pool_namespace && + lhs.image_id == rhs.image_id && + lhs.image_name == rhs.image_name && + lhs.trash == rhs.trash); +} + static bool operator==(const mirror_peer_t &lhs, const mirror_peer_t &rhs) { return (lhs.uuid == rhs.uuid && lhs.cluster_name == rhs.cluster_name && @@ -7473,6 +7491,85 @@ TEST_F(TestLibRBD, PoolStatsPP) ASSERT_EQ(1U, trash_snap_count); } +TEST_F(TestLibRBD, ImageSpec) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx)); + + librbd::RBD rbd; + librbd::Image parent_image; + std::string name = get_temp_image_name(); + + uint64_t size = 1; + int order = 0; + + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order)); + ASSERT_EQ(0, rbd.open(ioctx, parent_image, name.c_str(), NULL)); + + std::string parent_id; + ASSERT_EQ(0, parent_image.get_id(&parent_id)); + + uint64_t features; + ASSERT_EQ(0, parent_image.features(&features)); + + ASSERT_EQ(0, parent_image.snap_create("snap")); + ASSERT_EQ(0, parent_image.snap_protect("snap")); + + std::string clone_name = this->get_temp_image_name(); + ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "snap", ioctx, clone_name.c_str(), + features, &order)); + + librbd::Image clone_image; + ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL)); + + std::string clone_id; + ASSERT_EQ(0, clone_image.get_id(&clone_id)); + + std::vector images; + ASSERT_EQ(0, rbd.list2(ioctx, &images)); + + std::vector expected_images{ + {.id = parent_id, .name = name}, + {.id = clone_id, .name = clone_name} + }; + std::sort(expected_images.begin(), expected_images.end(), + [](const librbd::image_spec_t& lhs, const librbd::image_spec_t &rhs) { + return lhs.name < rhs.name; + }); + ASSERT_EQ(expected_images, images); + + librbd::linked_image_spec_t parent_image_spec; + librbd::snap_spec_t parent_snap_spec; + ASSERT_EQ(0, clone_image.get_parent(&parent_image_spec, &parent_snap_spec)); + + librbd::linked_image_spec_t expected_parent_image_spec{ + .pool_id = ioctx.get_id(), + .pool_name = ioctx.get_pool_name(), + .pool_namespace = ioctx.get_namespace(), + .image_id = parent_id, + .image_name = name, + .trash = false + }; + ASSERT_EQ(expected_parent_image_spec, parent_image_spec); + ASSERT_EQ(RBD_SNAP_NAMESPACE_TYPE_USER, parent_snap_spec.namespace_type); + ASSERT_EQ("snap", parent_snap_spec.name); + + std::vector children; + ASSERT_EQ(0, parent_image.list_children3(&children)); + + std::vector expected_children{ + { + .pool_id = ioctx.get_id(), + .pool_name = ioctx.get_pool_name(), + .pool_namespace = ioctx.get_namespace(), + .image_id = clone_id, + .image_name = clone_name, + .trash = false + } + }; +} + // poorman's ceph_assert() namespace ceph { void __ceph_assert_fail(const char *assertion, const char *file, int line, @@ -7480,3 +7577,6 @@ namespace ceph { ceph_abort(); } } + +#pragma GCC diagnostic pop +#pragma GCC diagnostic warning "-Wpragmas" diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index 02362c1bd71..510364edb6a 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -320,6 +320,10 @@ def test_list_empty(): def test_list(): eq([image_name], RBD().list(ioctx)) + with Image(ioctx, image_name) as image: + image_id = image.id() + eq([{'id': image_id, 'name': image_name}], list(RBD().list2(ioctx))) + @with_setup(create_image, remove_image) def test_rename(): rbd = RBD() @@ -1334,7 +1338,8 @@ class TestClone(object): 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, + [{'pool': pool_name, 'pool_namespace': '', + '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) @@ -1349,7 +1354,8 @@ class TestClone(object): 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, + {'pool': pool_name, 'pool_namespace': '', + '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) @@ -1386,7 +1392,8 @@ class TestClone(object): features) self.check_children([(pool_name, self.clone_name)]) self.check_children2( - [{'pool': pool_name, 'image': self.clone_name, 'trash': False, + [{'pool': pool_name, 'pool_namespace': '', + 'image': self.clone_name, 'trash': False, 'id': self.get_image_id(ioctx, self.clone_name)}]) self.clone = Image(ioctx, self.clone_name) -- 2.39.5