From f7a67cfbe4eaafe43c56e3fa00ee5fafbd0f6147 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 20 Jun 2017 19:26:13 -0400 Subject: [PATCH] librados: new API to manipulate pool application metadata Signed-off-by: Jason Dillaman --- src/include/rados/librados.h | 94 +++++++++++++++++++ src/include/rados/librados.hpp | 13 +++ src/librados/IoCtxImpl.cc | 150 +++++++++++++++++++++++++++++++ src/librados/IoCtxImpl.h | 13 +++ src/librados/RadosClient.cc | 5 ++ src/librados/RadosClient.h | 2 + src/librados/librados.cc | 160 +++++++++++++++++++++++++++++++++ src/pybind/rados/rados.pyx | 151 ++++++++++++++++++++++++++++++- src/test/librados/misc.cc | 92 +++++++++++++++++++ src/test/pybind/test_rados.py | 22 +++++ 10 files changed, 701 insertions(+), 1 deletion(-) diff --git a/src/include/rados/librados.h b/src/include/rados/librados.h index 544c2dbca39..9d427ebcd17 100644 --- a/src/include/rados/librados.h +++ b/src/include/rados/librados.h @@ -3507,6 +3507,100 @@ CEPH_RADOS_API int rados_blacklist_add(rados_t cluster, char *client_address, uint32_t expire_seconds); +/** + * Enable an application on a pool + * + * @param ioctx pool ioctx + * @param app_name application name + * @param force 0 if only single application per pool + * @returns 0 on success, negative error code on failure + */ +CEPH_RADOS_API int rados_application_enable(rados_ioctx_t io, + const char *app_name, int force); + +/** + * List all enabled applications + * + * If the provided buffer is too short, the required length is filled in and + * -ERANGE is returned. Otherwise, the buffers are filled with the application + * names, with a '\0' after each. + * + * @param ioctx pool ioctx + * @param app_name application name + * @param values buffer in which to store application names + * @param vals_len number of bytes in values buffer + * @returns 0 on success, negative error code on failure + * @returns -ERANGE if either buffer is too short + */ +CEPH_RADOS_API int rados_application_list(rados_ioctx_t io, char *values, + size_t *values_len); + +/** + * Get application metadata value from pool + * + * @param ioctx pool ioctx + * @param app_name application name + * @param key metadata key + * @param value result buffer + * @param value_len maximum len of value + * @returns 0 on success, negative error code on failure + */ +CEPH_RADOS_API int rados_application_metadata_get(rados_ioctx_t io, + const char *app_name, + const char *key, char *value, + size_t *value_len); + +/** + * Set application metadata on a pool + * + * @param ioctx pool ioctx + * @param app_name application name + * @param key metadata key + * @param value metadata key + * @returns 0 on success, negative error code on failure + */ +CEPH_RADOS_API int rados_application_metadata_set(rados_ioctx_t io, + const char *app_name, + const char *key, + const char *value); + +/** + * Remove application metadata from a pool + * + * @param ioctx pool ioctx + * @param app_name application name + * @param key metadata key + * @returns 0 on success, negative error code on failure + */ +CEPH_RADOS_API int rados_application_metadata_remove(rados_ioctx_t io, + const char *app_name, + const char *key); + +/** + * List all metadata key/value pairs associated with an application. + * + * This iterates over all metadata, key_len and val_len are filled in + * with the number of bytes put into the keys and values buffers. + * + * If the provided buffers are too short, the required lengths are filled + * in and -ERANGE is returned. Otherwise, the buffers are filled with + * the keys and values of the metadata, with a '\0' after each. + * + * @param ioctx pool ioctx + * @param app_name application name + * @param keys buffer in which to store key names + * @param keys_len number of bytes in keys buffer + * @param values buffer in which to store values + * @param vals_len number of bytes in values buffer + * @returns 0 on succcess, negative error code on failure + * @returns -ERANGE if either buffer is too short + */ +CEPH_RADOS_API int rados_application_metadata_list(rados_ioctx_t io, + const char *app_name, + char *keys, size_t *key_len, + char *values, + size_t *vals_len); + /** * @name Mon/OSD/PG Commands * diff --git a/src/include/rados/librados.hpp b/src/include/rados/librados.hpp index 0fc93a73361..830d3902650 100644 --- a/src/include/rados/librados.hpp +++ b/src/include/rados/librados.hpp @@ -1250,6 +1250,19 @@ namespace librados void set_osdmap_full_try(); void unset_osdmap_full_try(); + int application_enable(const std::string& app_name, bool force); + int application_list(std::set *app_names); + int application_metadata_get(const std::string& app_name, + const std::string &key, + std::string *value); + int application_metadata_set(const std::string& app_name, + const std::string &key, + const std::string& value); + int application_metadata_remove(const std::string& app_name, + const std::string &key); + int application_metadata_list(const std::string& app_name, + std::map *values); + private: /* You can only get IoCtx instances from Rados */ IoCtx(IoCtxImpl *io_ctx_impl_); diff --git a/src/librados/IoCtxImpl.cc b/src/librados/IoCtxImpl.cc index d0d5a1ae507..d41b5a3d9ce 100644 --- a/src/librados/IoCtxImpl.cc +++ b/src/librados/IoCtxImpl.cc @@ -2080,3 +2080,153 @@ void librados::IoCtxImpl::object_list_slice( hobject_t::_reverse_bits(rev_finish), poolid, string()); } +int librados::IoCtxImpl::application_enable(const std::string& app_name, + bool force) +{ + // pre-Luminous clusters will return -EINVAL and application won't be + // preserved until Luminous is configured as minimim version. + if (!client->get_required_monitor_features().contains_all( + ceph::features::mon::FEATURE_LUMINOUS)) { + return -EOPNOTSUPP; + } + + std::stringstream cmd; + cmd << "{" + << "\"prefix\": \"osd pool application enable\"," + << "\"pool\": \"" << get_cached_pool_name() << "\"," + << "\"app\": \"" << app_name << "\""; + if (force) { + cmd << ",\"force\":\"--yes-i-really-mean-it\""; + } + cmd << "}"; + + std::vector cmds; + cmds.push_back(cmd.str()); + bufferlist inbl; + int r = client->mon_command(cmds, inbl, nullptr, nullptr); + if (r < 0) { + return r; + } + + // ensure we have the latest osd map epoch before proceeding + return client->wait_for_latest_osdmap(); +} + +int librados::IoCtxImpl::application_list(std::set *app_names) +{ + int r = 0; + app_names->clear(); + objecter->with_osdmap([&](const OSDMap& o) { + auto pg_pool = o.get_pg_pool(poolid); + if (pg_pool == nullptr) { + r = -ENOENT; + return; + } + + for (auto &pair : pg_pool->application_metadata) { + app_names->insert(pair.first); + } + }); + return r; +} + +int librados::IoCtxImpl::application_metadata_get(const std::string& app_name, + const std::string &key, + std::string* value) +{ + int r = 0; + objecter->with_osdmap([&](const OSDMap& o) { + auto pg_pool = o.get_pg_pool(poolid); + if (pg_pool == nullptr) { + r = -ENOENT; + return; + } + + auto app_it = pg_pool->application_metadata.find(app_name); + if (app_it == pg_pool->application_metadata.end()) { + r = -ENOENT; + return; + } + + auto it = app_it->second.find(key); + if (it == app_it->second.end()) { + r = -ENOENT; + return; + } + + *value = it->second; + }); + return r; +} + +int librados::IoCtxImpl::application_metadata_set(const std::string& app_name, + const std::string &key, + const std::string& value) +{ + std::stringstream cmd; + cmd << "{" + << "\"prefix\":\"osd pool application set\"," + << "\"pool\":\"" << get_cached_pool_name() << "\"," + << "\"app\":\"" << app_name << "\"," + << "\"key\":\"" << key << "\"," + << "\"value\":\"" << value << "\"" + << "}"; + + std::vector cmds; + cmds.push_back(cmd.str()); + bufferlist inbl; + int r = client->mon_command(cmds, inbl, nullptr, nullptr); + if (r < 0) { + return r; + } + + // ensure we have the latest osd map epoch before proceeding + return client->wait_for_latest_osdmap(); +} + +int librados::IoCtxImpl::application_metadata_remove(const std::string& app_name, + const std::string &key) +{ + std::stringstream cmd; + cmd << "{" + << "\"prefix\":\"osd pool application rm\"," + << "\"pool\":\"" << get_cached_pool_name() << "\"," + << "\"app\":\"" << app_name << "\"," + << "\"key\":\"" << key << "\"" + << "}"; + + std::vector cmds; + cmds.push_back(cmd.str()); + bufferlist inbl; + int r = client->mon_command(cmds, inbl, nullptr, nullptr); + if (r < 0) { + return r; + } + + // ensure we have the latest osd map epoch before proceeding + return client->wait_for_latest_osdmap(); +} + +int librados::IoCtxImpl::application_metadata_list(const std::string& app_name, + std::map *values) +{ + int r = 0; + values->clear(); + objecter->with_osdmap([&](const OSDMap& o) { + auto pg_pool = o.get_pg_pool(poolid); + if (pg_pool == nullptr) { + r = -ENOENT; + return; + } + + auto it = pg_pool->application_metadata.find(app_name); + if (it == pg_pool->application_metadata.end()) { + r = -ENOENT; + return; + } + + *values = it->second; + }); + return r; +} + diff --git a/src/librados/IoCtxImpl.h b/src/librados/IoCtxImpl.h index 633cf9f7b0b..eda0cf0c7ef 100644 --- a/src/librados/IoCtxImpl.h +++ b/src/librados/IoCtxImpl.h @@ -282,6 +282,19 @@ struct librados::IoCtxImpl { int cache_pin(const object_t& oid); int cache_unpin(const object_t& oid); + int application_enable(const std::string& app_name, bool force); + int application_list(std::set *app_names); + int application_metadata_get(const std::string& app_name, + const std::string &key, + std::string* value); + int application_metadata_set(const std::string& app_name, + const std::string &key, + const std::string& value); + int application_metadata_remove(const std::string& app_name, + const std::string &key); + int application_metadata_list(const std::string& app_name, + std::map *values); + }; #endif diff --git a/src/librados/RadosClient.cc b/src/librados/RadosClient.cc index c90f3c129d4..9bc45970e8d 100644 --- a/src/librados/RadosClient.cc +++ b/src/librados/RadosClient.cc @@ -1069,3 +1069,8 @@ int librados::RadosClient::service_daemon_update_status( } return mgrclient.service_daemon_update_status(status); } + +mon_feature_t librados::RadosClient::get_required_monitor_features() const +{ + return monclient.monmap.get_required_features(); +} diff --git a/src/librados/RadosClient.h b/src/librados/RadosClient.h index 6c61c7f3fb6..cd3063d3e9c 100644 --- a/src/librados/RadosClient.h +++ b/src/librados/RadosClient.h @@ -162,6 +162,8 @@ public: const std::map& metadata); ///< static metadata about daemon int service_daemon_update_status( const std::map& status); + + mon_feature_t get_required_monitor_features() const; }; #endif diff --git a/src/librados/librados.cc b/src/librados/librados.cc index b1002194a4f..ffe7fc2687c 100644 --- a/src/librados/librados.cc +++ b/src/librados/librados.cc @@ -3087,6 +3087,128 @@ extern "C" int rados_blacklist_add(rados_t cluster, char *client_address, return radosp->blacklist_add(client_address, expire_seconds); } +extern "C" int rados_application_enable(rados_ioctx_t io, const char *app_name, + int force) +{ + librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io; + return ctx->application_enable(app_name, force != 0); +} + +extern "C" int rados_application_list(rados_ioctx_t io, char *values, + size_t *values_len) +{ + librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io; + std::set app_names; + int r = ctx->application_list(&app_names); + if (r < 0) { + return r; + } + + size_t total_len = 0; + for (auto app_name : app_names) { + total_len += app_name.size() + 1; + } + + if (*values_len < total_len) { + *values_len = total_len; + return -ERANGE; + } + + char *values_p = values; + for (auto app_name : app_names) { + size_t len = app_name.size() + 1; + strncpy(values_p, app_name.c_str(), len); + values_p += len; + } + *values_p = '\0'; + *values_len = total_len; + return 0; +} + +extern "C" int rados_application_metadata_get(rados_ioctx_t io, + const char *app_name, + const char *key, char *value, + size_t *value_len) +{ + librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io; + std::string value_str; + int r = ctx->application_metadata_get(app_name, key, &value_str); + if (r < 0) { + return r; + } + + size_t len = value_str.size() + 1; + if (*value_len < len) { + *value_len = len; + return -ERANGE; + } + + strncpy(value, value_str.c_str(), len); + *value_len = len; + return 0; +} + +extern "C" int rados_application_metadata_set(rados_ioctx_t io, + const char *app_name, + const char *key, + const char *value) +{ + librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io; + return ctx->application_metadata_set(app_name, key, value); +} + +extern "C" int rados_application_metadata_remove(rados_ioctx_t io, + const char *app_name, + const char *key) +{ + librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io; + return ctx->application_metadata_remove(app_name, key); +} + +extern "C" int rados_application_metadata_list(rados_ioctx_t io, + const char *app_name, + char *keys, size_t *keys_len, + char *values, size_t *vals_len) +{ + librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io; + std::map metadata; + int r = ctx->application_metadata_list(app_name, &metadata); + if (r < 0) { + return r; + } + + size_t total_key_len = 0; + size_t total_val_len = 0; + for (auto pair : metadata) { + total_key_len += pair.first.size() + 1; + total_val_len += pair.second.size() + 1; + } + + if (*keys_len < total_key_len || *vals_len < total_val_len) { + *keys_len = total_key_len; + *vals_len = total_val_len; + return -ERANGE; + } + + char *keys_p = keys; + char *vals_p = values; + for (auto pair : metadata) { + size_t key_len = pair.first.size() + 1; + strncpy(keys_p, pair.first.c_str(), key_len); + keys_p += key_len; + + size_t val_len = pair.second.size() + 1; + strncpy(vals_p, pair.second.c_str(), val_len); + vals_p += val_len; + } + *keys_p = '\0'; + *keys_len = total_key_len; + + *vals_p = '\0'; + *vals_len = total_val_len; + return 0; +} + extern "C" int rados_pool_list(rados_t cluster, char *buf, size_t len) { tracepoint(librados, rados_pool_list_enter, cluster, len); @@ -6341,3 +6463,41 @@ void librados::IoCtx::object_list_slice( (hobject_t*)(split_finish->c_cursor)); } +int librados::IoCtx::application_enable(const std::string& app_name, + bool force) +{ + return io_ctx_impl->application_enable(app_name, force); +} + +int librados::IoCtx::application_list(std::set *app_names) +{ + return io_ctx_impl->application_list(app_names); +} + +int librados::IoCtx::application_metadata_get(const std::string& app_name, + const std::string &key, + std::string* value) +{ + return io_ctx_impl->application_metadata_get(app_name, key, value); +} + +int librados::IoCtx::application_metadata_set(const std::string& app_name, + const std::string &key, + const std::string& value) +{ + return io_ctx_impl->application_metadata_set(app_name, key, value); +} + +int librados::IoCtx::application_metadata_remove(const std::string& app_name, + const std::string &key) +{ + return io_ctx_impl->application_metadata_remove(app_name, key); +} + +int librados::IoCtx::application_metadata_list(const std::string& app_name, + std::map *values) +{ + return io_ctx_impl->application_metadata_list(app_name, values); +} + + diff --git a/src/pybind/rados/rados.pyx b/src/pybind/rados/rados.pyx index 8e2b99d9b07..fe68c16321e 100644 --- a/src/pybind/rados/rados.pyx +++ b/src/pybind/rados/rados.pyx @@ -151,7 +151,21 @@ cdef extern from "rados/librados.h" nogil: int rados_cluster_stat(rados_t cluster, rados_cluster_stat_t *result) int rados_cluster_fsid(rados_t cluster, char *buf, size_t len) int rados_blacklist_add(rados_t cluster, char *client_address, uint32_t expire_seconds) - + int rados_application_enable(rados_ioctx_t io, const char *app_name, + int force) + int rados_application_list(rados_ioctx_t io, char *values, + size_t *values_len) + int rados_application_metadata_get(rados_ioctx_t io, const char *app_name, + const char *key, char *value, + size_t *value_len) + int rados_application_metadata_set(rados_ioctx_t io, const char *app_name, + const char *key, const char *value) + int rados_application_metadata_remove(rados_ioctx_t io, + const char *app_name, const char *key) + int rados_application_metadata_list(rados_ioctx_t io, + const char *app_name, char *keys, + size_t *key_len, char *values, + size_t *value_len) int rados_ping_monitor(rados_t cluster, const char *mon_id, char **outstr, size_t *outstrlen) int rados_mon_command(rados_t cluster, const char **cmd, size_t cmdlen, const char *inbuf, size_t inbuflen, @@ -3562,6 +3576,141 @@ returned %d, but should return zero on success." % (self.name, ret)) if ret < 0: raise make_ex(ret, "Ioctx.rados_lock_exclusive(%s): failed to set lock %s on %s" % (self.name, name, key)) + def application_enable(self, app_name, force=False): + """ + Enable an application on an OSD pool + + :param app_name: application name + :type app_name: str + :param force: False if only a single app should exist per pool + :type expire_seconds: boool + + :raises: :class:`Error` + """ + app_name = cstr(app_name, 'app_name') + cdef: + char *_app_name = app_name + int _force = (1 if force else 0) + + with nogil: + ret = rados_application_enable(self.io, _app_name, _force) + if ret < 0: + raise make_ex(ret, "error enabling application") + + def application_list(self): + """ + Returns a list of enabled applications + + :returns: list of app name string + """ + cdef: + size_t length = 128 + char *apps = NULL + + try: + while True: + apps = realloc_chk(apps, length) + with nogil: + ret = rados_application_list(self.io, apps, &length) + if ret == 0: + return [decode_cstr(app) for app in + apps[:length].split(b'\0') if app] + elif ret == -errno.ENOENT: + return None + elif ret == -errno.ERANGE: + pass + else: + raise make_ex(ret, "error listing applications") + finally: + free(apps) + + def application_metadata_set(self, app_name, key, value): + """ + Sets application metadata on an OSD pool + + :param app_name: application name + :type app_name: str + :param key: metadata key + :type key: str + :param value: metadata value + :type value: str + + :raises: :class:`Error` + """ + app_name = cstr(app_name, 'app_name') + key = cstr(key, 'key') + value = cstr(value, 'value') + cdef: + char *_app_name = app_name + char *_key = key + char *_value = value + + with nogil: + ret = rados_application_metadata_set(self.io, _app_name, _key, + _value) + if ret < 0: + raise make_ex(ret, "error setting application metadata") + + def application_metadata_remove(self, app_name, key): + """ + Remove application metadata from an OSD pool + + :param app_name: application name + :type app_name: str + :param key: metadata key + :type key: str + + :raises: :class:`Error` + """ + app_name = cstr(app_name, 'app_name') + key = cstr(key, 'key') + cdef: + char *_app_name = app_name + char *_key = key + + with nogil: + ret = rados_application_metadata_remove(self.io, _app_name, _key) + if ret < 0: + raise make_ex(ret, "error removing application metadata") + + def application_metadata_list(self, app_name): + """ + Returns a list of enabled applications + + :param app_name: application name + :type app_name: str + :returns: list of key/value tuples + """ + app_name = cstr(app_name, 'app_name') + cdef: + char *_app_name = app_name + size_t key_length = 128 + size_t val_length = 128 + char *c_keys = NULL + char *c_vals = NULL + + try: + while True: + c_keys = realloc_chk(c_keys, key_length) + c_vals = realloc_chk(c_vals, val_length) + with nogil: + ret = rados_application_metadata_list(self.io, _app_name, + c_keys, &key_length, + c_vals, &val_length) + if ret == 0: + keys = [decode_cstr(key) for key in + c_keys[:key_length].split(b'\0') if key] + vals = [decode_cstr(val) for val in + c_vals[:val_length].split(b'\0') if val] + return zip(keys, vals) + elif ret == -errno.ERANGE: + pass + else: + raise make_ex(ret, "error listing application metadata") + finally: + free(c_keys) + free(c_vals) + def set_object_locator(func): def retfunc(self, *args, **kwargs): diff --git a/src/test/librados/misc.cc b/src/test/librados/misc.cc index 3454861974a..290829d8796 100644 --- a/src/test/librados/misc.cc +++ b/src/test/librados/misc.cc @@ -1229,3 +1229,95 @@ TEST_F(LibRadosMisc, CmpExt) { ASSERT_EQ(-MAX_ERRNO - 5, rados_cmpext(ioctx, "cmpextpp", mismatch_str, sizeof(mismatch_str), 0)); } + +TEST_F(LibRadosMisc, Applications) { + char apps[128]; + size_t app_len; + + app_len = sizeof(apps); + ASSERT_EQ(0, rados_application_list(ioctx, apps, &app_len)); + ASSERT_EQ(0U, app_len); + + ASSERT_EQ(0, rados_application_enable(ioctx, "app1", 0)); + ASSERT_EQ(-EPERM, rados_application_enable(ioctx, "app2", 0)); + ASSERT_EQ(0, rados_application_enable(ioctx, "app2", 1)); + + ASSERT_EQ(-ERANGE, rados_application_list(ioctx, apps, &app_len)); + ASSERT_EQ(10U, app_len); + ASSERT_EQ(0, rados_application_list(ioctx, apps, &app_len)); + ASSERT_EQ(10U, app_len); + ASSERT_EQ(0, memcmp("app1\0app2\0", apps, app_len)); + + char keys[128]; + char vals[128]; + size_t key_len; + size_t val_len; + + key_len = sizeof(keys); + val_len = sizeof(vals); + ASSERT_EQ(-ENOENT, rados_application_metadata_list(ioctx, "dne", keys, + &key_len, vals, &val_len)); + ASSERT_EQ(0, rados_application_metadata_list(ioctx, "app1", keys, &key_len, + vals, &val_len)); + ASSERT_EQ(0U, key_len); + ASSERT_EQ(0U, val_len); + + ASSERT_EQ(-ENOENT, rados_application_metadata_set(ioctx, "dne", "key", + "value")); + ASSERT_EQ(0, rados_application_metadata_set(ioctx, "app1", "key1", "value1")); + ASSERT_EQ(0, rados_application_metadata_set(ioctx, "app1", "key2", "value2")); + + ASSERT_EQ(-ERANGE, rados_application_metadata_list(ioctx, "app1", keys, + &key_len, vals, &val_len)); + ASSERT_EQ(10U, key_len); + ASSERT_EQ(14U, val_len); + ASSERT_EQ(0, rados_application_metadata_list(ioctx, "app1", keys, &key_len, + vals, &val_len)); + ASSERT_EQ(10U, key_len); + ASSERT_EQ(14U, val_len); + ASSERT_EQ(0, memcmp("key1\0key2\0", keys, key_len)); + ASSERT_EQ(0, memcmp("value1\0value2\0", vals, val_len)); + + ASSERT_EQ(0, rados_application_metadata_remove(ioctx, "app1", "key1")); + ASSERT_EQ(0, rados_application_metadata_list(ioctx, "app1", keys, &key_len, + vals, &val_len)); + ASSERT_EQ(5U, key_len); + ASSERT_EQ(7U, val_len); + ASSERT_EQ(0, memcmp("key2\0", keys, key_len)); + ASSERT_EQ(0, memcmp("value2\0", vals, val_len)); +} + +TEST_F(LibRadosMiscPP, Applications) { + std::set expected_apps; + std::set apps; + ASSERT_EQ(0, ioctx.application_list(&apps)); + ASSERT_EQ(expected_apps, apps); + + ASSERT_EQ(0, ioctx.application_enable("app1", false)); + ASSERT_EQ(-EPERM, ioctx.application_enable("app2", false)); + ASSERT_EQ(0, ioctx.application_enable("app2", true)); + + expected_apps = {"app1", "app2"}; + ASSERT_EQ(0, ioctx.application_list(&apps)); + ASSERT_EQ(expected_apps, apps); + + std::map expected_meta; + std::map meta; + ASSERT_EQ(-ENOENT, ioctx.application_metadata_list("dne", &meta)); + ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta)); + ASSERT_EQ(expected_meta, meta); + + ASSERT_EQ(-ENOENT, ioctx.application_metadata_set("dne", "key1", "value1")); + ASSERT_EQ(0, ioctx.application_metadata_set("app1", "key1", "value1")); + ASSERT_EQ(0, ioctx.application_metadata_set("app1", "key2", "value2")); + + expected_meta = {{"key1", "value1"}, {"key2", "value2"}}; + ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta)); + ASSERT_EQ(expected_meta, meta); + + ASSERT_EQ(0, ioctx.application_metadata_remove("app1", "key1")); + + expected_meta = {{"key2", "value2"}}; + ASSERT_EQ(0, ioctx.application_metadata_list("app1", &meta)); + ASSERT_EQ(expected_meta, meta); +} diff --git a/src/test/pybind/test_rados.py b/src/test/pybind/test_rados.py index a4e1efd852e..ca496528df8 100644 --- a/src/test/pybind/test_rados.py +++ b/src/test/pybind/test_rados.py @@ -859,6 +859,28 @@ class TestIoctx(object): [i.remove() for i in self.ioctx.list_objects()] + def test_applications(self): + eq([], self.ioctx.application_list()) + + self.ioctx.application_enable("app1") + assert_raises(Error, self.ioctx.application_enable, "app2") + self.ioctx.application_enable("app2", True) + + assert_raises(Error, self.ioctx.application_metadata_list, "dne") + eq([], self.ioctx.application_metadata_list("app1")) + + assert_raises(Error, self.ioctx.application_metadata_set, "dne", "key", + "key") + self.ioctx.application_metadata_set("app1", "key1", "val1") + self.ioctx.application_metadata_set("app1", "key2", "val2") + self.ioctx.application_metadata_set("app2", "key1", "val1") + + eq([("key1", "val1"), ("key2", "val2")], + self.ioctx.application_metadata_list("app1")) + + self.ioctx.application_metadata_remove("app1", "key1") + eq([("key2", "val2")], self.ioctx.application_metadata_list("app1")) + class TestObject(object): def setUp(self): -- 2.39.5