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,
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,
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,
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:
int pool_metadata_list(IoCtx &io_ctx, const std::string &start, uint64_t max,
std::map<std::string, ceph::bufferlist> *pairs);
+ int config_list(IoCtx& io_ctx, std::vector<config_option_t> *options);
+
private:
/* We don't allow assignment or copying */
RBD(const RBD& rhs);
int list_watchers(std::list<image_watcher_t> &watchers);
+ int config_list(std::vector<config_option_t> *options);
+
private:
friend class RBD;
TrashWatcher.cc
Utils.cc
Watcher.cc
+ api/Config.cc
api/DiffIterate.cc
api/Group.cc
api/Image.cc
--- /dev/null
+// -*- 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<std::string, std::pair<std::string, config_source_t>> 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<std::string, bufferlist> 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 <typename I>
+bool Config<I>::is_option_name(librados::IoCtx& io_ctx,
+ const std::string &name) {
+ Options opts(false);
+
+ return (opts.find(name) != opts.end());
+}
+
+template <typename I>
+int Config<I>::list(librados::IoCtx& io_ctx,
+ std::vector<config_option_t> *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 <typename I>
+bool Config<I>::is_option_name(I *image_ctx, const std::string &name) {
+ Options opts(true);
+
+ return (opts.find(name) != opts.end());
+}
+
+template <typename I>
+int Config<I>::list(I *image_ctx, std::vector<config_option_t> *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<std::string, bufferlist> 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<librbd::ImageCtx>;
--- /dev/null
+// -*- 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 <typename ImageCtxT = librbd::ImageCtx>
+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<config_option_t> *options);
+
+ static bool is_option_name(ImageCtxT *image_ctx, const std::string &name);
+ static int list(ImageCtxT *image_ctx, std::vector<config_option_t> *options);
+};
+
+} // namespace api
+} // namespace librbd
+
+extern template class librbd::api::Config<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_API_CONFIG_H
#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"
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;
return r;
}
+ int RBD::config_list(IoCtx& io_ctx, std::vector<config_option_t> *options) {
+ return librbd::api::Config<>::list(io_ctx, options);
+ }
+
RBD::AioCompletion::AioCompletion(void *cb_arg, callback_t complete_cb)
{
pc = reinterpret_cast<void*>(librbd::io::AioCompletion::create(
return r;
}
+ int Image::config_list(std::vector<config_option_t> *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)
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<librbd::config_option_t> option_vector;
+ int r = librbd::api::Config<>::list(io_ctx, &option_vector);
+ if (r < 0) {
+ return r;
+ }
+
+ if (*max_options < static_cast<int>(option_vector.size())) {
+ *max_options = static_cast<int>(option_vector.size());
+ return -ERANGE;
+ }
+
+ for (int i = 0; i < static_cast<int>(option_vector.size()); ++i) {
+ config_option_cpp_to_c(option_vector[i], &options[i]);
+ }
+ *max_options = static_cast<int>(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)
{
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<librbd::config_option_t> option_vector;
+ int r = librbd::api::Config<>::list(ictx, &option_vector);
+ if (r < 0) {
+ return r;
+ }
+
+ if (*max_options < static_cast<int>(option_vector.size())) {
+ *max_options = static_cast<int>(option_vector.size());
+ return -ERANGE;
+ }
+
+ for (int i = 0; i < static_cast<int>(option_vector.size()); ++i) {
+ config_option_cpp_to_c(option_vector[i], &options[i]);
+ }
+ *max_options = static_cast<int>(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]);
+ }
+}
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)
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,
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
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
"""
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.
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 = <rbd_config_option_t *>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,
"""
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.
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 = <rbd_config_option_t *>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.
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"));
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"));
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<librbd::config_option_t> 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,
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
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)
# 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):