From 4c109366a66bb4ebf3c9d3d9665e393c5ec040c1 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Fri, 15 Jun 2018 09:20:12 -0400 Subject: [PATCH] cls/rbd: add namespace_create/remove/list API methods Signed-off-by: Jason Dillaman --- src/cls/rbd/cls_rbd.cc | 155 ++++++++++++++++++++++++++++++- src/cls/rbd/cls_rbd_client.cc | 72 ++++++++++++++ src/cls/rbd/cls_rbd_client.h | 15 +++ src/include/rbd_types.h | 1 + src/test/cls_rbd/test_cls_rbd.cc | 35 +++++++ 5 files changed, 277 insertions(+), 1 deletion(-) diff --git a/src/cls/rbd/cls_rbd.cc b/src/cls/rbd/cls_rbd.cc index f71e5867d8ec9..497580fa814fe 100644 --- a/src/cls/rbd/cls_rbd.cc +++ b/src/cls/rbd/cls_rbd.cc @@ -6167,6 +6167,148 @@ int trash_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) return r; } +namespace nspace { + +const std::string NAME_KEY_PREFIX("name_"); + +std::string key_for_name(const std::string& name) { + return NAME_KEY_PREFIX + name; +} + +std::string name_from_key(const std::string &key) { + return key.substr(NAME_KEY_PREFIX.size()); +} + +} // namespace nspace + +/** + * Add a namespace to the namespace directory. + * + * Input: + * @param name the name of the namespace + * + * Output: + * @returns -EEXIST if the namespace is already exists + * @returns 0 on success, negative error code on failure + */ +int namespace_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + std::string name; + try { + auto iter = in->cbegin(); + decode(name, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::string key(nspace::key_for_name(name)); + bufferlist value; + int r = cls_cxx_map_get_val(hctx, key, &value); + if (r < 0 && r != -ENOENT) { + return r; + } else if (r == 0) { + return -EEXIST; + } + + r = cls_cxx_map_set_val(hctx, key, &value); + if (r < 0) { + CLS_ERR("failed to set omap key: %s", key.c_str()); + return r; + } + + return 0; +} + +/** + * Add a namespace to the namespace directory. + * + * Input: + * @param name the name of the namespace + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int namespace_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + std::string name; + try { + auto iter = in->cbegin(); + decode(name, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::string key(nspace::key_for_name(name)); + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, key, &bl); + if (r < 0) { + return r; + } + + r = cls_cxx_map_remove_key(hctx, key); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Returns the list of namespaces in the rbd_namespace object + * + * Input: + * @param start_after which name to begin listing after + * (use the empty string to start at the beginning) + * @param max_return the maximum number of names to list + * + * Output: + * @param data list of namespace names + * @returns 0 on success, negative error code on failure + */ +int namespace_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + string start_after; + uint64_t max_return; + try { + auto iter = in->cbegin(); + decode(start_after, iter); + decode(max_return, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + std::list data; + std::string last_read = nspace::key_for_name(start_after); + bool more = true; + + CLS_LOG(20, "namespace_list"); + while (data.size() < max_return) { + std::map raw_data; + int max_read = std::min(RBD_MAX_KEYS_READ, + max_return - data.size()); + int r = cls_cxx_map_get_vals(hctx, last_read, nspace::NAME_KEY_PREFIX, + max_read, &raw_data, &more); + if (r < 0) { + CLS_ERR("failed to read the vals off of disk: %s", + cpp_strerror(r).c_str()); + return r; + } + + for (auto& it : raw_data) { + data.push_back(nspace::name_from_key(it.first)); + } + + if (raw_data.empty() || !more) { + break; + } + + last_read = raw_data.rbegin()->first; + } + + encode(data, *out); + return 0; +} + CLS_INIT(rbd) { CLS_LOG(20, "Loaded rbd class!"); @@ -6277,6 +6419,9 @@ CLS_INIT(rbd) cls_method_handle_t h_trash_remove; cls_method_handle_t h_trash_list; cls_method_handle_t h_trash_get; + cls_method_handle_t h_namespace_add; + cls_method_handle_t h_namespace_remove; + cls_method_handle_t h_namespace_list; cls_register("rbd", &h_class); cls_register_cxx_method(h_class, "create", @@ -6604,5 +6749,13 @@ CLS_INIT(rbd) CLS_METHOD_RD, trash_get, &h_trash_get); - return; + /* rbd_namespace object methods */ + cls_register_cxx_method(h_class, "namespace_add", + CLS_METHOD_RD | CLS_METHOD_WR, + namespace_add, &h_namespace_add); + cls_register_cxx_method(h_class, "namespace_remove", + CLS_METHOD_RD | CLS_METHOD_WR, + namespace_remove, &h_namespace_remove); + cls_register_cxx_method(h_class, "namespace_list", CLS_METHOD_RD, + namespace_list, &h_namespace_list); } diff --git a/src/cls/rbd/cls_rbd_client.cc b/src/cls/rbd/cls_rbd_client.cc index fc831ff4136d7..daad92d561a6d 100644 --- a/src/cls/rbd/cls_rbd_client.cc +++ b/src/cls/rbd/cls_rbd_client.cc @@ -2500,5 +2500,77 @@ namespace librbd { return trash_get_finish(&it, trash_spec); } + void namespace_add(librados::ObjectWriteOperation *op, + const std::string &name) + { + bufferlist bl; + encode(name, bl); + op->exec("rbd", "namespace_add", bl); + } + + int namespace_add(librados::IoCtx *ioctx, const std::string &name) + { + librados::ObjectWriteOperation op; + namespace_add(&op, name); + + return ioctx->operate(RBD_NAMESPACE, &op); + } + + void namespace_remove(librados::ObjectWriteOperation *op, + const std::string &name) + { + bufferlist bl; + encode(name, bl); + op->exec("rbd", "namespace_remove", bl); + } + + int namespace_remove(librados::IoCtx *ioctx, const std::string &name) + { + librados::ObjectWriteOperation op; + namespace_remove(&op, name); + + return ioctx->operate(RBD_NAMESPACE, &op); + } + + void namespace_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return) + { + bufferlist bl; + encode(start, bl); + encode(max_return, bl); + op->exec("rbd", "namespace_list", bl); + } + + int namespace_list_finish(bufferlist::const_iterator *it, + std::list *entries) + { + assert(entries); + + try { + decode(*entries, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; + } + + int namespace_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + std::list *entries) + { + librados::ObjectReadOperation op; + namespace_list_start(&op, start, max_return); + + bufferlist out_bl; + int r = ioctx->operate(RBD_NAMESPACE, &op, &out_bl); + if (r < 0) { + return r; + } + + auto iter = out_bl.cbegin(); + return namespace_list_finish(&iter, entries); + } + } // namespace cls_client } // namespace librbd diff --git a/src/cls/rbd/cls_rbd_client.h b/src/cls/rbd/cls_rbd_client.h index 498f47fc871b2..298fc0a9e83d8 100644 --- a/src/cls/rbd/cls_rbd_client.h +++ b/src/cls/rbd/cls_rbd_client.h @@ -519,6 +519,21 @@ namespace librbd { int trash_get(librados::IoCtx *ioctx, const std::string &id, cls::rbd::TrashImageSpec *trash_spec); + // operations on rbd_namespace object + void namespace_add(librados::ObjectWriteOperation *op, + const std::string &name); + int namespace_add(librados::IoCtx *ioctx, const std::string &name); + void namespace_remove(librados::ObjectWriteOperation *op, + const std::string &name); + int namespace_remove(librados::IoCtx *ioctx, const std::string &name); + void namespace_list_start(librados::ObjectReadOperation *op, + const std::string &start, uint64_t max_return); + int namespace_list_finish(bufferlist::const_iterator *it, + std::list *entries); + int namespace_list(librados::IoCtx *ioctx, + const std::string &start, uint64_t max_return, + std::list *entries); + } // namespace cls_client } // namespace librbd #endif // CEPH_LIBRBD_CLS_RBD_CLIENT_H diff --git a/src/include/rbd_types.h b/src/include/rbd_types.h index df3f248000955..b7d680c109880 100644 --- a/src/include/rbd_types.h +++ b/src/include/rbd_types.h @@ -41,6 +41,7 @@ #define RBD_SUFFIX ".rbd" #define RBD_DIRECTORY "rbd_directory" #define RBD_INFO "rbd_info" +#define RBD_NAMESPACE "rbd_namespace" /* * rbd_children object in each pool contains omap entries diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc index 0534cbeccebff..9c888c9e0e762 100644 --- a/src/test/cls_rbd/test_cls_rbd.cc +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -2490,6 +2490,7 @@ TEST_F(TestClsRbd, group_snap_get_by_id) { ASSERT_EQ(snap.name, received_snap.name); ASSERT_EQ(snap.state, received_snap.state); } + TEST_F(TestClsRbd, trash_methods) { librados::IoCtx ioctx; @@ -2729,3 +2730,37 @@ TEST_F(TestClsRbd, clone_child) ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_CHILD) == 0ULL); } +TEST_F(TestClsRbd, namespace_methods) +{ + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx)); + + string name1 = "123456789"; + string name2 = "123456780"; + + std::list entries; + ASSERT_EQ(-ENOENT, namespace_list(&ioctx, "", 1024, &entries)); + + ASSERT_EQ(0, namespace_add(&ioctx, name1)); + ASSERT_EQ(-EEXIST, namespace_add(&ioctx, name1)); + + ASSERT_EQ(0, namespace_remove(&ioctx, name1)); + ASSERT_EQ(-ENOENT, namespace_remove(&ioctx, name1)); + + ASSERT_EQ(0, namespace_list(&ioctx, "", 1024, &entries)); + ASSERT_TRUE(entries.empty()); + + ASSERT_EQ(0, namespace_add(&ioctx, name1)); + ASSERT_EQ(0, namespace_add(&ioctx, name2)); + + ASSERT_EQ(0, namespace_list(&ioctx, "", 1, &entries)); + ASSERT_EQ(1U, entries.size()); + ASSERT_EQ(name2, entries.front()); + + ASSERT_EQ(0, namespace_list(&ioctx, name2, 1, &entries)); + ASSERT_EQ(1U, entries.size()); + ASSERT_EQ(name1, entries.front()); + + ASSERT_EQ(0, namespace_list(&ioctx, name1, 1, &entries)); + ASSERT_TRUE(entries.empty()); +} -- 2.39.5