]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librados: add omap object operations to the c api
authorJosh Durgin <josh.durgin@inktank.com>
Fri, 7 Feb 2014 04:10:15 +0000 (20:10 -0800)
committerJosh Durgin <josh.durgin@inktank.com>
Tue, 18 Feb 2014 20:34:33 +0000 (12:34 -0800)
Create an iterator type similar to XattrIter for returning results.
It just wraps the std::map to preserve sorting.

When getting omap keys or values, use an extra callback to set up the
iterator, and default optional parameters to the empty string.

Since it's hard to test read or write omap ops in isolation, combine
them all in the c read ops test case.

One difference between this and the c++ api is that omap_cmp only
allows one assertion per sub-op, for simplicity of the
interface. Multiple omap_cmp operations can still be added to the same
op to get the same effect.

Fixes: #7194
Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
src/include/rados/librados.h
src/librados/librados.cc
src/test/librados/c_read_operations.cc

index 5143ddc6a9da904e74a41504c58fef946e67ad31..dfc1198f566a3114744c1e2ec52a6416264b647b 100644 (file)
@@ -180,6 +180,15 @@ typedef uint64_t rados_snap_t;
  */
 typedef void *rados_xattrs_iter_t;
 
+/**
+ * @typedef rados_omap_iter_t
+ * An iterator for listing omap key/value pairs on an object.
+ * Used with rados_read_op_omap_get_keys(), rados_read_op_omap_get_vals(),
+ * rados_read_op_omap_get_vals_by_keys(), rados_omap_get_next(), and
+ * rados_omap_get_end().
+ */
+typedef void *rados_omap_iter_t;
+
 /**
  * @struct rados_pool_stat_t
  * Usage information for a pool.
@@ -225,6 +234,9 @@ struct rados_cluster_stat_t {
  * - Extended attribute manipulation: rados_write_op_cmpxattr()
  *   rados_write_op_cmpxattr(), rados_write_op_setxattr(),
  *   rados_write_op_rmxattr()
+ * - Object map key/value pairs: rados_write_op_omap_set(),
+ *   rados_write_op_omap_rm_keys(), rados_write_op_omap_clear(),
+ *   rados_write_op_omap_cmp()
  * - Creating objects: rados_write_op_create()
  * - IO on objects: rados_write_op_append(), rados_write_op_write(), rados_write_op_zero
  *   rados_write_op_write_full(), rados_write_op_remove, rados_write_op_truncate(),
@@ -241,6 +253,9 @@ typedef void *rados_write_op_t;
  * - Creation and deletion: rados_create_read_op() rados_release_read_op()
  * - Extended attribute manipulation: rados_read_op_cmpxattr(),
  *   rados_read_op_getxattr(), rados_read_op_getxattrs()
+ * - Object map key/value pairs: rados_read_op_omap_get_vals(),
+ *   rados_read_op_omap_get_keys(), rados_read_op_omap_get_vals_by_keys(),
+ *   rados_read_op_omap_cmp()
  * - Object properties: rados_read_op_stat(), rados_read_op_assert_exists()
  * - IO on objects: rados_read_op_read()
  * - Custom operations: rados_read_op_exec(), rados_read_op_exec_user_buf()
@@ -1214,6 +1229,37 @@ void rados_getxattrs_end(rados_xattrs_iter_t iter);
 
 /** @} Xattrs */
 
