From b251534cfbe7da676260c01b0f23460930ba661e Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Sun, 26 Aug 2018 11:23:22 +0300 Subject: [PATCH] librbd: API to manipulate pool metadata Signed-off-by: Mykola Golub --- src/include/rbd/librbd.h | 12 +++ src/include/rbd/librbd.hpp | 8 ++ src/librbd/CMakeLists.txt | 1 + src/librbd/api/PoolMetadata.cc | 106 +++++++++++++++++++++ src/librbd/api/PoolMetadata.h | 40 ++++++++ src/librbd/librbd.cc | 98 ++++++++++++++++++++ src/pybind/rbd/rbd.pyx | 158 ++++++++++++++++++++++++++++++++ src/test/librbd/test_librbd.cc | 162 +++++++++++++++++++++++++++++++++ src/test/pybind/test_rbd.py | 32 +++++++ 9 files changed, 617 insertions(+) create mode 100644 src/librbd/api/PoolMetadata.cc create mode 100644 src/librbd/api/PoolMetadata.h diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index aaad6b811af9f..35a81ff399869 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -391,6 +391,18 @@ CEPH_RBD_API void rbd_mirror_image_status_list_cleanup(char **image_ids, CEPH_RBD_API int rbd_mirror_image_status_summary(rados_ioctx_t io_ctx, rbd_mirror_image_status_state_t *states, int *counts, size_t *maxlen); +/* pool metadata */ +CEPH_RBD_API int rbd_pool_metadata_get(rados_ioctx_t io_ctx, const char *key, + char *value, size_t *val_len); +CEPH_RBD_API int rbd_pool_metadata_set(rados_ioctx_t io_ctx, const char *key, + const char *value); +CEPH_RBD_API int rbd_pool_metadata_remove(rados_ioctx_t io_ctx, + const char *key); +CEPH_RBD_API int rbd_pool_metadata_list(rados_ioctx_t io_ctx, const char *start, + uint64_t max, char *keys, + size_t *key_len, char *values, + size_t *vals_len); + 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, diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index 052ec111caf33..980a331fa2712 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -278,6 +278,14 @@ public: int namespace_list(IoCtx& io_ctx, std::vector* namespace_names); int namespace_exists(IoCtx& io_ctx, const char *namespace_name, bool *exists); + int pool_metadata_get(IoCtx &io_ctx, const std::string &key, + std::string *value); + int pool_metadata_set(IoCtx &io_ctx, const std::string &key, + const std::string &value); + int pool_metadata_remove(IoCtx &io_ctx, const std::string &key); + int pool_metadata_list(IoCtx &io_ctx, const std::string &start, uint64_t max, + std::map *pairs); + private: /* We don't allow assignment or copying */ RBD(const RBD& rhs); diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index a0744a5cd087e..af423df862284 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -31,6 +31,7 @@ set(librbd_internal_srcs api/Mirror.cc api/Namespace.cc api/Snapshot.cc + api/PoolMetadata.cc cache/ImageWriteback.cc cache/ObjectCacherObjectDispatch.cc cache/PassthroughImageCache.cc diff --git a/src/librbd/api/PoolMetadata.cc b/src/librbd/api/PoolMetadata.cc new file mode 100644 index 0000000000000..4909d88db5541 --- /dev/null +++ b/src/librbd/api/PoolMetadata.cc @@ -0,0 +1,106 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/api/PoolMetadata.h" +#include "cls/rbd/cls_rbd_client.h" +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/Utils.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::PoolMetadata: " << __func__ << ": " + +namespace librbd { +namespace api { + +template +int PoolMetadata::get(librados::IoCtx& io_ctx, + const std::string &key, std::string *value) { + CephContext *cct = (CephContext *)io_ctx.cct(); + + int r = cls_client::metadata_get(&io_ctx, RBD_INFO, key, value); + if (r < 0 && r != -ENOENT) { + lderr(cct) << "failed reading metadata " << key << ": " << cpp_strerror(r) + << dendl; + } + + return r; +} + +template +int PoolMetadata::set(librados::IoCtx& io_ctx, const std::string &key, + const std::string &value) { + CephContext *cct = (CephContext *)io_ctx.cct(); + + std::string config_key; + if (util::is_metadata_config_override(key, &config_key)) { + int r = ConfigProxy{false}.set_val(config_key.c_str(), value); + if (r < 0) { + lderr(cct) << "validation for " << key << " failed: " << cpp_strerror(r) + << dendl; + return -EINVAL; + } + } + + ceph::bufferlist bl; + bl.append(value); + + int r = cls_client::metadata_set(&io_ctx, RBD_INFO, {{key, bl}}); + if (r < 0) { + lderr(cct) << "failed setting metadata " << key << ": " << cpp_strerror(r) + << dendl; + return r; + } + + return 0; +} + +template +int PoolMetadata::remove(librados::IoCtx& io_ctx, const std::string &key) { + CephContext *cct = (CephContext *)io_ctx.cct(); + + std::string value; + int r = cls_client::metadata_get(&io_ctx, RBD_INFO, key, &value); + if (r < 0) { + if (r == -ENOENT) { + ldout(cct, 1) << "metadata " << key << " does not exist" << dendl; + } else { + lderr(cct) << "failed reading metadata " << key << ": " << cpp_strerror(r) + << dendl; + } + return r; + } + + r = cls_client::metadata_remove(&io_ctx, RBD_INFO, key); + if (r < 0) { + lderr(cct) << "failed removing metadata " << key << ": " << cpp_strerror(r) + << dendl; + return r; + } + + return 0; +} + +template +int PoolMetadata::list(librados::IoCtx& io_ctx, const std::string &start, + uint64_t max, + std::map *pairs) { + CephContext *cct = (CephContext *)io_ctx.cct(); + + int r = cls_client::metadata_list(&io_ctx, RBD_INFO, start, max, pairs); + if (r == -ENOENT) { + r = 0; + } else if (r < 0) { + lderr(cct) << "failed listing metadata: " << cpp_strerror(r) + << dendl; + return r; + } + + return 0; +} + +} // namespace api +} // namespace librbd + +template class librbd::api::PoolMetadata; diff --git a/src/librbd/api/PoolMetadata.h b/src/librbd/api/PoolMetadata.h new file mode 100644 index 0000000000000..ffee3afb03809 --- /dev/null +++ b/src/librbd/api/PoolMetadata.h @@ -0,0 +1,40 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_API_POOL_METADATA_H +#define CEPH_LIBRBD_API_POOL_METADATA_H + +#include "include/buffer.h" + +#include + +namespace librados { + +class IoCtx; + +} + +namespace librbd { + +class ImageCtx; + +namespace api { + +template +class PoolMetadata { +public: + static int get(librados::IoCtx& io_ctx, const std::string &key, + std::string *value); + static int set(librados::IoCtx& io_ctx, const std::string &key, + const std::string &value); + static int remove(librados::IoCtx& io_ctx, const std::string &key); + static int list(librados::IoCtx& io_ctx, const std::string &start, + uint64_t max, std::map *pairs); +}; + +} // namespace api +} // namespace librbd + +extern template class librbd::api::PoolMetadata; + +#endif // CEPH_LIBRBD_API_POOL_METADATA_H diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 14e83b46a3c83..0782a9ba2940c 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -32,6 +32,7 @@ #include "librbd/api/Migration.h" #include "librbd/api/Mirror.h" #include "librbd/api/Namespace.h" +#include "librbd/api/PoolMetadata.h" #include "librbd/api/Snapshot.h" #include "librbd/io/AioCompletion.h" #include "librbd/io/ImageRequestWQ.h" @@ -996,6 +997,33 @@ namespace librbd { return r; } + int RBD::pool_metadata_get(IoCtx& ioctx, const std::string &key, + std::string *value) + { + int r = librbd::api::PoolMetadata<>::get(ioctx, key, value); + return r; + } + + int RBD::pool_metadata_set(IoCtx& ioctx, const std::string &key, + const std::string &value) + { + int r = librbd::api::PoolMetadata<>::set(ioctx, key, value); + return r; + } + + int RBD::pool_metadata_remove(IoCtx& ioctx, const std::string &key) + { + int r = librbd::api::PoolMetadata<>::remove(ioctx, key); + return r; + } + + int RBD::pool_metadata_list(IoCtx& ioctx, const std::string &start, + uint64_t max, map *pairs) + { + int r = librbd::api::PoolMetadata<>::list(ioctx, start, max, pairs); + return r; + } + RBD::AioCompletion::AioCompletion(void *cb_arg, callback_t complete_cb) { pc = reinterpret_cast(librbd::io::AioCompletion::create( @@ -3270,6 +3298,76 @@ extern "C" void rbd_migration_status_cleanup(rbd_image_migration_status_t *s) free(s->state_description); } +extern "C" int rbd_pool_metadata_get(rados_ioctx_t p, const char *key, + char *value, size_t *vallen) +{ + librados::IoCtx io_ctx; + librados::IoCtx::from_rados_ioctx_t(p, io_ctx); + string val_s; + int r = librbd::api::PoolMetadata<>::get(io_ctx, key, &val_s); + if (*vallen < val_s.size() + 1) { + r = -ERANGE; + *vallen = val_s.size() + 1; + } else { + strncpy(value, val_s.c_str(), val_s.size() + 1); + } + + return r; +} + +extern "C" int rbd_pool_metadata_set(rados_ioctx_t p, const char *key, + const char *value) +{ + librados::IoCtx io_ctx; + librados::IoCtx::from_rados_ioctx_t(p, io_ctx); + int r = librbd::api::PoolMetadata<>::set(io_ctx, key, value); + return r; +} + +extern "C" int rbd_pool_metadata_remove(rados_ioctx_t p, const char *key) +{ + librados::IoCtx io_ctx; + librados::IoCtx::from_rados_ioctx_t(p, io_ctx); + int r = librbd::api::PoolMetadata<>::remove(io_ctx, key); + return r; +} + +extern "C" int rbd_pool_metadata_list(rados_ioctx_t p, const char *start, + uint64_t max, char *key, size_t *key_len, + char *value, size_t *val_len) +{ + librados::IoCtx io_ctx; + librados::IoCtx::from_rados_ioctx_t(p, io_ctx); + map pairs; + int r = librbd::api::PoolMetadata<>::list(io_ctx, start, max, &pairs); + if (r < 0) { + return r; + } + size_t key_total_len = 0, val_total_len = 0; + for (auto &it : pairs) { + key_total_len += it.first.size() + 1; + val_total_len += it.second.length() + 1; + } + if (*key_len < key_total_len || *val_len < val_total_len) { + *key_len = key_total_len; + *val_len = val_total_len; + return -ERANGE; + } + *key_len = key_total_len; + *val_len = val_total_len; + + char *key_p = key, *value_p = value; + for (auto &it : pairs) { + strncpy(key_p, it.first.c_str(), it.first.size() + 1); + key_p += it.first.size() + 1; + strncpy(value_p, it.second.c_str(), it.second.length()); + value_p += it.second.length(); + *value_p = '\0'; + value_p++; + } + return 0; +} + extern "C" int rbd_open(rados_ioctx_t p, const char *name, rbd_image_t *image, const char *snap_name) { diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index 3b2c37412a36c..d660f3b542841 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -293,6 +293,15 @@ cdef extern from "rbd/librbd.h" nogil: rbd_mirror_image_status_state_t *states, int *counts, size_t *maxlen) + int rbd_pool_metadata_get(rados_ioctx_t io_ctx, const char *key, + char *value, size_t *val_len) + int rbd_pool_metadata_set(rados_ioctx_t io_ctx, const char *key, + const char *value) + int rbd_pool_metadata_remove(rados_ioctx_t io_ctx, const char *key) + int rbd_pool_metadata_list(rados_ioctx_t io_ctx, const char *start, + uint64_t max, char *keys, size_t *key_len, + char *values, size_t *vals_len) + 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, @@ -1567,6 +1576,91 @@ class RBD(object): free(states) free(counts) + def pool_metadata_get(self, ioctx, key): + """ + Get pool metadata for the given key. + + :param ioctx: determines which RADOS pool is read + :type ioctx: :class:`rados.Ioctx` + :param key: metadata key + :type key: str + :returns: str - metadata value + """ + key = cstr(key, 'key') + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + char *_key = key + size_t size = 4096 + char *value = NULL + int ret + try: + while True: + value = realloc_chk(value, size) + with nogil: + ret = rbd_pool_metadata_get(_ioctx, _key, value, &size) + if ret != -errno.ERANGE: + break + if ret == -errno.ENOENT: + raise KeyError('no metadata %s' % (key)) + if ret != 0: + raise make_ex(ret, 'error getting metadata %s' % (key)) + return decode_cstr(value) + finally: + free(value) + + def pool_metadata_set(self, ioctx, key, value): + """ + Set pool metadata for the given key. + + :param ioctx: determines which RADOS pool is read + :type ioctx: :class:`rados.Ioctx` + :param key: metadata key + :type key: str + :param value: metadata value + :type value: str + """ + key = cstr(key, 'key') + value = cstr(value, 'value') + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + char *_key = key + char *_value = value + with nogil: + ret = rbd_pool_metadata_set(_ioctx, _key, _value) + + if ret != 0: + raise make_ex(ret, 'error setting metadata %s' % (key)) + + def pool_metadata_remove(self, ioctx, key): + """ + Remove pool metadata for the given key. + + :param ioctx: determines which RADOS pool is read + :type ioctx: :class:`rados.Ioctx` + :param key: metadata key + :type key: str + :returns: str - metadata value + """ + key = cstr(key, 'key') + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + char *_key = key + with nogil: + ret = rbd_pool_metadata_remove(_ioctx, _key) + + if ret == -errno.ENOENT: + raise KeyError('no metadata %s' % (key)) + if ret != 0: + raise make_ex(ret, 'error removing metadata %s' % (key)) + + def pool_metadata_list(self, ioctx): + """ + List pool metadata. + + :returns: :class:`PoolMetadataIterator` + """ + return PoolMetadataIterator(ioctx) + def group_create(self, ioctx, name): """ Create a group. @@ -1800,6 +1894,70 @@ cdef class MirrorImageStatusIterator(object): free(self.last_read) self.last_read = strdup("") +cdef class PoolMetadataIterator(object): + """ + Iterator over pool metadata list. + + Yields ``(key, value)`` tuple. + + * ``key`` (str) - metadata key + * ``value`` (str) - metadata value + """ + + cdef: + rados_ioctx_t ioctx + char *last_read + uint64_t max_read + object next_chunk + + def __init__(self, ioctx): + self.ioctx = convert_ioctx(ioctx) + self.last_read = strdup("") + self.max_read = 32 + self.get_next_chunk() + + def __iter__(self): + while len(self.next_chunk) > 0: + for pair in self.next_chunk: + yield pair + if len(self.next_chunk) < self.max_read: + break + self.get_next_chunk() + + def __dealloc__(self): + if self.last_read: + free(self.last_read) + + def get_next_chunk(self): + cdef: + char *c_keys = NULL + size_t keys_size = 4096 + char *c_vals = NULL + size_t vals_size = 4096 + try: + while True: + c_keys = realloc_chk(c_keys, keys_size) + c_vals = realloc_chk(c_vals, vals_size) + with nogil: + ret = rbd_pool_metadata_list(self.ioctx, self.last_read, + self.max_read, c_keys, &keys_size, + c_vals, &vals_size) + if ret >= 0: + break + elif ret != -errno.ERANGE: + raise make_ex(ret, 'error listing metadata') + keys = [decode_cstr(key) for key in + c_keys[:keys_size].split(b'\0') if key] + vals = [decode_cstr(val) for val in + c_vals[:vals_size].split(b'\0') if val] + if len(keys) > 0: + free(self.last_read) + self.last_read = strdup(keys[-1]) + self.next_chunk = zip(keys, vals) + finally: + free(c_keys) + free(c_vals) + 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, diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index 9ab40bd8d5e0b..f0f9b0e5ec7ce 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -7037,6 +7037,168 @@ TEST_F(TestLibRBD, ZeroOverlapFlatten) { ASSERT_EQ(0, clone_image.flatten()); } +TEST_F(TestLibRBD, PoolMetadata) +{ + REQUIRE_FORMAT_V2(); + + rados_ioctx_t ioctx; + rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx); + + char keys[1024]; + char vals[1024]; + size_t keys_len = sizeof(keys); + size_t vals_len = sizeof(vals); + + memset_rand(keys, keys_len); + memset_rand(vals, vals_len); + + ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals, + &vals_len)); + ASSERT_EQ(0U, keys_len); + ASSERT_EQ(0U, vals_len); + + char value[1024]; + size_t value_len = sizeof(value); + memset_rand(value, value_len); + + ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key1", "value1")); + ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key2", "value2")); + ASSERT_EQ(0, rbd_pool_metadata_get(ioctx, "key1", value, &value_len)); + ASSERT_STREQ(value, "value1"); + value_len = 1; + ASSERT_EQ(-ERANGE, rbd_pool_metadata_get(ioctx, "key1", value, &value_len)); + ASSERT_EQ(value_len, strlen("value1") + 1); + + ASSERT_EQ(-ERANGE, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals, + &vals_len)); + keys_len = sizeof(keys); + vals_len = sizeof(vals); + memset_rand(keys, keys_len); + memset_rand(vals, vals_len); + ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals, + &vals_len)); + ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1); + ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1); + ASSERT_STREQ(keys, "key1"); + ASSERT_STREQ(keys + strlen(keys) + 1, "key2"); + ASSERT_STREQ(vals, "value1"); + ASSERT_STREQ(vals + strlen(vals) + 1, "value2"); + + ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key1")); + ASSERT_EQ(-ENOENT, rbd_pool_metadata_remove(ioctx, "key3")); + value_len = sizeof(value); + ASSERT_EQ(-ENOENT, rbd_pool_metadata_get(ioctx, "key3", value, &value_len)); + ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals, + &vals_len)); + ASSERT_EQ(keys_len, strlen("key2") + 1); + ASSERT_EQ(vals_len, strlen("value2") + 1); + ASSERT_STREQ(keys, "key2"); + ASSERT_STREQ(vals, "value2"); + + // test config setting + 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")); + + // test short buffer cases + ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key1", "value1")); + ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key3", "value3")); + ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key4", "value4")); + + keys_len = strlen("key1") + 1; + vals_len = strlen("value1") + 1; + memset_rand(keys, keys_len); + memset_rand(vals, vals_len); + ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 1, keys, &keys_len, vals, + &vals_len)); + ASSERT_EQ(keys_len, strlen("key1") + 1); + ASSERT_EQ(vals_len, strlen("value1") + 1); + ASSERT_STREQ(keys, "key1"); + ASSERT_STREQ(vals, "value1"); + + ASSERT_EQ(-ERANGE, rbd_pool_metadata_list(ioctx, "", 2, keys, &keys_len, vals, + &vals_len)); + ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1); + ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1); + + ASSERT_EQ(-ERANGE, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals, + &vals_len)); + ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") + + 1 + strlen("key4") + 1); + ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1 + + strlen("value3") + 1 + strlen("value4") + 1); + + // test `start` param + keys_len = sizeof(keys); + vals_len = sizeof(vals); + memset_rand(keys, keys_len); + memset_rand(vals, vals_len); + ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "key2", 0, keys, &keys_len, vals, + &vals_len)); + ASSERT_EQ(keys_len, strlen("key3") + 1 + strlen("key4") + 1); + ASSERT_EQ(vals_len, strlen("value3") + 1 + strlen("value4") + 1); + ASSERT_STREQ(keys, "key3"); + ASSERT_STREQ(vals, "value3"); + + //cleanup + ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key1")); + ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key2")); + ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key3")); + ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key4")); + rados_ioctx_destroy(ioctx); +} + +TEST_F(TestLibRBD, PoolMetadataPP) +{ + REQUIRE_FORMAT_V2(); + + librbd::RBD rbd; + string value; + map pairs; + + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx)); + + ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "", 0, &pairs)); + ASSERT_TRUE(pairs.empty()); + + ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key1", "value1")); + ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key2", "value2")); + ASSERT_EQ(0, rbd.pool_metadata_get(ioctx, "key1", &value)); + ASSERT_EQ(value, "value1"); + ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "", 0, &pairs)); + ASSERT_EQ(2U, pairs.size()); + ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6)); + ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6)); + + ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key1")); + ASSERT_EQ(-ENOENT, rbd.pool_metadata_remove(ioctx, "key3")); + ASSERT_EQ(-ENOENT, rbd.pool_metadata_get(ioctx, "key3", &value)); + pairs.clear(); + ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "", 0, &pairs)); + ASSERT_EQ(1U, pairs.size()); + ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6)); + + // test `start` param + ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key1", "value1")); + ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key3", "value3")); + + pairs.clear(); + ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "key2", 0, &pairs)); + ASSERT_EQ(1U, pairs.size()); + ASSERT_EQ(0, strncmp("value3", pairs["key3"].c_str(), 6)); + + // test config setting + 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")); + + // cleanup + ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key1")); + ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key2")); + ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key3")); +} + // 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 2da63420ba4f1..749f6877ea4b9 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -325,6 +325,38 @@ def test_rename(): rbd.rename(ioctx, image_name2, image_name) eq([image_name], rbd.list(ioctx)) +def test_pool_metadata(): + rbd = RBD() + metadata = list(rbd.pool_metadata_list(ioctx)) + eq(len(metadata), 0) + assert_raises(KeyError, rbd.pool_metadata_get, ioctx, "key1") + rbd.pool_metadata_set(ioctx, "key1", "value1") + rbd.pool_metadata_set(ioctx, "key2", "value2") + value = rbd.pool_metadata_get(ioctx, "key1") + eq(value, "value1") + value = rbd.pool_metadata_get(ioctx, "key2") + eq(value, "value2") + metadata = list(rbd.pool_metadata_list(ioctx)) + eq(len(metadata), 2) + rbd.pool_metadata_remove(ioctx, "key1") + metadata = list(rbd.pool_metadata_list(ioctx)) + eq(len(metadata), 1) + eq(metadata[0], ("key2", "value2")) + rbd.pool_metadata_remove(ioctx, "key2") + assert_raises(KeyError, rbd.pool_metadata_remove, ioctx, "key2") + metadata = list(rbd.pool_metadata_list(ioctx)) + eq(len(metadata), 0) + + N = 65 + for i in xrange(N): + rbd.pool_metadata_set(ioctx, "key" + str(i), "X" * 1025) + metadata = list(rbd.pool_metadata_list(ioctx)) + eq(len(metadata), N) + for i in xrange(N): + rbd.pool_metadata_remove(ioctx, "key" + str(i)) + metadata = list(rbd.pool_metadata_list(ioctx)) + eq(len(metadata), N - i - 1) + def rand_data(size): return os.urandom(size) -- 2.39.5