size_t group_snap_info_size,
size_t num_entries);
+CEPH_RBD_API int rbd_namespace_create(rados_ioctx_t io,
+ const char *namespace_name);
+CEPH_RBD_API int rbd_namespace_remove(rados_ioctx_t io,
+ const char *namespace_name);
+CEPH_RBD_API int rbd_namespace_list(rados_ioctx_t io, char *namespace_names,
+ size_t *size);
+
#ifdef __cplusplus
}
#endif
std::vector<group_snap_info_t> *snaps,
size_t group_snap_info_size);
+ int namespace_create(IoCtx& ioctx, const char *namespace_name);
+ int namespace_remove(IoCtx& ioctx, const char *namespace_name);
+ int namespace_list(IoCtx& io_ctx, std::vector<std::string>* namespace_names);
+
private:
/* We don't allow assignment or copying */
RBD(const RBD& rhs);
api/Group.cc
api/Image.cc
api/Mirror.cc
+ api/Namespace.cc
api/Snapshot.cc
cache/ImageWriteback.cc
cache/ObjectCacherObjectDispatch.cc
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/errno.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/api/Namespace.h"
+#include "librbd/ImageCtx.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::api::Namespace: " << __func__ << ": "
+
+namespace librbd {
+namespace api {
+
+template <typename I>
+int Namespace<I>::create(librados::IoCtx& io_ctx, const std::string& name)
+{
+ CephContext *cct = (CephContext *)io_ctx.cct();
+ ldout(cct, 5) << "name=" << name << dendl;
+
+ if (name.empty()) {
+ return -EINVAL;
+ }
+
+ librados::IoCtx default_ns_ctx;
+ default_ns_ctx.dup(io_ctx);
+ default_ns_ctx.set_namespace("");
+
+ int r = cls_client::namespace_add(&default_ns_ctx, name);
+ if (r < 0) {
+ lderr(cct) << "failed to add namespace: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ librados::IoCtx ns_ctx;
+ ns_ctx.dup(io_ctx);
+ ns_ctx.set_namespace(name);
+
+ r = cls_client::dir_state_set(&ns_ctx, RBD_DIRECTORY,
+ cls::rbd::DIRECTORY_STATE_READY);
+ if (r < 0) {
+ lderr(cct) << "failed to initialize image directory: " << cpp_strerror(r)
+ << dendl;
+ goto rollback;
+ }
+
+ return 0;
+
+rollback:
+ int ret_val = cls_client::namespace_remove(&default_ns_ctx, name);
+ if (ret_val < 0) {
+ lderr(cct) << "failed to remove namespace: " << cpp_strerror(r) << dendl;
+ }
+
+ return r;
+}
+
+template <typename I>
+int Namespace<I>::remove(librados::IoCtx& io_ctx, const std::string& name)
+{
+ CephContext *cct = (CephContext *)io_ctx.cct();
+ ldout(cct, 5) << "name=" << name << dendl;
+
+ if (name.empty()) {
+ return -EINVAL;
+ }
+
+ librados::IoCtx default_ns_ctx;
+ default_ns_ctx.dup(io_ctx);
+ default_ns_ctx.set_namespace("");
+
+ librados::IoCtx ns_ctx;
+ ns_ctx.dup(io_ctx);
+ ns_ctx.set_namespace(name);
+
+ std::map<std::string, cls::rbd::TrashImageSpec> trash_entries;
+
+ librados::ObjectWriteOperation dir_op;
+ librbd::cls_client::dir_state_set(
+ &dir_op, cls::rbd::DIRECTORY_STATE_ADD_DISABLED);
+ dir_op.remove();
+
+ int r = ns_ctx.operate(RBD_DIRECTORY, &dir_op);
+ if (r == -EBUSY) {
+ ldout(cct, 5) << "image directory not empty" << dendl;
+ goto rollback;
+ } else if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to disable the namespace: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ r = cls_client::trash_list(&ns_ctx, "", 1, &trash_entries);
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to list trash directory: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ } else if (!trash_entries.empty()) {
+ ldout(cct, 5) << "image trash not empty" << dendl;
+ goto rollback;
+ }
+
+ r = cls_client::namespace_remove(&default_ns_ctx, name);
+ if (r < 0) {
+ lderr(cct) << "failed to remove namespace: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ return 0;
+
+rollback:
+
+ r = librbd::cls_client::dir_state_set(
+ &ns_ctx, RBD_DIRECTORY, cls::rbd::DIRECTORY_STATE_READY);
+ if (r < 0) {
+ lderr(cct) << "failed to restore directory state: " << cpp_strerror(r)
+ << dendl;
+ }
+
+ return -EBUSY;
+}
+
+template <typename I>
+int Namespace<I>::list(IoCtx& io_ctx, vector<string> *names)
+{
+ CephContext *cct = (CephContext *)io_ctx.cct();
+ ldout(cct, 5) << dendl;
+
+ librados::IoCtx default_ns_ctx;
+ default_ns_ctx.dup(io_ctx);
+ default_ns_ctx.set_namespace("");
+
+ int r;
+ int max_read = 1024;
+ std::string last_read = "";
+ do {
+ std::list<std::string> name_list;
+ r = cls_client::namespace_list(&default_ns_ctx, last_read, max_read,
+ &name_list);
+ if (r == -ENOENT) {
+ return 0;
+ } else if (r < 0) {
+ lderr(cct) << "error listing namespaces: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ names->insert(names->end(), name_list.begin(), name_list.end());
+ if (!name_list.empty()) {
+ last_read = name_list.back();
+ }
+ r = name_list.size();
+ } while (r == max_read);
+
+ return 0;
+}
+
+} // namespace api
+} // namespace librbd
+
+template class librbd::api::Namespace<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_NAMESPACE_H
+#define CEPH_LIBRBD_API_NAMESPACE_H
+
+#include "include/rbd/librbd.hpp"
+#include <string>
+#include <vector>
+
+namespace librados { struct IoCtx; }
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace api {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+struct Namespace {
+
+ static int create(librados::IoCtx& io_ctx, const std::string& name);
+ static int remove(librados::IoCtx& io_ctx, const std::string& name);
+ static int list(librados::IoCtx& io_ctx, std::vector<std::string>* names);
+
+};
+
+} // namespace api
+} // namespace librbd
+
+extern template class librbd::api::Namespace<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_API_NAMESPACE_H
#include "librbd/api/Group.h"
#include "librbd/api/Image.h"
#include "librbd/api/Mirror.h"
+#include "librbd/api/Namespace.h"
#include "librbd/api/Snapshot.h"
#include "librbd/io/AioCompletion.h"
#include "librbd/io/ImageRequestWQ.h"
return r;
}
+ int RBD::namespace_create(IoCtx& io_ctx, const char *namespace_name) {
+ return librbd::api::Namespace<>::create(io_ctx, namespace_name);
+ }
+
+ int RBD::namespace_remove(IoCtx& io_ctx, const char *namespace_name) {
+ return librbd::api::Namespace<>::remove(io_ctx, namespace_name);
+ }
+
+ int RBD::namespace_list(IoCtx& io_ctx,
+ std::vector<std::string>* namespace_names) {
+ return librbd::api::Namespace<>::list(io_ctx, namespace_names);
+ }
+
int RBD::list(IoCtx& io_ctx, vector<string>& names)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
return r;
}
+extern "C" int rbd_namespace_create(rados_ioctx_t io,
+ const char *namespace_name) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(io, io_ctx);
+
+ return librbd::api::Namespace<>::create(io_ctx, namespace_name);
+}
+
+extern "C" int rbd_namespace_remove(rados_ioctx_t io,
+ const char *namespace_name) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(io, io_ctx);
+
+ return librbd::api::Namespace<>::remove(io_ctx, namespace_name);
+}
+
+extern "C" int rbd_namespace_list(rados_ioctx_t io, char *names, size_t *size) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(io, io_ctx);
+
+ if (names == nullptr || size == nullptr) {
+ return -EINVAL;
+ }
+
+ std::vector<std::string> cpp_names;
+ int r = librbd::api::Namespace<>::list(io_ctx, &cpp_names);
+ if (r < 0) {
+ return r;
+ }
+
+ size_t expected_size = 0;
+ for (size_t i = 0; i < cpp_names.size(); i++) {
+ expected_size += cpp_names[i].size() + 1;
+ }
+ if (*size < expected_size) {
+ *size = expected_size;
+ return -ERANGE;
+ }
+
+ for (int i = 0; i < (int)cpp_names.size(); i++) {
+ const char* name = cpp_names[i].c_str();
+ strcpy(names, name);
+ names += strlen(names) + 1;
+ }
+
+ return (int)expected_size;
+}
+
extern "C" int rbd_copy(rbd_image_t image, rados_ioctx_t dest_p,
const char *destname)
{
ASSERT_EQ(0, image.snap_set_by_id(CEPH_NOSNAP));
}
+TEST_F(TestLibRBD, Namespaces) {
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+ rados_remove(ioctx, RBD_NAMESPACE);
+
+ ASSERT_EQ(0, rbd_namespace_create(ioctx, "name1"));
+ ASSERT_EQ(0, rbd_namespace_create(ioctx, "name2"));
+ ASSERT_EQ(0, rbd_namespace_create(ioctx, "name3"));
+ ASSERT_EQ(0, rbd_namespace_remove(ioctx, "name2"));
+
+ char names[1024];
+ size_t max_size = sizeof(names);
+ int len = rbd_namespace_list(ioctx, names, &max_size);
+
+ std::vector<std::string> cpp_names;
+ for (char* cur_name = names; cur_name < names + len; ) {
+ cpp_names.push_back(cur_name);
+ cur_name += strlen(cur_name) + 1;
+ }
+ ASSERT_EQ(2U, cpp_names.size());
+ ASSERT_EQ("name1", cpp_names[0]);
+ ASSERT_EQ("name3", cpp_names[1]);
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, NamespacesPP) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_NAMESPACE);
+
+ librbd::RBD rbd;
+ ASSERT_EQ(-EINVAL, rbd.namespace_create(ioctx, ""));
+ ASSERT_EQ(-EINVAL, rbd.namespace_remove(ioctx, ""));
+
+ ASSERT_EQ(0, rbd.namespace_create(ioctx, "name1"));
+ ASSERT_EQ(-EEXIST, rbd.namespace_create(ioctx, "name1"));
+ ASSERT_EQ(0, rbd.namespace_create(ioctx, "name2"));
+ ASSERT_EQ(0, rbd.namespace_create(ioctx, "name3"));
+ ASSERT_EQ(0, rbd.namespace_remove(ioctx, "name2"));
+ ASSERT_EQ(-ENOENT, rbd.namespace_remove(ioctx, "name2"));
+
+ std::vector<std::string> names;
+ ASSERT_EQ(0, rbd.namespace_list(ioctx, &names));
+ ASSERT_EQ(2U, names.size());
+
+ ASSERT_EQ(2U, names.size());
+ ASSERT_EQ("name1", names[0]);
+ ASSERT_EQ("name3", names[1]);
+}
+
// poorman's assert()
namespace ceph {
void __ceph_assert_fail(const char *assertion, const char *file, int line,