cls_method_handle_t h_unlock_image;
cls_method_handle_t h_break_lock;
cls_method_handle_t h_list_locks;
+cls_method_handle_t h_get_id;
+cls_method_handle_t h_set_id;
cls_method_handle_t h_old_snapshots_list;
cls_method_handle_t h_old_snapshot_add;
cls_method_handle_t h_old_snapshot_remove;
return 0;
}
+static bool is_valid_id(const string &id) {
+ if (!id.size())
+ return false;
+ for (size_t i = 0; i < id.size(); ++i) {
+ if (!isalnum(id[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
/**
* Initialize the header with basic metadata.
* Extra features may initialize more fields in the future.
return 0;
}
+/************************ rbd_id object methods **************************/
+
+/**
+ * Input:
+ * @param in ignored
+ *
+ * Output:
+ * @param id the id stored in the object
+ * @returns 0 on success, negative error code on failure
+ */
+int get_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ uint64_t size;
+ int r = cls_cxx_stat(hctx, &size, NULL);
+ if (r < 0)
+ return r;
+
+ if (size == 0)
+ return -ENOENT;
+
+ bufferlist read_bl;
+ r = cls_cxx_read(hctx, 0, size, &read_bl);
+ if (r < 0) {
+ CLS_ERR("get_id: could not read id: %d", r);
+ return r;
+ }
+
+ string id;
+ try {
+ bufferlist::iterator iter = read_bl.begin();
+ ::decode(id, iter);
+ } catch (const buffer::error &err) {
+ return -EIO;
+ }
+
+ ::encode(id, *out);
+ return 0;
+};
+
+/**
+ * Set the id of an image. The object must already exist.
+ *
+ * Input:
+ * @param id the id of the image, as an alpha-numeric string
+ *
+ * Output:
+ * @returns 0 on success, -EEXIST if the atomic create fails,
+ * negative error code on other error
+ */
+int set_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ int r = check_exists(hctx);
+ if (r < 0)
+ return r;
+
+ string id;
+ try {
+ bufferlist::iterator iter = in->begin();
+ ::decode(id, iter);
+ } catch (const buffer::error &err) {
+ return -EINVAL;
+ }
+
+ if (!is_valid_id(id)) {
+ CLS_ERR("set_id: invalid id '%s'", id.c_str());
+ return -EINVAL;
+ }
+
+ uint64_t size;
+ r = cls_cxx_stat(hctx, &size, NULL);
+ if (r < 0)
+ return r;
+ if (size != 0)
+ return -EEXIST;
+
+ CLS_LOG(20, "set_id: id=%s", id.c_str());
+
+ bufferlist write_bl;
+ ::encode(id, write_bl);
+ return cls_cxx_write(hctx, 0, write_bl.length(), &write_bl);
+}
+
/****************************** Old format *******************************/
int old_snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PUBLIC,
remove_parent, &h_remove_parent);
+ /* methods for the rbd_id.$image_name objects */
+ cls_register_cxx_method(h_class, "get_id",
+ CLS_METHOD_RD | CLS_METHOD_PUBLIC,
+ get_id, &h_get_id);
+ cls_register_cxx_method(h_class, "set_id",
+ CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PUBLIC,
+ set_id, &h_set_id);
+
+
/* methods for the old format */
cls_register_cxx_method(h_class, "snap_list",
CLS_METHOD_RD | CLS_METHOD_PUBLIC,
return ioctx->exec(oid, "rbd", "break_lock", in, out);
}
+ /************************ rbd_id object methods ************************/
+
+ int get_id(librados::IoCtx *ioctx, const std::string &oid, std::string *id)
+ {
+ bufferlist in, out;
+ int r = ioctx->exec(oid, "rbd", "get_id", in, out);
+ if (r < 0)
+ return r;
+
+ bufferlist::iterator iter = out.begin();
+ try {
+ ::decode(*id, iter);
+ } catch (const buffer::error &err) {
+ return -EBADMSG;
+ }
+
+ return 0;
+ }
+
+ int set_id(librados::IoCtx *ioctx, const std::string &oid, std::string id)
+ {
+ bufferlist in, out;
+ ::encode(*id, in);
+ return ioctx->exec(oid, "rbd", "set_id", in, out);
+ }
+
} // namespace cls_client
} // namespace librbd
std::string *parent_image, snapid_t *parent_snap_id,
uint64_t *parent_overlap);
int set_parent(librados::IoCtx *ioctx, const std::string &oid,
- int64_t parent_pool, const std::string& parent_image, snapid_t parent_snap_id,
- uint64_t parent_overlap);
+ int64_t parent_pool, const std::string& parent_image,
+ snapid_t parent_snap_id, uint64_t parent_overlap);
int remove_parent(librados::IoCtx *ioctx, const std::string &oid);
int snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
snapid_t snap_id, const std::string &snap_name);
int break_lock(librados::IoCtx *ioctx, const std::string& oid,
const std::string &locker, const std::string &cookie);
+ // operations on rbd_id objects
+ int get_id(librados::IoCtx *ioctx, const std::string &oid, std::string *id);
+ int set_id(librados::IoCtx *ioctx, const std::string &oid, std::string id);
+
// class operations on the old format, kept for
// backwards compatability
int old_snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
using ::librbd::cls_client::lock_image_shared;
using ::librbd::cls_client::unlock_image;
using ::librbd::cls_client::break_lock;
+using ::librbd::cls_client::get_id;
+using ::librbd::cls_client::set_id;
+
+TEST(cls_rbd, get_and_set_id)
+{
+ librados::Rados rados;
+ librados::IoCtx ioctx;
+ string pool_name = get_temp_pool_name();
+
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+
+ string oid = "rbd_id_test";
+ string id;
+ string valid_id = "0123abcxyzZYXCBA";
+ string invalid_id = ".abc";
+ string empty_id;
+
+ ASSERT_EQ(-ENOENT, get_id(&ioctx, oid, &id));
+ ASSERT_EQ(-ENOENT, set_id(&ioctx, oid, valid_id));
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+ ASSERT_EQ(-EINVAL, set_id(&ioctx, oid, invalid_id));
+ ASSERT_EQ(-EINVAL, set_id(&ioctx, oid, empty_id));
+ ASSERT_EQ(-ENOENT, get_id(&ioctx, oid, &id));
+
+ ASSERT_EQ(0, set_id(&ioctx, oid, valid_id));
+ ASSERT_EQ(-EEXIST, set_id(&ioctx, oid, valid_id));
+ ASSERT_EQ(-EEXIST, set_id(&ioctx, oid, valid_id + valid_id));
+ ASSERT_EQ(0, get_id(&ioctx, oid, &id));
+ ASSERT_EQ(id, valid_id);
+
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+}
TEST(cls_rbd, create)
{