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<std::string> 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<std::string, bufferlist> raw_data;
+ int max_read = std::min<int32_t>(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!");
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",
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);
}
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<std::string> *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<std::string> *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
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<std::string> *entries);
+ int namespace_list(librados::IoCtx *ioctx,
+ const std::string &start, uint64_t max_return,
+ std::list<std::string> *entries);
+
} // namespace cls_client
} // namespace librbd
#endif // CEPH_LIBRBD_CLS_RBD_CLIENT_H
#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
ASSERT_EQ(snap.name, received_snap.name);
ASSERT_EQ(snap.state, received_snap.state);
}
+
TEST_F(TestClsRbd, trash_methods)
{
librados::IoCtx ioctx;
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<std::string> 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());
+}