+/**
+ * Get the next omap key/value pair on the object
+ *
+ * @pre iter is a valid iterator
+ *
+ * @post key and val are the next key/value pair. key is
+ * null-terminated, and val has length len. If the end of the list has
+ * been reached, key and val are NULL, and len is 0. key and val will
+ * not be accessible after rados_omap_get_end() is called on iter, so
+ * if they are needed after that they should be copied.
+ *
+ * @param iter iterator to advance
+ * @param key where to store the key of the next omap entry
+ * @param val where to store the value of the next omap entry
+ * @param len where to store the number of bytes in val
+ * @returns 0 on success, negative error code on failure
+ */
+int rados_omap_get_next(rados_omap_iter_t iter,
+                       char **key,
+                       char **val,
+                       size_t *len);
+
+/**
+ * Close the omap iterator.
+ *
+ * iter should not be used after this is called.
+ *
+ * @param iter the iterator to close
+ */
+void rados_omap_get_end(rados_omap_iter_t iter);
+
 /**
  * Get object stats (size/mtime)
  *
@@ -1795,6 +1841,26 @@ void rados_write_op_cmpxattr(rados_write_op_t write_op,
                              const char *value,
                              size_t value_len);
 
+/**
+ * Ensure that the an omap value satisfies a comparison,
+ * with the supplied value on the right hand side (i.e.
+ * for OP_LT, the comparison is actual_value < value.
+ *
+ * @param write_op operation to add this action to
+ * @param key which omap value to compare
+ * @param comparison_operator one of LIBRADOS_CMPXATTR_OP_EQ,
+   LIBRADOS_CMPXATTR_OP_LT, or LIBRADOS_CMPXATTR_OP_GT
+ * @param val value to compare with
+ * @param val_len length of value in bytes
+ * @param prval where to store the return value from this action
+ */
+void rados_write_op_omap_cmp(rados_write_op_t write_op,
+                            const char *key,
+                            uint8_t comparison_operator,
+                            const char *val,
+                            size_t val_len,
+                            int *prval);
+
 /**
  * Set an xattr
  * @param write_op operation to add this action to
@@ -1897,6 +1963,39 @@ void rados_write_op_exec(rados_write_op_t write_op,
                         size_t in_len,
                         int *prval);
 
+/**
+ * Set key/value pairs on an object
+ *
+ * @param write_op operation to add this action to
+ * @param keys array of null-terminated char arrays representing keys to set
+ * @param vals array of pointers to values to set
+ * @param lens array of lengths corresponding to each value
+ * @param num number of key/value pairs to set
+ */
+void rados_write_op_omap_set(rados_write_op_t write_op,
+                            char const* const* keys,
+                            char const* const* vals,
+                            const size_t *lens,
+                            size_t num);
+
+/**
+ * Remove key/value pairs from an object
+ *
+ * @param write_op operation to add this action to
+ * @param keys array of null-terminated char arrays representing keys to remove
+ * @param keys_len number of key/value pairs to remove
+ */
+void rados_write_op_omap_rm_keys(rados_write_op_t write_op,
+                                char const* const* keys,
+                                size_t keys_len);
+
+/**
+ * Remove all key/value pairs from an object
+ *
+ * @param write_op operation to add this action to
+ */
+void rados_write_op_omap_clear(rados_write_op_t write_op);
+
 /**
  * Perform a write operation synchronously
  * @param write_op operation to perform
@@ -1981,6 +2080,25 @@ void rados_read_op_getxattrs(rados_read_op_t read_op,
                             rados_xattrs_iter_t *iter,
                             int *prval);
 
+/**
+ * Ensure that the an omap value satisfies a comparison,
+ * with the supplied value on the right hand side (i.e.
+ * for OP_LT, the comparison is actual_value < value.
+ *
+ * @param read_op operation to add this action to
+ * @param key which omap value to compare
+ * @param comparison_operator one of LIBRADOS_CMPXATTR_OP_EQ,
+   LIBRADOS_CMPXATTR_OP_LT, or LIBRADOS_CMPXATTR_OP_GT
+ * @param val value to compare with
+ * @param val_len length of value in bytes
+ * @param prval where to store the return value from this action
+ */
+void rados_read_op_omap_cmp(rados_read_op_t read_op,
+                           const char *key,
+                           uint8_t comparison_operator,
+                           const char *val,
+                           size_t val_len,
+                           int *prval);
 
 /**
  * Get object size and mtime
@@ -2069,6 +2187,59 @@ void rados_read_op_exec_user_buf(rados_read_op_t read_op,
                                 size_t *used_len,
                                 int *prval);
 
+/**
+ * Start iterating over key/value pairs on an object.
+ *
+ * They will be returned sorted by key.
+ *
+ * @param read_op operation to add this action to
+ * @param start_after list keys starting after start_after
+ * @param filter_prefix list only keys beginning with filter_prefix
+ * @parem max_return list no more than max_return key/value pairs
+ * @param iter where to store the iterator
+ * @param prval where to store the return value from this action
+ */
+void rados_read_op_omap_get_vals(rados_read_op_t read_op,
+                                const char *start_after,
+                                const char *filter_prefix,
+                                uint64_t max_return,
+                                rados_omap_iter_t *iter,
+                                int *prval);
+
+/**
+ * Start iterating over keys on an object.
+ *
+ * They will be returned sorted by key, and the iterator
+ * will fill in NULL for all values if specified.
+ *
+ * @param read_op operation to add this action to
+ * @param start_after list keys starting after start_after
+ * @parem max_return list no more than max_return keys
+ * @param iter where to store the iterator
+ * @param prval where to store the return value from this action
+ */
+void rados_read_op_omap_get_keys(rados_read_op_t read_op,
+                                const char *start_after,
+                                uint64_t max_return,
+                                rados_omap_iter_t *iter,
+                                int *prval);
+
+/**
+ * Start iterating over specific key/value pairs
+ *
+ * They will be returned sorted by key.
+ *
+ * @param read_op operation to add this action to
+ * @param keys array of pointers to null-terminated keys to get
+ * @param keys_len the number of strings in keys
+ * @param iter where to store the iterator
+ * @param prval where to store the return value from this action
+ */
+void rados_read_op_omap_get_vals_by_keys(rados_read_op_t read_op,
+                                        char const* const* keys,
+                                        size_t keys_len,
+                                        rados_omap_iter_t *iter,
+                                        int *prval);
 
 /**
  * Perform a write operation synchronously
index 34a2b1d4eec8623f6165f1ce5dd342de296d6d96..258070aa4788766dcb73785791446632072f30ca 100644 (file)
@@ -3066,6 +3066,31 @@ extern "C" void rados_write_op_cmpxattr(rados_write_op_t write_op,
                                            bl);
 }
 
+static void rados_c_omap_cmp(ObjectOperation *op,
+                            const char *key,
+                            uint8_t comparison_operator,
+                            const char *val,
+                            size_t val_len,
+                            int *prval)
+{
+  bufferlist bl;
+  bl.append(val, val_len);
+  std::map<std::string, pair<bufferlist, int> > assertions;
+  assertions[key] = std::make_pair(bl, comparison_operator);
+  op->omap_cmp(assertions, prval);
+}
+
+extern "C" void rados_write_op_omap_cmp(rados_write_op_t write_op,
+                                       const char *key,
+                                       uint8_t comparison_operator,
+                                       const char *val,
+                                       size_t val_len,
+                                       int *prval)
+{
+  rados_c_omap_cmp((::ObjectOperation *)write_op, key, comparison_operator,
+                  val, val_len, prval);
+}
+
 extern "C" void rados_write_op_setxattr(rados_write_op_t write_op,
                                        const char *name,
                                       const char *value,
@@ -3154,6 +3179,34 @@ extern "C" void rados_write_op_exec(rados_write_op_t write_op,
   ((::ObjectOperation *)write_op)->call(cls, method, inbl, NULL, NULL, prval);
 }
 
+extern "C" void rados_write_op_omap_set(rados_write_op_t write_op,
+                                       char const* const* keys,
+                                       char const* const* vals,
+                                       const size_t *lens,
+                                       size_t num)
+{
+  std::map<std::string, bufferlist> entries;
+  for (size_t i = 0; i < num; ++i) {
+    bufferlist bl(lens[i]);
+    bl.append(vals[i], lens[i]);
+    entries[keys[i]] = bl;
+  }
+  ((::ObjectOperation *)write_op)->omap_set(entries);
+}
+
+extern "C" void rados_write_op_omap_rm_keys(rados_write_op_t write_op,
+                                           char const* const* keys,
+                                           size_t keys_len)
+{
+  std::set<std::string> to_remove(keys, keys + keys_len);
+  ((::ObjectOperation *)write_op)->omap_rm_keys(to_remove);
+}
+
+extern "C" void rados_write_op_omap_clear(rados_write_op_t write_op)
+{
+  ((::ObjectOperation *)write_op)->omap_clear();
+}
+
 extern "C" int rados_write_op_operate(rados_write_op_t write_op,
                                       rados_ioctx_t io,
                                       const char *oid,
@@ -3214,6 +3267,17 @@ extern "C" void rados_read_op_cmpxattr(rados_read_op_t read_op,
                                           bl);
 }
 
+extern "C" void rados_read_op_omap_cmp(rados_read_op_t read_op,
+                                      const char *key,
+                                      uint8_t comparison_operator,
+                                      const char *val,
+                                      size_t val_len,
+                                      int *prval)
+{
+  rados_c_omap_cmp((::ObjectOperation *)read_op, key, comparison_operator,
+                  val, val_len, prval);
+}
+
 extern "C" void rados_read_op_stat(rados_read_op_t read_op,
                                   uint64_t *psize,
                                   time_t *pmtime,
@@ -3308,6 +3372,20 @@ extern "C" void rados_read_op_exec_user_buf(rados_read_op_t read_op,
                                       prval);
 }
 
+struct RadosOmapIter {
+  std::map<std::string, bufferlist> values;
+  std::map<std::string, bufferlist>::iterator i;
+};
+
+class C_OmapIter : public Context {
+  RadosOmapIter *iter;
+public:
+  C_OmapIter(RadosOmapIter *iter) : iter(iter) {}
+  void finish(int r) {
+    iter->i = iter->values.begin();
+  }
+};
+
 class C_XattrsIter : public Context {
   RadosXattrsIter *iter;
 public:
@@ -3327,6 +3405,97 @@ extern "C" void rados_read_op_getxattrs(rados_read_op_t read_op,
   *iter = xattrs_iter;
 }
 
+extern "C" void rados_read_op_omap_get_vals(rados_read_op_t read_op,
+                                           const char *start_after,
+                                           const char *filter_prefix,
+                                           uint64_t max_return,
+                                           rados_omap_iter_t *iter,
+                                           int *prval)
+{
+  RadosOmapIter *omap_iter = new RadosOmapIter;
+  const char *start = start_after ? start_after : "";
+  const char *filter = filter_prefix ? filter_prefix : "";
+  ((::ObjectOperation *)read_op)->omap_get_vals(start,
+                                               filter,
+                                               max_return,
+                                               &omap_iter->values,
+                                               prval);
+  ((::ObjectOperation *)read_op)->add_handler(new C_OmapIter(omap_iter));
+  *iter = omap_iter;
+}
+
+struct C_OmapKeysIter : public Context {
+  RadosOmapIter *iter;
+  std::set<std::string> keys;
+  C_OmapKeysIter(RadosOmapIter *iter) : iter(iter) {}
+  void finish(int r) {
+    // map each key to an empty bl
+    for (std::set<std::string>::const_iterator i = keys.begin();
+        i != keys.end(); ++i) {
+      iter->values[*i];
+    }
+    iter->i = iter->values.begin();
+  }
+};
+
+extern "C" void rados_read_op_omap_get_keys(rados_read_op_t read_op,
+                                           const char *start_after,
+                                           uint64_t max_return,
+                                           rados_omap_iter_t *iter,
+                                           int *prval)
+{
+  RadosOmapIter *omap_iter = new RadosOmapIter;
+  C_OmapKeysIter *ctx = new C_OmapKeysIter(omap_iter);
+  ((::ObjectOperation *)read_op)->omap_get_keys(start_after ? start_after : "",
+                                               max_return, &ctx->keys, prval);
+  ((::ObjectOperation *)read_op)->add_handler(ctx);
+  *iter = omap_iter;
+}
+
+extern "C" void rados_read_op_omap_get_vals_by_keys(rados_read_op_t read_op,
+                                                   char const* const* keys,
+                                                   size_t keys_len,
+                                                   rados_omap_iter_t *iter,
+                                                   int *prval)
+{
+  std::set<std::string> to_get(keys, keys + keys_len);
+
+  RadosOmapIter *omap_iter = new RadosOmapIter;
+  ((::ObjectOperation *)read_op)->omap_get_vals_by_keys(to_get,
+                                                       &omap_iter->values,
+                                                       prval);
+  ((::ObjectOperation *)read_op)->add_handler(new C_OmapIter(omap_iter));
+  *iter = omap_iter;
+}
+
+extern "C" int rados_omap_get_next(rados_omap_iter_t iter,
+                                  char **key,
+                                  char **val,
+                                  size_t *len)
+{
+  RadosOmapIter *it = (RadosOmapIter *)iter;
+  if (it->i == it->values.end()) {
+    *key = NULL;
+    *val = NULL;
+    *len = 0;
+    return 0;
+  }
+  if (key)
+    *key = (char*)it->i->first.c_str();
+  if (val)
+    *val = it->i->second.c_str();
+  if (len)
+    *len = it->i->second.length();
+  ++it->i;
+  return 0;
+}
+
+extern "C" void rados_omap_get_end(rados_omap_iter_t iter)
+{
+  RadosOmapIter *it = (RadosOmapIter *)iter;
+  delete it;
+}
+
 extern "C" int rados_read_op_operate(rados_read_op_t read_op,
                                     rados_ioctx_t io,
                                     const char *oid,
index aa058c64ead856237740f500d0b55a179b612094..c540c697ae2bf759858e323c659bcc657513256d 100644 (file)
@@ -31,6 +31,62 @@ protected:
     return r;
   }
 
+  void fetch_and_verify_omap_vals(char const* const* keys,
+                                 char const* const* vals,
+                                 const size_t *lens,
+                                 size_t len)
+  {
+    rados_omap_iter_t iter_vals, iter_keys, iter_vals_by_key;
+    int r_vals, r_keys, r_vals_by_key;
+    rados_read_op_t op = rados_create_read_op();
+    rados_read_op_omap_get_vals(op, NULL, NULL, 100, &iter_vals, &r_vals);
+    rados_read_op_omap_get_keys(op, NULL, 100, &iter_keys, &r_keys);
+    rados_read_op_omap_get_vals_by_keys(op, keys, len,
+                                       &iter_vals_by_key, &r_vals_by_key);
+    ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+    rados_release_read_op(op);
+    ASSERT_EQ(0, r_vals);
+    ASSERT_EQ(0, r_keys);
+    ASSERT_EQ(0, r_vals_by_key);
+
+    const char *zeros[len];
+    size_t zero_lens[len];
+    memset(zeros, 0, len);
+    memset(zero_lens, 0, len * sizeof(size_t));
+    compare_omap_vals(keys, vals, lens, len, iter_vals);
+    compare_omap_vals(keys, zeros, zero_lens, len, iter_keys);
+    compare_omap_vals(keys, vals, lens, len, iter_vals_by_key);
+  }
+
+  void compare_omap_vals(char const* const* keys,
+                        char const* const* vals,
+                        const size_t *lens,
+                        size_t len,
+                        rados_omap_iter_t iter)
+  {
+    size_t i = 0;
+    char *key = NULL;
+    char *val = NULL;
+    size_t val_len = 0;
+    while (i < len) {
+      ASSERT_EQ(0, rados_omap_get_next(iter, &key, &val, &val_len));
+      if (len == 0 && key == NULL && val == NULL)
+       break;
+      if (key)
+       EXPECT_EQ(std::string(keys[i]), std::string(key));
+      else
+       EXPECT_EQ(keys[i], key);
+      ASSERT_EQ(0, memcmp(vals[i], val, val_len));
+      ASSERT_EQ(lens[i], val_len);
+      ++i;
+    }
+    ASSERT_EQ(i, len);
+    ASSERT_EQ(0, rados_omap_get_next(iter, &key, &val, &val_len));
+    ASSERT_EQ((char*)NULL, key);
+    ASSERT_EQ((char*)NULL, val);
+    ASSERT_EQ(0u, val_len);
+    rados_omap_get_end(iter);
+  }
 
   void compare_xattrs(char const* const* keys,
                      char const* const* vals,
@@ -359,6 +415,95 @@ TEST_F(CReadOpsTest, Stat) {
   rados_release_read_op(op);
 }
 
+TEST_F(CReadOpsTest, Omap) {
+  char *keys[] = {(char*)"bar",
+                 (char*)"foo",
+                 (char*)"test1",
+                 (char*)"test2"};
+  char *vals[] = {(char*)"",
+                 (char*)"\0",
+                 (char*)"abc",
+                 (char*)"va\0lue"};
+  size_t lens[] = {0, 1, 3, 6};
+
+  // check for -ENOENT before the object exists and when it exists
+  // with no omap entries
+  rados_omap_iter_t iter_vals;
+  rados_read_op_t rop = rados_create_read_op();
+  rados_read_op_omap_get_vals(rop, "", "", 10, &iter_vals, NULL);
+  ASSERT_EQ(-ENOENT, rados_read_op_operate(rop, ioctx, obj, 0));
+  rados_release_read_op(rop);
+  compare_omap_vals(NULL, NULL, NULL, 0, iter_vals);
+
+  write_object();
+
+  fetch_and_verify_omap_vals(NULL, NULL, NULL, 0);
+
+  // write and check for the k/v pairs
+  rados_write_op_t op = rados_create_write_op();
+  rados_write_op_omap_set(op, keys, vals, lens, 4);
+  ASSERT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+  rados_release_write_op(op);
+
+  fetch_and_verify_omap_vals(keys, vals, lens, 4);
+
+  rados_omap_iter_t iter_keys;
+  int r_vals = -1, r_keys = -1;
+  rop = rados_create_read_op();
+  rados_read_op_omap_get_vals(rop, "", "test", 1, &iter_vals, &r_vals);
+  rados_read_op_omap_get_keys(rop, "test", 1, &iter_keys, &r_keys);
+  ASSERT_EQ(0, rados_read_op_operate(rop, ioctx, obj, 0));
+  rados_release_read_op(rop);
+  EXPECT_EQ(0, r_vals);
+  EXPECT_EQ(0, r_keys);
+
+  compare_omap_vals(&keys[2], &vals[2], &lens[2], 1, iter_vals);
+  compare_omap_vals(&keys[2], &vals[0], &lens[0], 1, iter_keys);
+
+  // check omap_cmp finds all expected values
+  rop = rados_create_read_op();
+  int rvals[4];
+  for (int i = 0; i < 4; ++i)
+    rados_read_op_omap_cmp(rop, keys[i], LIBRADOS_CMPXATTR_OP_EQ,
+                          vals[i], lens[i], &rvals[i]);
+  EXPECT_EQ(0, rados_read_op_operate(rop, ioctx, obj, 0));
+  rados_release_read_op(rop);
+  for (int i = 0; i < 4; ++i)
+    EXPECT_EQ(0, rvals[i]);
+
+  // try to remove keys with a guard that should fail
+  op = rados_create_write_op();
+  rados_write_op_omap_cmp(op, keys[2], LIBRADOS_CMPXATTR_OP_LT,
+                         vals[2], lens[2], &r_vals);
+  rados_write_op_omap_rm_keys(op, keys, 2);
+  EXPECT_EQ(-ECANCELED, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+  rados_release_write_op(op);
+  // due to http://tracker.ceph.com/issues/6483 this is 0:
+  EXPECT_EQ(0, r_vals);
+
+  // verifying the keys are still there, and then remove them
+  op = rados_create_write_op();
+  rados_write_op_omap_cmp(op, keys[0], LIBRADOS_CMPXATTR_OP_EQ,
+                         vals[0], lens[0], NULL);
+  rados_write_op_omap_cmp(op, keys[1], LIBRADOS_CMPXATTR_OP_EQ,
+                         vals[1], lens[1], NULL);
+  rados_write_op_omap_rm_keys(op, keys, 2);
+  EXPECT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+  rados_release_write_op(op);
+
+  fetch_and_verify_omap_vals(&keys[2], &vals[2], &lens[2], 2);
+
+  // clear the rest and check there are none left
+  op = rados_create_write_op();
+  rados_write_op_omap_clear(op);
+  EXPECT_EQ(0, rados_write_op_operate(op, ioctx, obj, NULL, 0));
+  rados_release_write_op(op);
+
+  fetch_and_verify_omap_vals(NULL, NULL, NULL, 0);
+
+  remove_object();
+}
+
 TEST_F(CReadOpsTest, GetXattrs) {
   write_object();