summaryrefslogtreecommitdiffstats
path: root/src/ceph/examples/librados
diff options
context:
space:
mode:
authorQiaowei Ren <qiaowei.ren@intel.com>2018-01-04 13:43:33 +0800
committerQiaowei Ren <qiaowei.ren@intel.com>2018-01-05 11:59:39 +0800
commit812ff6ca9fcd3e629e49d4328905f33eee8ca3f5 (patch)
tree04ece7b4da00d9d2f98093774594f4057ae561d4 /src/ceph/examples/librados
parent15280273faafb77777eab341909a3f495cf248d9 (diff)
initial code repo
This patch creates initial code repo. For ceph, luminous stable release will be used for base code, and next changes and optimization for ceph will be added to it. For opensds, currently any changes can be upstreamed into original opensds repo (https://github.com/opensds/opensds), and so stor4nfv will directly clone opensds code to deploy stor4nfv environment. And the scripts for deployment based on ceph and opensds will be put into 'ci' directory. Change-Id: I46a32218884c75dda2936337604ff03c554648e4 Signed-off-by: Qiaowei Ren <qiaowei.ren@intel.com>
Diffstat (limited to 'src/ceph/examples/librados')
-rw-r--r--src/ceph/examples/librados/Makefile39
-rw-r--r--src/ceph/examples/librados/hello_radosstriper.cc102
-rw-r--r--src/ceph/examples/librados/hello_world.cc298
-rw-r--r--src/ceph/examples/librados/hello_world.readme14
-rw-r--r--src/ceph/examples/librados/hello_world_c.c313
5 files changed, 766 insertions, 0 deletions
diff --git a/src/ceph/examples/librados/Makefile b/src/ceph/examples/librados/Makefile
new file mode 100644
index 0000000..533a4c6
--- /dev/null
+++ b/src/ceph/examples/librados/Makefile
@@ -0,0 +1,39 @@
+
+CXX?=g++
+CXX_FLAGS?=-std=c++11 -Wall -Wextra -Werror -g
+CXX_LIBS?=-lboost_system -lrados -lradosstriper
+CXX_INC?=$(LOCAL_LIBRADOS_INC)
+CXX_CC=$(CXX) $(CXX_FLAGS) $(CXX_INC) $(LOCAL_LIBRADOS) $(CXX_LIBS)
+
+CC?=gcc
+CC_FLAGS=-Wall -Wextra -Werror -g
+CC_INC=$(LOCAL_LIBRADOS_INC)
+CC_LIBS?=-lrados
+CC_CC=$(CC) $(CC_FLAGS) $(CC_INC) $(LOCAL_LIBRADOS) $(CC_LIBS)
+
+# Relative path to the Ceph source:
+CEPH_SRC_HOME?=../../src
+CEPH_BLD_HOME?=../../build
+
+LOCAL_LIBRADOS?=-L$(CEPH_BLD_HOME)/lib/ -Wl,-rpath,$(CEPH_BLD_HOME)/lib
+LOCAL_LIBRADOS_INC?=-I$(CEPH_SRC_HOME)/include
+
+all: hello_world_cpp hello_radosstriper_cpp hello_world_c
+
+# Build against the system librados instead of the one in the build tree:
+all-system: LOCAL_LIBRADOS=
+all-system: LOCAL_LIBRADOS_INC=
+all-system: all
+
+hello_world_cpp: hello_world.cc
+ $(CXX_CC) -o hello_world_cpp hello_world.cc
+
+hello_radosstriper_cpp: hello_radosstriper.cc
+ $(CXX_CC) -o hello_radosstriper_cpp hello_radosstriper.cc
+
+hello_world_c: hello_world_c.c
+ $(CC_CC) -o hello_world_c hello_world_c.c
+
+clean:
+ rm -f hello_world_cpp hello_radosstriper_cpp hello_world_c
+
diff --git a/src/ceph/examples/librados/hello_radosstriper.cc b/src/ceph/examples/librados/hello_radosstriper.cc
new file mode 100644
index 0000000..f1b43d8
--- /dev/null
+++ b/src/ceph/examples/librados/hello_radosstriper.cc
@@ -0,0 +1,102 @@
+#include "rados/librados.hpp"
+#include "radosstriper/libradosstriper.hpp"
+#include <iostream>
+#include <string>
+
+
+int main(int argc, char* argv[])
+{
+ if(argc != 6)
+ {
+ std::cout <<"Please put in correct params\n"<<
+ "Stripe Count:\n"<<
+ "Object Size:\n" <<
+ "File Name:\n" <<
+ "Object Name:\n"
+ "Pool Name:"<< std::endl;
+ return EXIT_FAILURE;
+ }
+ uint32_t strip_count = std::stoi(argv[1]);
+ uint32_t obj_size = std::stoi(argv[2]);
+ std::string fname = argv[3];
+ std::string obj_name = argv[4];
+ std::string pool_name = argv[5];
+ int ret = 0;
+ librados::IoCtx io_ctx;
+ librados::Rados cluster;
+ libradosstriper::RadosStriper* rs = new libradosstriper::RadosStriper;
+
+ // make sure the keyring file is in /etc/ceph/ and is world readable
+ ret = cluster.init2("client.admin","ceph",0);
+ if( ret < 0)
+ {
+ std::cerr << "Couldn't init cluster "<< ret << std::endl;
+ }
+
+ // make sure ceph.conf is in /etc/ceph/ and is world readable
+ ret = cluster.conf_read_file("ceph.conf");
+ if( ret < 0)
+ {
+ std::cerr << "Couldn't read conf file "<< ret << std::endl;
+ }
+ ret = cluster.connect();
+ if(ret < 0)
+ {
+ std::cerr << "Couldn't connect to cluster "<< ret << std::endl;
+ }
+ else
+ {
+ std::cout << "Connected to Cluster"<< std::endl;
+ }
+
+ ret = cluster.ioctx_create(pool_name.c_str(), io_ctx);
+
+ if(ret < 0)
+ {
+ std::cerr << "Couldn't Create IO_CTX"<< ret << std::endl;
+ }
+ ret = libradosstriper::RadosStriper::striper_create(io_ctx,rs);
+ if(ret < 0)
+ {
+ std::cerr << "Couldn't Create RadosStriper"<< ret << std::endl;
+ delete rs;
+ }
+ uint64_t alignment = 0;
+ ret = io_ctx.pool_required_alignment2(&alignment);
+ if(ret < 0)
+ {
+ std::cerr << "IO_CTX didn't give alignment "<< ret
+ << "\n Is this an erasure coded pool? "<< std::endl;
+
+ delete rs;
+ io_ctx.close();
+ cluster.shutdown();
+ return EXIT_FAILURE;
+ }
+ std::cout << "Pool alignment: "<< alignment << std::endl;
+ rs->set_object_layout_stripe_unit(alignment);
+ // how many objects are we striping across?
+ rs->set_object_layout_stripe_count(strip_count);
+ // how big should each object be?
+ rs->set_object_layout_object_size(obj_size);
+
+ std::string err = "no_err";
+ librados::bufferlist bl;
+ bl.read_file(fname.c_str(),&err);
+ if(err != "no_err")
+ {
+ std::cout << "Error reading file into bufferlist: "<< err << std::endl;
+ delete rs;
+ io_ctx.close();
+ cluster.shutdown();
+ return EXIT_FAILURE;
+ }
+
+ std::cout << "Writing: " << fname << "\nas: "<< obj_name << std::endl;
+ rs->write_full(obj_name,bl);
+ std::cout << "done with: " << fname << std::endl;
+
+ delete rs;
+ io_ctx.close();
+ cluster.shutdown();
+}
diff --git a/src/ceph/examples/librados/hello_world.cc b/src/ceph/examples/librados/hello_world.cc
new file mode 100644
index 0000000..48e1fd1
--- /dev/null
+++ b/src/ceph/examples/librados/hello_world.cc
@@ -0,0 +1,298 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ * Copyright 2013 Inktank
+ */
+
+// install the librados-dev package to get this
+#include <rados/librados.hpp>
+#include <iostream>
+#include <string>
+
+int main(int argc, const char **argv)
+{
+ int ret = 0;
+
+ // we will use all of these below
+ const char *pool_name = "hello_world_pool";
+ std::string hello("hello world!");
+ std::string object_name("hello_object");
+ librados::IoCtx io_ctx;
+
+ // first, we create a Rados object and initialize it
+ librados::Rados rados;
+ {
+ ret = rados.init("admin"); // just use the client.admin keyring
+ if (ret < 0) { // let's handle any error that might have come back
+ std::cerr << "couldn't initialize rados! error " << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ std::cout << "we just set up a rados cluster object" << std::endl;
+ }
+ }
+
+ /*
+ * Now we need to get the rados object its config info. It can
+ * parse argv for us to find the id, monitors, etc, so let's just
+ * use that.
+ */
+ {
+ ret = rados.conf_parse_argv(argc, argv);
+ if (ret < 0) {
+ // This really can't happen, but we need to check to be a good citizen.
+ std::cerr << "failed to parse config options! error " << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ std::cout << "we just parsed our config options" << std::endl;
+ // We also want to apply the config file if the user specified
+ // one, and conf_parse_argv won't do that for us.
+ for (int i = 0; i < argc; ++i) {
+ if ((strcmp(argv[i], "-c") == 0) || (strcmp(argv[i], "--conf") == 0)) {
+ ret = rados.conf_read_file(argv[i+1]);
+ if (ret < 0) {
+ // This could fail if the config file is malformed, but it'd be hard.
+ std::cerr << "failed to parse config file " << argv[i+1]
+ << "! error" << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * next, we actually connect to the cluster
+ */
+ {
+ ret = rados.connect();
+ if (ret < 0) {
+ std::cerr << "couldn't connect to cluster! error " << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ std::cout << "we just connected to the rados cluster" << std::endl;
+ }
+ }
+
+ /*
+ * let's create our own pool instead of scribbling over real data.
+ * Note that this command creates pools with default PG counts specified
+ * by the monitors, which may not be appropriate for real use -- it's fine
+ * for testing, though.
+ */
+ {
+ ret = rados.pool_create(pool_name);
+ if (ret < 0) {
+ std::cerr << "couldn't create pool! error " << ret << std::endl;
+ return EXIT_FAILURE;
+ } else {
+ std::cout << "we just created a new pool named " << pool_name << std::endl;
+ }
+ }
+
+ /*
+ * create an "IoCtx" which is used to do IO to a pool
+ */
+ {
+ ret = rados.ioctx_create(pool_name, io_ctx);
+ if (ret < 0) {
+ std::cerr << "couldn't set up ioctx! error " << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ std::cout << "we just created an ioctx for our pool" << std::endl;
+ }
+ }
+
+ /*
+ * now let's do some IO to the pool! We'll write "hello world!" to a
+ * new object.
+ */
+ {
+ /*
+ * "bufferlist"s are Ceph's native transfer type, and are carefully
+ * designed to be efficient about copying. You can fill them
+ * up from a lot of different data types, but strings or c strings
+ * are often convenient. Just make sure not to deallocate the memory
+ * until the bufferlist goes out of scope and any requests using it
+ * have been finished!
+ */
+ librados::bufferlist bl;
+ bl.append(hello);
+
+ /*
+ * now that we have the data to write, let's send it to an object.
+ * We'll use the synchronous interface for simplicity.
+ */
+ ret = io_ctx.write_full(object_name, bl);
+ if (ret < 0) {
+ std::cerr << "couldn't write object! error " << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ std::cout << "we just wrote new object " << object_name
+ << ", with contents\n" << hello << std::endl;
+ }
+ }
+
+ /*
+ * now let's read that object back! Just for fun, we'll do it using
+ * async IO instead of synchronous. (This would be more useful if we
+ * wanted to send off multiple reads at once; see
+ * http://docs.ceph.com/docs/master/rados/api/librados/#asychronous-io )
+ */
+ {
+ librados::bufferlist read_buf;
+ int read_len = 4194304; // this is way more than we need
+ // allocate the completion from librados
+ librados::AioCompletion *read_completion = librados::Rados::aio_create_completion();
+ // send off the request.
+ ret = io_ctx.aio_read(object_name, read_completion, &read_buf, read_len, 0);
+ if (ret < 0) {
+ std::cerr << "couldn't start read object! error " << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ // wait for the request to complete, and check that it succeeded.
+ read_completion->wait_for_complete();
+ ret = read_completion->get_return_value();
+ if (ret < 0) {
+ std::cerr << "couldn't read object! error " << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ std::cout << "we read our object " << object_name
+ << ", and got back " << ret << " bytes with contents\n";
+ std::string read_string;
+ read_buf.copy(0, ret, read_string);
+ std::cout << read_string << std::endl;
+ }
+ }
+
+ /*
+ * We can also use xattrs that go alongside the object.
+ */
+ {
+ librados::bufferlist version_bl;
+ version_bl.append('1');
+ ret = io_ctx.setxattr(object_name, "version", version_bl);
+ if (ret < 0) {
+ std::cerr << "failed to set xattr version entry! error "
+ << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ std::cout << "we set the xattr 'version' on our object!" << std::endl;
+ }
+ }
+
+ /*
+ * And if we want to be really cool, we can do multiple things in a single
+ * atomic operation. For instance, we can update the contents of our object
+ * and set the version at the same time.
+ */
+ {
+ librados::bufferlist bl;
+ bl.append(hello);
+ bl.append("v2");
+ librados::ObjectWriteOperation write_op;
+ write_op.write_full(bl);
+ librados::bufferlist version_bl;
+ version_bl.append('2');
+ write_op.setxattr("version", version_bl);
+ ret = io_ctx.operate(object_name, &write_op);
+ if (ret < 0) {
+ std::cerr << "failed to do compound write! error " << ret << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ std::cout << "we overwrote our object " << object_name
+ << " with contents\n" << bl.c_str() << std::endl;
+ }
+ }
+
+ /*
+ * And to be even cooler, we can make sure that the object looks the
+ * way we expect before doing the write! Notice how this attempt fails
+ * because the xattr differs.
+ */
+ {
+ librados::ObjectWriteOperation failed_write_op;
+ librados::bufferlist bl;
+ bl.append(hello);
+ bl.append("v2");
+ librados::ObjectWriteOperation write_op;
+ write_op.write_full(bl);
+ librados::bufferlist version_bl;
+ version_bl.append('2');
+ librados::bufferlist old_version_bl;
+ old_version_bl.append('1');
+ failed_write_op.cmpxattr("version", LIBRADOS_CMPXATTR_OP_EQ, old_version_bl);
+ failed_write_op.write_full(bl);
+ failed_write_op.setxattr("version", version_bl);
+ ret = io_ctx.operate(object_name, &failed_write_op);
+ if (ret < 0) {
+ std::cout << "we just failed a write because the xattr wasn't as specified"
+ << std::endl;
+ } else {
+ std::cerr << "we succeeded on writing despite an xattr comparison mismatch!"
+ << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ /*
+ * Now let's do the update with the correct xattr values so it
+ * actually goes through
+ */
+ bl.clear();
+ bl.append(hello);
+ bl.append("v3");
+ old_version_bl.clear();
+ old_version_bl.append('2');
+ version_bl.clear();
+ version_bl.append('3');
+ librados::ObjectWriteOperation update_op;
+ update_op.cmpxattr("version", LIBRADOS_CMPXATTR_OP_EQ, old_version_bl);
+ update_op.write_full(bl);
+ update_op.setxattr("version", version_bl);
+ ret = io_ctx.operate(object_name, &update_op);
+ if (ret < 0) {
+ std::cerr << "failed to do a compound write update! error " << ret
+ << std::endl;
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ std::cout << "we overwrote our object " << object_name
+ << " following an xattr test with contents\n" << bl.c_str()
+ << std::endl;
+ }
+ }
+
+ ret = EXIT_SUCCESS;
+ out:
+ /*
+ * And now we're done, so let's remove our pool and then
+ * shut down the connection gracefully.
+ */
+ int delete_ret = rados.pool_delete(pool_name);
+ if (delete_ret < 0) {
+ // be careful not to
+ std::cerr << "We failed to delete our test pool!" << std::endl;
+ ret = EXIT_FAILURE;
+ }
+
+ rados.shutdown();
+
+ return ret;
+}
diff --git a/src/ceph/examples/librados/hello_world.readme b/src/ceph/examples/librados/hello_world.readme
new file mode 100644
index 0000000..d438f93
--- /dev/null
+++ b/src/ceph/examples/librados/hello_world.readme
@@ -0,0 +1,14 @@
+This simple librados program can be built by running "make" (and cleaned up
+with "make clean"), assuming you have librados-dev already installed.
+
+By default, the makefile will build against the librados headers and library in your
+build tree (ie. using relative paths). If you would like to build the examples against
+your system librados and headers, use "make all-system".
+
+And executed using
+./librados_hello_world -c ../../src/ceph.conf
+(or whatever path to a ceph.conf is appropriate to you, or
+by explicitly specifying monitors, user id, and keys).
+
+It demonstrates using librados in a non-Ceph project and the code should
+be self-explanatory.
diff --git a/src/ceph/examples/librados/hello_world_c.c b/src/ceph/examples/librados/hello_world_c.c
new file mode 100644
index 0000000..5b3efc9
--- /dev/null
+++ b/src/ceph/examples/librados/hello_world_c.c
@@ -0,0 +1,313 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ * Copyright 2013 Inktank
+ */
+
+// install the librados-dev package to get this
+#include <rados/librados.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, const char **argv)
+{
+ int ret = 0;
+
+ // we will use all of these below
+ const char *pool_name = "hello_world_pool";
+ const char* hello = "hello world!";
+ const char* object_name = "hello_object";
+ rados_ioctx_t io_ctx = NULL;
+ int pool_created = 0;
+
+ // first, we create a Rados object and initialize it
+ rados_t rados = NULL;
+ {
+ ret = rados_create(&rados, "admin"); // just use the client.admin keyring
+ if (ret < 0) { // let's handle any error that might have come back
+ printf("couldn't initialize rados! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ printf("we just set up a rados cluster object\n");
+ }
+ }
+
+ /*
+ * Now we need to get the rados object its config info. It can
+ * parse argv for us to find the id, monitors, etc, so let's just
+ * use that.
+ */
+ {
+ ret = rados_conf_parse_argv(rados, argc, argv);
+ if (ret < 0) {
+ // This really can't happen, but we need to check to be a good citizen.
+ printf("failed to parse config options! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ printf("we just parsed our config options\n");
+ // We also want to apply the config file if the user specified
+ // one, and conf_parse_argv won't do that for us.
+ int i;
+ for (i = 0; i < argc; ++i) {
+ if ((strcmp(argv[i], "-c") == 0) || (strcmp(argv[i], "--conf") == 0)) {
+ ret = rados_conf_read_file(rados, argv[i+1]);
+ if (ret < 0) {
+ // This could fail if the config file is malformed, but it'd be hard.
+ printf("failed to parse config file %s! error %d\n", argv[i+1], ret);
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * next, we actually connect to the cluster
+ */
+ {
+ ret = rados_connect(rados);
+ if (ret < 0) {
+ printf("couldn't connect to cluster! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ printf("we just connected to the rados cluster\n");
+ }
+ }
+
+ /*
+ * let's create our own pool instead of scribbling over real data.
+ * Note that this command creates pools with default PG counts specified
+ * by the monitors, which may not be appropriate for real use -- it's fine
+ * for testing, though.
+ */
+ {
+ ret = rados_pool_create(rados, pool_name);
+ if (ret < 0) {
+ printf("couldn't create pool! error %d\n", ret);
+ return EXIT_FAILURE;
+ } else {
+ printf("we just created a new pool named %s\n", pool_name);
+ }
+ pool_created = 1;
+ }
+
+ /*
+ * create an "IoCtx" which is used to do IO to a pool
+ */
+ {
+ ret = rados_ioctx_create(rados, pool_name, &io_ctx);
+ if (ret < 0) {
+ printf("couldn't set up ioctx! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ printf("we just created an ioctx for our pool\n");
+ }
+ }
+
+ /*
+ * now let's do some IO to the pool! We'll write "hello world!" to a
+ * new object.
+ */
+ {
+ /*
+ * now that we have the data to write, let's send it to an object.
+ * We'll use the synchronous interface for simplicity.
+ */
+ ret = rados_write_full(io_ctx, object_name, hello, strlen(hello));
+ if (ret < 0) {
+ printf("couldn't write object! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ printf("we just wrote new object %s, with contents '%s'\n", object_name, hello);
+ }
+ }
+
+ /*
+ * now let's read that object back! Just for fun, we'll do it using
+ * async IO instead of synchronous. (This would be more useful if we
+ * wanted to send off multiple reads at once; see
+ * http://docs.ceph.com/docs/master/rados/api/librados/#asychronous-io )
+ */
+ {
+ int read_len = 4194304; // this is way more than we need
+ char* read_buf = malloc(read_len + 1); // add one for the terminating 0 we'll add later
+ if (!read_buf) {
+ printf("couldn't allocate read buffer\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ // allocate the completion from librados
+ rados_completion_t read_completion;
+ ret = rados_aio_create_completion(NULL, NULL, NULL, &read_completion);
+ if (ret < 0) {
+ printf("couldn't create completion! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ free(read_buf);
+ goto out;
+ } else {
+ printf("we just created a new completion\n");
+ }
+ // send off the request.
+ ret = rados_aio_read(io_ctx, object_name, read_completion, read_buf, read_len, 0);
+ if (ret < 0) {
+ printf("couldn't start read object! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ free(read_buf);
+ rados_aio_release(read_completion);
+ goto out;
+ }
+ // wait for the request to complete, and check that it succeeded.
+ rados_aio_wait_for_complete(read_completion);
+ ret = rados_aio_get_return_value(read_completion);
+ if (ret < 0) {
+ printf("couldn't read object! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ free(read_buf);
+ rados_aio_release(read_completion);
+ goto out;
+ } else {
+ read_buf[ret] = 0; // null-terminate the string
+ printf("we read our object %s, and got back %d bytes with contents\n%s\n", object_name, ret, read_buf);
+ }
+
+ free(read_buf);
+ rados_aio_release(read_completion);
+ }
+
+ /*
+ * We can also use xattrs that go alongside the object.
+ */
+ {
+ const char* version = "1";
+ ret = rados_setxattr(io_ctx, object_name, "version", version, strlen(version));
+ if (ret < 0) {
+ printf("failed to set xattr version entry! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ goto out;
+ } else {
+ printf("we set the xattr 'version' on our object!\n");
+ }
+ }
+
+ /*
+ * And if we want to be really cool, we can do multiple things in a single
+ * atomic operation. For instance, we can update the contents of our object
+ * and set the version at the same time.
+ */
+ {
+ const char* content = "v2";
+ rados_write_op_t write_op = rados_create_write_op();
+ if (!write_op) {
+ printf("failed to allocate write op\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ rados_write_op_write_full(write_op, content, strlen(content));
+ const char* version = "2";
+ rados_write_op_setxattr(write_op, "version", version, strlen(version));
+ ret = rados_write_op_operate(write_op, io_ctx, object_name, NULL, 0);
+ if (ret < 0) {
+ printf("failed to do compound write! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ rados_release_write_op(write_op);
+ goto out;
+ } else {
+ printf("we overwrote our object %s with contents\n%s\n", object_name, content);
+ }
+ rados_release_write_op(write_op);
+ }
+
+ /*
+ * And to be even cooler, we can make sure that the object looks the
+ * way we expect before doing the write! Notice how this attempt fails
+ * because the xattr differs.
+ */
+ {
+ rados_write_op_t failed_write_op = rados_create_write_op();
+ if (!failed_write_op) {
+ printf("failed to allocate write op\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ const char* content = "v2";
+ const char* version = "2";
+ const char* old_version = "1";
+ rados_write_op_cmpxattr(failed_write_op, "version", LIBRADOS_CMPXATTR_OP_EQ, old_version, strlen(old_version));
+ rados_write_op_write_full(failed_write_op, content, strlen(content));
+ rados_write_op_setxattr(failed_write_op, "version", version, strlen(version));
+ ret = rados_write_op_operate(failed_write_op, io_ctx, object_name, NULL, 0);
+ if (ret < 0) {
+ printf("we just failed a write because the xattr wasn't as specified\n");
+ } else {
+ printf("we succeeded on writing despite an xattr comparison mismatch!\n");
+ ret = EXIT_FAILURE;
+ rados_release_write_op(failed_write_op);
+ goto out;
+ }
+ rados_release_write_op(failed_write_op);
+
+ /*
+ * Now let's do the update with the correct xattr values so it
+ * actually goes through
+ */
+ content = "v3";
+ old_version = "2";
+ version = "3";
+ rados_write_op_t update_op = rados_create_write_op();
+ if (!failed_write_op) {
+ printf("failed to allocate write op\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ rados_write_op_cmpxattr(update_op, "version", LIBRADOS_CMPXATTR_OP_EQ, old_version, strlen(old_version));
+ rados_write_op_write_full(update_op, content, strlen(content));
+ rados_write_op_setxattr(update_op, "version", version, strlen(version));
+ ret = rados_write_op_operate(update_op, io_ctx, object_name, NULL, 0);
+ if (ret < 0) {
+ printf("failed to do a compound write update! error %d\n", ret);
+ ret = EXIT_FAILURE;
+ rados_release_write_op(update_op);
+ goto out;
+ } else {
+ printf("we overwrote our object %s following an xattr test with contents\n%s\n", object_name, content);
+ }
+ rados_release_write_op(update_op);
+ }
+
+ ret = EXIT_SUCCESS;
+
+ out:
+ if (io_ctx) {
+ rados_ioctx_destroy(io_ctx);
+ }
+
+ if (pool_created) {
+ /*
+ * And now we're done, so let's remove our pool and then
+ * shut down the connection gracefully.
+ */
+ int delete_ret = rados_pool_delete(rados, pool_name);
+ if (delete_ret < 0) {
+ // be careful not to
+ printf("We failed to delete our test pool!\n");
+ ret = EXIT_FAILURE;
+ }
+ }
+
+ rados_shutdown(rados);
+
+ return ret;
+}