From 595147963ecd478cd714512c46dae74ce7ce8652 Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Tue, 4 Sep 2018 13:18:48 +0300 Subject: [PATCH] librbd: API for listing pool and image level config overrides Signed-off-by: Mykola Golub --- src/include/rbd/librbd.h | 24 ++++ src/include/rbd/librbd.hpp | 12 ++ src/librbd/CMakeLists.txt | 1 + src/librbd/api/Config.cc | 221 +++++++++++++++++++++++++++++++++ src/librbd/api/Config.h | 37 ++++++ src/librbd/librbd.cc | 83 +++++++++++++ src/pybind/rbd/rbd.pyx | 137 ++++++++++++++++++++ src/test/librbd/test_librbd.cc | 163 ++++++++++++++++++++++++ src/test/pybind/test_rbd.py | 40 +++++- 9 files changed, 717 insertions(+), 1 deletion(-) create mode 100644 src/librbd/api/Config.cc create mode 100644 src/librbd/api/Config.h diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index 35a81ff399869..4c381614533c1 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -250,6 +250,18 @@ typedef struct { char *state_description; } rbd_image_migration_status_t; +typedef enum { + RBD_CONFIG_SOURCE_CONFIG = 0, + RBD_CONFIG_SOURCE_POOL = 1, + RBD_CONFIG_SOURCE_IMAGE = 2, +} rbd_config_source_t; + +typedef struct { + char *name; + char *value; + rbd_config_source_t source; +} rbd_config_option_t; + CEPH_RBD_API void rbd_image_options_create(rbd_image_options_t* opts); CEPH_RBD_API void rbd_image_options_destroy(rbd_image_options_t opts); CEPH_RBD_API int rbd_image_options_set_string(rbd_image_options_t opts, @@ -403,6 +415,12 @@ CEPH_RBD_API int rbd_pool_metadata_list(rados_ioctx_t io_ctx, const char *start, size_t *key_len, char *values, size_t *vals_len); +CEPH_RBD_API int rbd_config_pool_list(rados_ioctx_t io_ctx, + rbd_config_option_t *options, + int *max_options); +CEPH_RBD_API void rbd_config_pool_list_cleanup(rbd_config_option_t *options, + int max_options); + CEPH_RBD_API int rbd_open(rados_ioctx_t io, const char *name, rbd_image_t *image, const char *snap_name); CEPH_RBD_API int rbd_open_by_id(rados_ioctx_t io, const char *id, @@ -1011,6 +1029,12 @@ CEPH_RBD_API int rbd_watchers_list(rbd_image_t image, CEPH_RBD_API void rbd_watchers_list_cleanup(rbd_image_watcher_t *watchers, size_t num_watchers); +CEPH_RBD_API int rbd_config_image_list(rbd_image_t image, + rbd_config_option_t *options, + int *max_options); +CEPH_RBD_API void rbd_config_image_list_cleanup(rbd_config_option_t *options, + int max_options); + CEPH_RBD_API int rbd_group_image_add(rados_ioctx_t group_p, const char *group_name, rados_ioctx_t image_p, diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index 980a331fa2712..1447484a7e72a 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -141,6 +141,14 @@ namespace librbd { std::string state_description; } image_migration_status_t; + typedef rbd_config_source_t config_source_t; + + typedef struct { + std::string name; + std::string value; + config_source_t source; + } config_option_t; + class CEPH_RBD_API RBD { public: @@ -286,6 +294,8 @@ public: int pool_metadata_list(IoCtx &io_ctx, const std::string &start, uint64_t max, std::map *pairs); + int config_list(IoCtx& io_ctx, std::vector *options); + private: /* We don't allow assignment or copying */ RBD(const RBD& rhs); @@ -570,6 +580,8 @@ public: int list_watchers(std::list &watchers); + int config_list(std::vector *options); + private: friend class RBD; diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index af423df862284..b6f38f219cfac 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -24,6 +24,7 @@ set(librbd_internal_srcs TrashWatcher.cc Utils.cc Watcher.cc + api/Config.cc api/DiffIterate.cc api/Group.cc api/Image.cc diff --git a/src/librbd/api/Config.cc b/src/librbd/api/Config.cc new file mode 100644 index 0000000000000..f7d1cb4df3669 --- /dev/null +++ b/src/librbd/api/Config.cc @@ -0,0 +1,221 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/api/Config.h" +#include "cls/rbd/cls_rbd_client.h" +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/ImageCtx.h" +#include "librbd/Utils.h" +#include "librbd/api/PoolMetadata.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::Config: " << __func__ << ": " + +namespace librbd { +namespace api { + +namespace { + +const uint32_t MAX_KEYS = 64; + +typedef std::map> Parent; + +struct Options : Parent { + Options(bool image_apply_only_options) : + Parent{ + {"rbd_atime_update_interval", {}}, + {"rbd_balance_parent_reads", {}}, + {"rbd_balance_snap_reads", {}}, + {"rbd_blacklist_expire_seconds", {}}, + {"rbd_blacklist_on_break_lock", {}}, + {"rbd_cache", {}}, + {"rbd_cache_block_writes_upfront", {}}, + {"rbd_cache_max_dirty", {}}, + {"rbd_cache_max_dirty_age", {}}, + {"rbd_cache_max_dirty_object", {}}, + {"rbd_cache_size", {}}, + {"rbd_cache_target_dirty", {}}, + {"rbd_cache_writethrough_until_flush", {}}, + {"rbd_clone_copy_on_read", {}}, + {"rbd_concurrent_management_ops", {}}, + {"rbd_journal_commit_age", {}}, + {"rbd_journal_max_concurrent_object_sets", {}}, + {"rbd_journal_max_payload_bytes", {}}, + {"rbd_journal_object_flush_age", {}}, + {"rbd_journal_object_flush_bytes", {}}, + {"rbd_journal_object_flush_interval", {}}, + {"rbd_journal_order", {}}, + {"rbd_journal_pool", {}}, + {"rbd_journal_splay_width", {}}, + {"rbd_localize_parent_reads", {}}, + {"rbd_localize_snap_reads", {}}, + {"rbd_mirroring_delete_delay", {}}, + {"rbd_mirroring_replay_delay", {}}, + {"rbd_mirroring_resync_after_disconnect", {}}, + {"rbd_mtime_update_interval", {}}, + {"rbd_non_blocking_aio", {}}, + {"rbd_qos_bps_limit", {}}, + {"rbd_qos_iops_limit", {}}, + {"rbd_qos_read_bps_limit", {}}, + {"rbd_qos_read_iops_limit", {}}, + {"rbd_qos_write_bps_limit", {}}, + {"rbd_qos_write_iops_limit", {}}, + {"rbd_readahead_disable_after_bytes", {}}, + {"rbd_readahead_max_bytes", {}}, + {"rbd_readahead_trigger_requests", {}}, + {"rbd_request_timed_out_seconds", {}}, + {"rbd_skip_partial_discard", {}}, + {"rbd_sparse_read_threshold_bytes", {}}, + } { + if (!image_apply_only_options) { + Parent image_create_opts = { + {"rbd_default_data_pool", {}}, + {"rbd_default_features", {}}, + {"rbd_default_order", {}}, + {"rbd_default_stripe_count", {}}, + {"rbd_default_stripe_unit", {}}, + {"rbd_journal_order", {}}, + {"rbd_journal_pool", {}}, + {"rbd_journal_splay_width", {}}, + }; + insert(image_create_opts.begin(), image_create_opts.end()); + } + } + + int init(librados::IoCtx& io_ctx) { + CephContext *cct = (CephContext *)io_ctx.cct(); + + for (auto &it : *this) { + int r = cct->_conf.get_val(it.first.c_str(), &it.second.first); + ceph_assert(r == 0); + it.second.second = RBD_CONFIG_SOURCE_CONFIG; + } + + std::string last_key = ImageCtx::METADATA_CONF_PREFIX; + bool more_results = true; + + while (more_results) { + std::map pairs; + + int r = librbd::api::PoolMetadata<>::list(io_ctx, last_key, MAX_KEYS, + &pairs); + if (r < 0) { + return r; + } + + if (pairs.empty()) { + break; + } + + more_results = (pairs.size() == MAX_KEYS); + last_key = pairs.rbegin()->first; + + for (auto kv : pairs) { + std::string key; + if (!util::is_metadata_config_override(kv.first, &key)) { + more_results = false; + break; + } + auto it = find(key); + if (it != end()) { + it->second = {{kv.second.c_str(), kv.second.length()}, + RBD_CONFIG_SOURCE_POOL}; + } + } + } + return 0; + } +}; + +} // anonymous namespace + +template +bool Config::is_option_name(librados::IoCtx& io_ctx, + const std::string &name) { + Options opts(false); + + return (opts.find(name) != opts.end()); +} + +template +int Config::list(librados::IoCtx& io_ctx, + std::vector *options) { + Options opts(false); + + int r = opts.init(io_ctx); + if (r < 0) { + return r; + } + + for (auto &it : opts) { + options->push_back({it.first, it.second.first, it.second.second}); + } + + return 0; +} + +template +bool Config::is_option_name(I *image_ctx, const std::string &name) { + Options opts(true); + + return (opts.find(name) != opts.end()); +} + +template +int Config::list(I *image_ctx, std::vector *options) { + CephContext *cct = image_ctx->cct; + Options opts(true); + + int r = opts.init(image_ctx->md_ctx); + if (r < 0) { + return r; + } + + std::string last_key = ImageCtx::METADATA_CONF_PREFIX; + bool more_results = true; + + while (more_results) { + std::map pairs; + + r = cls_client::metadata_list(&image_ctx->md_ctx, image_ctx->header_oid, + last_key, MAX_KEYS, &pairs); + if (r < 0) { + lderr(cct) << "failed reading image metadata: " << cpp_strerror(r) + << dendl; + return r; + } + + if (pairs.empty()) { + break; + } + + more_results = (pairs.size() == MAX_KEYS); + last_key = pairs.rbegin()->first; + + for (auto kv : pairs) { + std::string key; + if (!util::is_metadata_config_override(kv.first, &key)) { + more_results = false; + break; + } + auto it = opts.find(key); + if (it != opts.end()) { + it->second = {{kv.second.c_str(), kv.second.length()}, + RBD_CONFIG_SOURCE_IMAGE}; + } + } + } + + for (auto &it : opts) { + options->push_back({it.first, it.second.first, it.second.second}); + } + + return 0; +} + +} // namespace api +} // namespace librbd + +template class librbd::api::Config; diff --git a/src/librbd/api/Config.h b/src/librbd/api/Config.h new file mode 100644 index 0000000000000..9f27000966dfd --- /dev/null +++ b/src/librbd/api/Config.h @@ -0,0 +1,37 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_API_CONFIG_H +#define CEPH_LIBRBD_API_CONFIG_H + +#include "include/rbd/librbd.hpp" + +namespace librados { + +class IoCtx; + +} + +namespace librbd { + +class ImageCtx; + +namespace api { + +template +class Config { +public: + static bool is_option_name(librados::IoCtx& io_ctx, const std::string &name); + static int list(librados::IoCtx& io_ctx, + std::vector *options); + + static bool is_option_name(ImageCtxT *image_ctx, const std::string &name); + static int list(ImageCtxT *image_ctx, std::vector *options); +}; + +} // namespace api +} // namespace librbd + +extern template class librbd::api::Config; + +#endif // CEPH_LIBRBD_API_CONFIG_H diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 0782a9ba2940c..75413110ba0f9 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -26,6 +26,7 @@ #include "librbd/ImageState.h" #include "librbd/internal.h" #include "librbd/Operations.h" +#include "librbd/api/Config.h" #include "librbd/api/DiffIterate.h" #include "librbd/api/Group.h" #include "librbd/api/Image.h" @@ -202,6 +203,18 @@ void trash_image_info_cpp_to_c(const librbd::trash_image_info_t &cpp_info, c_info->deferment_end_time = cpp_info.deferment_end_time; } +void config_option_cpp_to_c(const librbd::config_option_t &cpp_option, + rbd_config_option_t *c_option) { + c_option->name = strdup(cpp_option.name.c_str()); + c_option->value = strdup(cpp_option.value.c_str()); + c_option->source = cpp_option.source; +} + +void config_option_cleanup(rbd_config_option_t &option) { + free(option.name); + free(option.value); +} + struct C_MirrorImageGetInfo : public Context { rbd_mirror_image_info_t *mirror_image_info; Context *on_finish; @@ -1024,6 +1037,10 @@ namespace librbd { return r; } + int RBD::config_list(IoCtx& io_ctx, std::vector *options) { + return librbd::api::Config<>::list(io_ctx, options); + } + RBD::AioCompletion::AioCompletion(void *cb_arg, callback_t complete_cb) { pc = reinterpret_cast(librbd::io::AioCompletion::create( @@ -2402,6 +2419,11 @@ namespace librbd { return r; } + int Image::config_list(std::vector *options) { + ImageCtx *ictx = (ImageCtx *)ctx; + return librbd::api::Config<>::list(ictx, options); + } + } // namespace librbd extern "C" void rbd_version(int *major, int *minor, int *extra) @@ -3368,6 +3390,37 @@ extern "C" int rbd_pool_metadata_list(rados_ioctx_t p, const char *start, return 0; } +extern "C" int rbd_config_pool_list(rados_ioctx_t p, + rbd_config_option_t *options, + int *max_options) { + librados::IoCtx io_ctx; + librados::IoCtx::from_rados_ioctx_t(p, io_ctx); + + std::vector option_vector; + int r = librbd::api::Config<>::list(io_ctx, &option_vector); + if (r < 0) { + return r; + } + + if (*max_options < static_cast(option_vector.size())) { + *max_options = static_cast(option_vector.size()); + return -ERANGE; + } + + for (int i = 0; i < static_cast(option_vector.size()); ++i) { + config_option_cpp_to_c(option_vector[i], &options[i]); + } + *max_options = static_cast(option_vector.size()); + return 0; +} + +extern "C" void rbd_config_pool_list_cleanup(rbd_config_option_t *options, + int max_options) { + for (int i = 0; i < max_options; ++i) { + config_option_cleanup(options[i]); + } +} + extern "C" int rbd_open(rados_ioctx_t p, const char *name, rbd_image_t *image, const char *snap_name) { @@ -5539,3 +5592,33 @@ extern "C" void rbd_watchers_list_cleanup(rbd_image_watcher_t *watchers, free(watchers[i].addr); } } + +extern "C" int rbd_config_image_list(rbd_image_t image, + rbd_config_option_t *options, + int *max_options) { + librbd::ImageCtx *ictx = (librbd::ImageCtx*)image; + + std::vector option_vector; + int r = librbd::api::Config<>::list(ictx, &option_vector); + if (r < 0) { + return r; + } + + if (*max_options < static_cast(option_vector.size())) { + *max_options = static_cast(option_vector.size()); + return -ERANGE; + } + + for (int i = 0; i < static_cast(option_vector.size()); ++i) { + config_option_cpp_to_c(option_vector[i], &options[i]); + } + *max_options = static_cast(option_vector.size()); + return 0; +} + +extern "C" void rbd_config_image_list_cleanup(rbd_config_option_t *options, + int max_options) { + for (int i = 0; i < max_options; ++i) { + config_option_cleanup(options[i]); + } +} diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index d660f3b542841..b300edd0d1a38 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -216,6 +216,16 @@ cdef extern from "rbd/librbd.h" nogil: rbd_image_migration_state_t state char *state_description + ctypedef enum rbd_config_source_t: + _RBD_CONFIG_SOURCE_CONFIG "RBD_CONFIG_SOURCE_CONFIG" + _RBD_CONFIG_SOURCE_POOL "RBD_CONFIG_SOURCE_POOL" + _RBD_CONFIG_SOURCE_IMAGE "RBD_CONFIG_SOURCE_IMAGE" + + ctypedef struct rbd_config_option_t: + char *name + char *value + rbd_config_source_t source + ctypedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg) ctypedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void* ptr) @@ -302,6 +312,11 @@ cdef extern from "rbd/librbd.h" nogil: uint64_t max, char *keys, size_t *key_len, char *values, size_t *vals_len) + int rbd_config_pool_list(rados_ioctx_t io_ctx, rbd_config_option_t *options, + int *max_options) + void rbd_config_pool_list_cleanup(rbd_config_option_t *options, + int max_options) + int rbd_open(rados_ioctx_t io, const char *name, rbd_image_t *image, const char *snap_name) int rbd_open_by_id(rados_ioctx_t io, const char *image_id, @@ -504,6 +519,11 @@ cdef extern from "rbd/librbd.h" nogil: void rbd_watchers_list_cleanup(rbd_image_watcher_t *watchers, size_t num_watchers) + int rbd_config_image_list(rbd_image_t image, rbd_config_option_t *options, + int *max_options) + void rbd_config_image_list_cleanup(rbd_config_option_t *options, + int max_options) + RBD_FEATURE_LAYERING = _RBD_FEATURE_LAYERING RBD_FEATURE_STRIPINGV2 = _RBD_FEATURE_STRIPINGV2 RBD_FEATURE_EXCLUSIVE_LOCK = _RBD_FEATURE_EXCLUSIVE_LOCK @@ -571,6 +591,10 @@ RBD_IMAGE_MIGRATION_STATE_PREPARED = _RBD_IMAGE_MIGRATION_STATE_PREPARED RBD_IMAGE_MIGRATION_STATE_EXECUTING = _RBD_IMAGE_MIGRATION_STATE_EXECUTING RBD_IMAGE_MIGRATION_STATE_EXECUTED = _RBD_IMAGE_MIGRATION_STATE_EXECUTED +RBD_CONFIG_SOURCE_CONFIG = _RBD_CONFIG_SOURCE_CONFIG +RBD_CONFIG_SOURCE_POOL = _RBD_CONFIG_SOURCE_POOL +RBD_CONFIG_SOURCE_IMAGE = _RBD_CONFIG_SOURCE_IMAGE + class Error(Exception): pass @@ -1661,6 +1685,14 @@ class RBD(object): """ return PoolMetadataIterator(ioctx) + def config_list(self, ioctx): + """ + List pool-level config overrides. + + :returns: :class:`ConfigPoolIterator` + """ + return ConfigPoolIterator(ioctx) + def group_create(self, ioctx, name): """ Create a group. @@ -1958,6 +1990,55 @@ cdef class PoolMetadataIterator(object): free(c_keys) free(c_vals) +cdef class ConfigPoolIterator(object): + """ + Iterator over pool-level overrides for a pool. + + Yields a dictionary containing information about an override. + + Keys are: + + * ``name`` (str) - override name + + * ``value`` (str) - override value + + * ``source`` (str) - override source + """ + + cdef: + rbd_config_option_t *options + int num_options + + def __init__(self, ioctx): + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + self.options = NULL + self.num_options = 32 + while True: + self.options = realloc_chk( + self.options, self.num_options * sizeof(rbd_mirror_peer_t)) + with nogil: + ret = rbd_config_pool_list(_ioctx, self.options, &self.num_options) + if ret < 0: + if ret == -errno.ERANGE: + continue + self.num_options = 0 + raise make_ex(ret, 'error listing config options') + break + + def __iter__(self): + for i in range(self.num_options): + yield { + 'name' : decode_cstr(self.options[i].name), + 'value' : decode_cstr(self.options[i].value), + 'source' : self.options[i].source, + } + + def __dealloc__(self): + if self.options: + rbd_config_pool_list_cleanup(self.options, self.num_options) + free(self.options) + cdef int diff_iterate_cb(uint64_t offset, size_t length, int write, void *cb) \ except? -9000 with gil: # Make sure that if we wound up with an exception from a previous callback, @@ -3757,6 +3838,14 @@ written." % (self.name, ret, length)) """ return WatcherIterator(self) + def config_list(self): + """ + List image-level config overrides. + + :returns: :class:`ConfigPoolIterator` + """ + return ConfigImageIterator(self) + def snap_get_namespace_type(self, snap_id): """ Get the snapshot namespace type. @@ -4175,6 +4264,54 @@ cdef class WatcherIterator(object): rbd_watchers_list_cleanup(self.watchers, self.num_watchers) free(self.watchers) +cdef class ConfigImageIterator(object): + """ + Iterator over image-level overrides for an image. + + Yields a dictionary containing information about an override. + + Keys are: + + * ``name`` (str) - override name + + * ``value`` (str) - override value + + * ``source`` (str) - override source + """ + + cdef: + rbd_config_option_t *options + int num_options + + def __init__(self, Image image): + self.options = NULL + self.num_options = 32 + while True: + self.options = realloc_chk( + self.options, self.num_options * sizeof(rbd_mirror_peer_t)) + with nogil: + ret = rbd_config_image_list(image.image, self.options, + &self.num_options) + if ret < 0: + if ret == -errno.ERANGE: + continue + self.num_options = 0 + raise make_ex(ret, 'error listing config options') + break + + def __iter__(self): + for i in range(self.num_options): + yield { + 'name' : decode_cstr(self.options[i].name), + 'value' : decode_cstr(self.options[i].value), + 'source' : self.options[i].source, + } + + def __dealloc__(self): + if self.options: + rbd_config_image_list_cleanup(self.options, self.num_options) + free(self.options) + cdef class GroupImageIterator(object): """ Iterator over image info for a group. diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index f0f9b0e5ec7ce..432d626ef2a8f 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -7096,6 +7096,7 @@ TEST_F(TestLibRBD, PoolMetadata) ASSERT_STREQ(vals, "value2"); // test config setting + ASSERT_EQ(-EINVAL, rbd_pool_metadata_set(ioctx, "conf_UNKNOWN", "false")); ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "conf_rbd_cache", "false")); ASSERT_EQ(-EINVAL, rbd_pool_metadata_set(ioctx, "conf_rbd_cache", "INVALID")); ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "conf_rbd_cache")); @@ -7189,6 +7190,7 @@ TEST_F(TestLibRBD, PoolMetadataPP) ASSERT_EQ(0, strncmp("value3", pairs["key3"].c_str(), 6)); // test config setting + ASSERT_EQ(-EINVAL, rbd.pool_metadata_set(ioctx, "conf_UNKNOWN", "false")); ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "false")); ASSERT_EQ(-EINVAL, rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "INVALID")); ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "conf_rbd_cache")); @@ -7199,6 +7201,167 @@ TEST_F(TestLibRBD, PoolMetadataPP) ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key3")); } +TEST_F(TestLibRBD, Config) +{ + REQUIRE_FORMAT_V2(); + + rados_ioctx_t ioctx; + rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx); + + ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "conf_rbd_cache", "false")); + + rbd_config_option_t options[1024]; + int max_options = 0; + ASSERT_EQ(-ERANGE, rbd_config_pool_list(ioctx, options, &max_options)); + ASSERT_EQ(0, rbd_config_pool_list(ioctx, options, &max_options)); + ASSERT_GT(max_options, 0); + ASSERT_LT(max_options, 1024); + for (int i = 0; i < max_options; i++) { + if (options[i].name == std::string("rbd_cache")) { + ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_POOL); + ASSERT_STREQ("false", options[i].value); + } else { + ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG); + } + } + rbd_config_pool_list_cleanup(options, max_options); + + rbd_image_t image; + int order = 0; + std::string name = get_temp_image_name(); + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); + + ASSERT_EQ(0, rbd_config_image_list(image, options, &max_options)); + for (int i = 0; i < max_options; i++) { + if (options[i].name == std::string("rbd_cache")) { + ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_POOL); + ASSERT_STREQ("false", options[i].value); + } else { + ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG); + } + } + rbd_config_image_list_cleanup(options, max_options); + + ASSERT_EQ(0, rbd_metadata_set(image, "conf_rbd_cache", "true")); + + ASSERT_EQ(0, rbd_config_image_list(image, options, &max_options)); + for (int i = 0; i < max_options; i++) { + if (options[i].name == std::string("rbd_cache")) { + ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_IMAGE); + ASSERT_STREQ("true", options[i].value); + } else { + ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG); + } + } + rbd_config_image_list_cleanup(options, max_options); + + ASSERT_EQ(0, rbd_metadata_remove(image, "conf_rbd_cache")); + + ASSERT_EQ(0, rbd_config_image_list(image, options, &max_options)); + for (int i = 0; i < max_options; i++) { + if (options[i].name == std::string("rbd_cache")) { + ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_POOL); + ASSERT_STREQ("false", options[i].value); + } else { + ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG); + } + } + rbd_config_image_list_cleanup(options, max_options); + + ASSERT_EQ(0, rbd_close(image)); + + ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "conf_rbd_cache")); + + ASSERT_EQ(-ERANGE, rbd_config_pool_list(ioctx, options, &max_options)); + ASSERT_EQ(0, rbd_config_pool_list(ioctx, options, &max_options)); + for (int i = 0; i < max_options; i++) { + ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG); + } + rbd_config_pool_list_cleanup(options, max_options); + + rados_ioctx_destroy(ioctx); +} + +TEST_F(TestLibRBD, ConfigPP) +{ + REQUIRE_FORMAT_V2(); + + librbd::RBD rbd; + string value; + + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx)); + + ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "false")); + + std::vector options; + ASSERT_EQ(0, rbd.config_list(ioctx, &options)); + for (auto &option : options) { + if (option.name == std::string("rbd_cache")) { + ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_POOL); + ASSERT_EQ("false", option.value); + } else { + ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG); + } + } + + int order = 0; + std::string name = get_temp_image_name(); + uint64_t size = 2 << 20; + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order)); + + librbd::Image image; + ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr)); + + options.clear(); + ASSERT_EQ(0, image.config_list(&options)); + for (auto &option : options) { + if (option.name == std::string("rbd_cache")) { + ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_POOL); + ASSERT_EQ("false", option.value); + } else { + ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG); + } + } + + ASSERT_EQ(0, image.metadata_set("conf_rbd_cache", "true")); + + options.clear(); + ASSERT_EQ(0, image.config_list(&options)); + for (auto &option : options) { + if (option.name == std::string("rbd_cache")) { + ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_IMAGE); + ASSERT_EQ("true", option.value); + } else { + ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG); + } + } + + ASSERT_EQ(0, image.metadata_remove("conf_rbd_cache")); + + options.clear(); + ASSERT_EQ(0, image.config_list(&options)); + for (auto &option : options) { + if (option.name == std::string("rbd_cache")) { + ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_POOL); + ASSERT_EQ("false", option.value); + } else { + ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG); + } + } + + ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "conf_rbd_cache")); + + options.clear(); + ASSERT_EQ(0, rbd.config_list(ioctx, &options)); + for (auto &option : options) { + ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG); + } +} + // poorman's ceph_assert() namespace ceph { void __ceph_assert_fail(const char *assertion, const char *file, int line, diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index 749f6877ea4b9..9dc98537336cb 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -23,7 +23,8 @@ from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists, RBD_MIRROR_IMAGE_DISABLED, MIRROR_IMAGE_STATUS_STATE_UNKNOWN, RBD_LOCK_MODE_EXCLUSIVE, RBD_OPERATION_FEATURE_GROUP, RBD_SNAP_NAMESPACE_TYPE_TRASH, - RBD_IMAGE_MIGRATION_STATE_PREPARED) + RBD_IMAGE_MIGRATION_STATE_PREPARED, RBD_CONFIG_SOURCE_CONFIG, + RBD_CONFIG_SOURCE_POOL, RBD_CONFIG_SOURCE_IMAGE) rados = None ioctx = None @@ -357,6 +358,25 @@ def test_pool_metadata(): metadata = list(rbd.pool_metadata_list(ioctx)) eq(len(metadata), N - i - 1) +def test_config_list(): + rbd = RBD() + + for option in rbd.config_list(ioctx): + eq(option['source'], RBD_CONFIG_SOURCE_CONFIG) + + rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "true") + + for option in rbd.config_list(ioctx): + if option['name'] == "rbd_cache": + eq(option['source'], RBD_CONFIG_SOURCE_POOL) + else: + eq(option['source'], RBD_CONFIG_SOURCE_CONFIG) + + rbd.pool_metadata_remove(ioctx, "conf_rbd_cache") + + for option in rbd.config_list(ioctx): + eq(option['source'], RBD_CONFIG_SOURCE_CONFIG) + def rand_data(size): return os.urandom(size) @@ -1027,6 +1047,24 @@ class TestImage(object): # watcher. eq(len(watchers), 1) + def test_config_list(self): + with Image(ioctx, image_name) as image: + for option in image.config_list(): + eq(option['source'], RBD_CONFIG_SOURCE_CONFIG) + + image.metadata_set("conf_rbd_cache", "true") + + for option in image.config_list(): + if option['name'] == "rbd_cache": + eq(option['source'], RBD_CONFIG_SOURCE_IMAGE) + else: + eq(option['source'], RBD_CONFIG_SOURCE_CONFIG) + + image.metadata_remove("conf_rbd_cache") + + for option in image.config_list(): + eq(option['source'], RBD_CONFIG_SOURCE_CONFIG) + class TestImageId(object): def setUp(self): -- 2.39.5